/*
This file describes the interface between LS-OPT and User Defined
Metamodels.

Livermore Software Technology Corporation (LSTC) holds the copyright
for this code. LSTC hereby grants anyone the right to create and
distribute derivative work based on this code under their own terms.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef UserMetaModel_INC
#include <stdlib.h>
#include <Python.h>

#define UserMetaModel_INC

#define UMM_NOERROR  0
#define UMM_ERROR    1
#define UMM_WARNING  2
#define UMM_MUSTFREE 128
/**
 * Structure for user MetaModels
 * IMPORTANT: This structure is part of the ABI (Application binary interface)
 *            between LS-OPT and the user MetaModels. What we want to achieve
 *            is forward and backward compatibility between different versions
 *            of LS-OPT and user MetaModel code. This is done through the
 *            size_* members. When this struct is allocated by LS-OPT,
 *            before the call to UserMetaModel_init, the size_lsopt member is
 *            set by LS-OPT. Using this member, the user space MetaModel code
 *            can check whether future extensoins to this struct has been
 *            implemented by the particular version of LS-OPT that is calling
 *            the MetaModel.  In the same manner LS-OPT can determine if 
 *            the user MetaModel accepts extensions (through the size_user
 *            member.
 *
 *            In essence, the user MetaModel code MUST test using the
 *            macro UMM_UNDERSTOOD_BY_LSOPT before reading or writing
 *            to any member of the struct. In the same way, LS-OPT
 *            MUST test using the macro UMM_UNDERSTOOD_BY_USER before
 *            expecting return values in the struct. The exception to
 *            this rule is any members declared up to and including
 *            error_message. These members where in the initial
 *            version of the interface and are always valid.
 *
 *            Also, it is strictly forbidden to change the definition of
 *            this struct, except for adding new members at the end of it.
 */
typedef struct UserMetaModel_ {
    /** Set by LS-OPT to sizeof(func_UserAttr) */
    size_t size_lsopt;
    
    /** Must be set by user code to sizeof(func_UserAttr) */
    size_t size_user;
    
    /** Set by LS-OPT. Contains the paramters given in the com-file
        The count for this is in the member numparam_in */
    double *param_in;

    /** Set by LS-OPT to the number of parameters in the param_in array. */
    int numparam_in;

    /** Set by LS-OPT. Contains the string given in the com-file as
        "solver user metamodel command" */
    char *stringparam;

    /** Set by LS-OPT to the path where LS-OPT found the shared object
        containing the user metamodel. Can be used if the user code
        wants to find other related files. */
    char *ummpath;

    /** Set by the user metamodel to describe itself. LS-OPT expects
        this string pointer to be valid between the calls to
        UserMetaModel_Init and UserMetaModel_Cleanup, so it can be
        either a string allocated by the MetaModel (but then it must
        be freed by Cleanup), or just set to a constant.
    */
    char *description;
    
    /** 
     * May be used by the user metamodel to store state data between calls.
     * Beware, this is not saved between LS-OPT runs, so this state must not
     * store information required to define the MetaModel. It may be used to
     * optimize algorithms for example.
     */
    void *userstate;

    /** Written by LS-OPT _or_ the user code.
     * The constants that make up the metamodel. 
     * However, this is _always_ allocated by LS-OPT! 
     */
    double * constants;

    /** Written by LS-OPT.
     * Contains the number of constants. */
    int num_constants;

    /** 
     * Set by LS-OPT. Number of variables in case. Not neccesarily set
     * when UserMetaModel_Init is called!
     * @see numpoints_good
     */
    int numvar;

    /**
     * Used to signal errors from the MetaModel to LS-OPT.
     * Initially set to UMM_NOERROR by LS-OPT, but may be set to
     * UMM_ERROR or UMM_WARNING by the user MetaModel code if something
     * goes wrong. After each call to user MetaModel code, LS-OPT will check
     * this variable. UMM_ERROR signals a fatal error. UMM_WARNING signals
     * a non-fatal error, that will just be reported to the user.
     * The user code can supply an optional error message to LS-OPT in the
     * member error_message. If this error message has been allocated on the
     * heap, the user code can signal this to LSOPT by bitwise ORing the value
     * UUM_MUSTFREE to the UUM_ERROR or UUM_WARNING value.
     */
    int error;
    
    /** An optional error message. @see error */
    char *error_message;
    


} UserMetaModel;

/**
 * Tests if a particular member of a UserMetaModel struct is understood by
 * the version of LS-OPT that we are dealing with.
 *
 * @param metamodel A variable of type UserMetaModel*
 * @param member The name of the member (as an identifier, not string)
 */
#define UMM_UNDERSTOOD_BY_LSOPT(metamodel,member) \
    (((char*)((metamodel)->member) - (char*)(metamodel) \
    + sizeof((metamodel)->member)) <= (metamodel)->size_lsopt)

/**
 * Tests if a particular member of a UserMetaModel struct is understood by
 * the version of user MetaModel that we are dealing with.
 *
 * @param metamodel A variable of type UserMetaModel*
 * @param member The name of the member (as an identifier, not string)
 */
#define UMM_UNDERSTOOD_BY_USER(metamodel,member) \
    (((char*)((metamodel)->member) - (char*)(metamodel) \
    + sizeof((metamodel)->member)) <= (metamodel)->size_user)

#ifndef UMM_BUILT_FROM_LSOPT

/* Windows specific defines */
#ifdef _WIN32
# ifdef _WIN64
#  error 64-bit not supported!
# endif
# define EXPORTED_FUNCTION __declspec(dllexport)
#else
# define EXPORTED_FUNCTION
#endif

 /**
 * Initializes the user MetaModel. LS-OPT has allocated the UserMetaModel
 * struct and filled it in. This call should set the description member,
 * and do any checking of the input paramters that are required.
 *
 * In case of an error during initialization, the MetaModel sets the
 * error member of the struct. In this case Cleanup will NOT be called by
 * LS-OPT.
 */
EXPORTED_FUNCTION
void UserMetaModel_Init(UserMetaModel* mm);

/**
 * Called by LS-OPT to allow the user metamodel code to clean up after it
 * is no longer used.
 */
EXPORTED_FUNCTION
void UserMetaModel_CleanUp(UserMetaModel* mm);

/**
 * Called by LS-OPT before calling UserMetaModel_Build, to figure out
 * how many constants is needed to define ths metamodel. Before making this
 * call, LS-OPT will have filled in the numvar member of the UserMetaModel.
 *
 * @param mm The User MetaModel data structre
 * @param numpoints_good The number of points used to build the metamodel
 */
EXPORTED_FUNCTION
int UserMetaModel_GetConstantCount(UserMetaModel* mm, int numpoints_good);

/**
 * Called by LS-OPT to have the MetaModel build itself. A list of known
 * points is given, and the MetaModel is expected to update mm->constants.
 * mm->num_constants is set by LS-OPT beforehand.
 *
 * @param x_case_good Point coordinates for known points
 * @param funcvals_good Function values for corresponding points
 * @param numpoints_good Number of points in the vectors
 */
EXPORTED_FUNCTION
void UserMetaModel_Build(UserMetaModel* mm,
			 double ** x_case_good,
			 double * funcvals_good,int numpoints_good);

/**
 * Called by LS-OPT to request a certain point's function value to be 
 * calculated by the metamodel. The coordinates of the point is given in
 * the coords array. An error may be reported using mm->error.
 */
EXPORTED_FUNCTION
double UserMetaModel_FuncVal(UserMetaModel* mm, double *coords);

/**
 * Called by LS-OPT to request a certain point's function gradient to be 
 * calculated by the metamodel. The coordinates of the point is given in
 * the coords array. The variable to calculate gradient on is given as a zero
 * based index in the var parameter. An error may be reported using mm->error.
 */
EXPORTED_FUNCTION
double UserMetaModel_FuncGrad(UserMetaModel* mm, int var, double *coords);



#endif

#endif
double RunPython(UserMetaModel* mm, PyObject *pArgs, PyObject *pFunc);
void InitPython(UserMetaModel* mm);