/*
    PsychError.h

    AUTHORS:

        Allen.Ingling@nyu.edu           awi
        mario.kleiner.de@gmail.com      mk

    HISTORY:

        08/18/02  awi    Wrote it.
        11/16/04  awi    Added PsychErorr_argumentValueOutOfRange

    DESCRIPTION:


    TO DO:

        ĄThe present system for reporting errors works pretty well, here was an early ambitious scheme to improve it:

            Errors should be pointers to structures which hold information about
            the error. Errors of each type should be generated by error-generator
            functions which dynamically allocate a variant structure for storing
            error-specific information.  For example:

            theError = GenError_invalidArg();   //
            theError = GenError_none();         //returns null

            or perhaps the constructors could accept more info:

            theError = GenError_missingArg(Input, 1);
            theError = GetError_none();

            An acceptable alternative would be to have one generator function and
            pass it an enumerated type identifying the error class, for example:

            theError = GenError(PsychError_missingArg, Input, 1);
            theError = Generror(PsychError_none);


            Currrently, errors are variables which hold an enumerated
            type classifying the error.  Additional information about the error
            is stashed in static variables within Psychtoolbox library files but
            1) It is not attached to the error and there is no organizing
            principle to suggest where it might be found. 2) It has no state,
            so subsequent errors will overwrite or invalidate the information
            held within the static state variables.  This is particularly a
            problem with PsychErrorExitMsg(), which must now be called immediatlty
            after the error or else the error information is lost.

            If error are pointers to structures then this adds two complications:
            1- We need to dynamically allocate memory to hold the error structure
            and clean it up on exit.  For prototyping purposes, we could
            statically allocate an array of variant structures and limit the
            maximum number of concurrent errors to the length of the array.   2-
            Testing for the error type can no longer be done directly with the
            equals operator unless it were  overloaded.  A comparison function
            which dereferenced the error and examined the type would have to be
            supplied. Something like:

            if(ErrorEqual(error1, error2)) ....

            or

            if(GetErrorType(error1) == GetErrorType(error2))....
*/


//begin include once
#ifndef PSYCH_IS_INCLUDED_PsychError
#define PSYCH_IS_INCLUDED_PsychError

//define the PsychErrorExitMsg Macro
#define PsychErrorExitMsg(pse1, pse2)   PsychErrorExitC(pse1, pse2, __LINE__, __func__, __FILE__)
#define PsychErrorExit(pse1)            PsychErrorExitC(pse1, NULL, __LINE__, __func__, __FILE__)

#include "Psych.h"

//types of error.
typedef enum
{
    PsychError_none = 0,

    // These four constants describe different ways the user can give an invalid argument.  They are treated specially by PsychErrorExitC
    // (called by macros PsychErrorExit and PsychErrorExitMsg) which gives an extra verbose report for these errors,  relying on
    // cached argument descriptors to make its report.
    PsychError_invalidArg_absent,
    PsychError_invalidArg_extra,
    PsychError_invalidArg_type,
    PsychError_invalidArg_size,

    // These three four contants are used for the cap an limit calls at the head of module sufunctions.  They don't cause the special behavior in
    // PsychErrorExit.
    PsychError_extraInputArg,
    PsychError_missingInputArg,
    PsychError_extraOutputArg,
    PsychError_missingOutputArg,

    PsychError_toomanyWin,
    PsychError_outofMemory,
    PsychError_scumberNotWindex,
    PsychError_windexNotScumber,
    PsychError_invalidIntegerArg,
    PsychError_invalidWindex,
    PsychError_invalidScumber,
    PsychError_invalidNumdex,
    PsychError_invalidColorArg,
    PsychError_invalidDepthArg,
    PsychError_invalidRectArg,
    PsychError_invalidNumberBuffersArg,     //invalid number of video buffers specified to SCREEN OpenWindow
    PsychError_nullWinRecPntr,
    PsychError_registerLimit,
    PsychError_registered,
    PsychError_longString,
    PsychError_longStringPassed,
    PsychError_unimplemented,               //unimplemented feature
    PsychError_internal,                    //catch-all category for INTERNAL PSYCHTOOLBOX ERRORS.
    PsychError_system,                      //errors reported by system calls
    PsychError_invalidArgRef,
    PsychError_OpenGL,
    PsychError_SDL,
    PsychError_SurfaceLockFailed,
    PsychError_SurfaceAlreadyLocked,
    PsychError_InvalidWindowRecord,
    PsychError_unsupportedVideoMode,
    PsychError_user,                        //catch-all category for USER ERRORS.
    PsychError_unrecognizedPreferenceName,
    PsychError_unsupportedOS9Preference,
    PsychError_inputMatrixIllegalDimensionSize,
    PsychError_stringOverrun,
    PsychErorr_argumentValueOutOfRange,
    PsychError_last
} PsychError;
//This should be "PsychErrorType", not "PsychError"


/*
    Enumerated types and structures used to describe arguments passed from the scripting environment.  The ScriptingGlue functions which
    retrieve arguments can push a description of the desired arguments and push a description of the found
    arguments and then return an error if there is a discrepancy.  PsychErrorExitMsg can then pop the descriptions
    if the error type is PsychError_invalidInArgType or PsychError_invalidArgs to generate an error message
    telling the user why the argument passed was bogus.
*/
typedef enum
{
    #define PsychArgType_MIN        PsychArgType_unspecified
    PsychArgType_none               = 0,
    PsychArgType_unspecified        = 1,
    PsychArgType_unclassified       = 2,
    PsychArgType_char               = 4,
    PsychArgType_uint8              = 8,
    PsychArgType_uint16             = 16,
    PsychArgType_uint32             = 32,
    PsychArgType_int8               = 64,
    PsychArgType_int16              = 128,
    PsychArgType_int32              = 256,
    PsychArgType_double             = 512,
    PsychArgType_boolean            = 1024,
    PsychArgType_structArray        = 2048,
    PsychArgType_cellArray          = 4096,
    PsychArgType_single             = 8192,
    PsychArgType_uint64             = 2^14,
    PsychArgType_int64              = 2^15,
    PsychArgType_default            = 2^16,
    #define PsychArgType_MAX        PsychArgType_default
    #define PsychArgType_NUMTYPES   17    //this does not include the PsychArgType_none
} PsychArgFormatType;  //change this to PsychArgType for brevity


typedef enum
{
    PsychArgIn,  //change to PsychArg_in for consistant style
    PsychArgOut
} PsychArgDirectionType;


typedef enum
{
    kPsychArgAbsent  = 0,
    kPsychArgPresent = 1,
    kPsychArgFixed   = 2            //Arguments which are always present, the first return argument in MATLAB.
} PsychArgPresenceType;


typedef enum
{
    kPsychArgOptional = 0,          //Argument may be either absent or present and of the specified type
    kPsychArgRequired = 1,          //Argument must be present and of the specified type
    kPsychArgAnything = 2           //Argument may be present with specified type, absent, or of a different type.
} PsychArgRequirementType;


typedef struct
{
    int                         position;
    PsychArgDirectionType       direction;
    PsychArgPresenceType        isThere;
    PsychArgRequirementType     isRequired;
    PsychArgFormatType          type;
    int                         numDims;    // used only to set the received descriptor, to catch > 3 dims.
    psych_int64                 mDimMin;    // 1 is lowest
    psych_int64                 mDimMax;    // kPsychUnboundedArraySize = -1 is infinity
    psych_int64                 nDimMin;    // 1 is lowest
    psych_int64                 nDimMax;    // kPsychUnboundedArraySize = -1 is infinity
    psych_int64                 pDimMin;    // 0 is lowest.  (this should be changed to make 1 the lowest, so it agrees with n)
    psych_int64                 pDimMax;    // kPsychUnboundedArraySize = -1 is infinity. Set p min and max to zero to forbid
} PsychArgDescriptorType;

#define                         kPsychUnboundedArraySize    (psych_int64) -1
#define                         kPsychUnusedArrayDimension  (psych_int64) -2


//function prototypes
PsychError InitPsychError(void);
void PsychErrorExitC(PsychError error,
                     const char *extraErrorString,
                     int lineNum,
                     const char *funcName,
                     const char *fileName);

PsychError PsychStoreArgDescriptor(PsychArgDescriptorType *specified,
                                   PsychArgDescriptorType *provided);

PsychError PsychGetArgDescriptor(PsychArgDescriptorType **specified,
                                 PsychArgDescriptorType **provided);

int PsychDecomposeArgFormat(PsychArgFormatType argType,  const char **names);

void PsychDumpArgDescriptors(void);

//This is dumb, but we define the PsychFunctionPtr type inside of PsychError.
typedef PsychError (*PsychFunctionPtr)(void);

//end include once
#endif
