/*
 * dmlib
 * -- Demo engine / editor common code and definitions
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2012-2013 Tecnic Software productions (TNSP)
 */
#ifndef DMENGINE_H
#define DMENGINE_H

#include "dmlib.h"
#include <SDL_events.h>
#include <SDL_audio.h>
#include "dmres.h"
#include "dmvecmat.h"

#ifdef DM_USE_JSS
#include "jss.h"
#include "jssmod.h"
#include "jssmix.h"
#include "jssplr.h"
#endif


#ifdef __cplusplus
extern "C" {
#endif

// Video setup screen / window size
#define DM_VSETUP_WIDTH       640
#define DM_VSETUP_HEIGHT      480


// Video setup type
enum
{
    DM_VSETUP_NONE    = 0x00,  // No video setup, just set window/screen to what demo specifies
    DM_VSETUP_ASPECT  = 0x01,  // Only modes that match the optVidAspect aspect ratio are ok
    DM_VSETUP_ANY     = 0x02,  // Any available mode is okay, the code can adapt
};

// Audio setup type
enum
{
    DM_ASETUP_NONE    = 0,
    DM_ASETUP_JSS     = 1,
    DM_ASETUP_TREMOR  = 2,
};



#define DT_MAGIC_ID           "SDMETLNE"
#define DT_MAX_EFFECT_PARAMS  16
#define DT_MAX_NAME_LENGTH    32
#define DT_FLOAT_STORE_SIZE   16


enum
{
    EFIT_SET,    // No interpolation, just "set" the value
    EFIT_LINEAR, // Linear interpolation
    EFIT_SMOOTH, // Smoothstep interpolation
};

enum
{
    EFPT_INT,
    EFPT_FLOAT,
    EFPT_STRING,
    EFPT_VECTOR,
    EFPT_MATRIX,
};


typedef struct
{
    int type;                // Interpolation type (EFIT_*) between this and next point
    int time;                // Offsets to event start, -1 == attach to event start, -2 = event end (if event)

    int vint;
    DMFloat vfloat;
    DMVector vector;
    DMMatrix matrix;
} DMTimelinePoint;


typedef struct
{
    int npoints, nallocated;
    DMTimelinePoint *points;
} DMTimelinePoints;


typedef struct
{
    char *name;              // Name / description of the parameter
    int type;                // Type (EFPT_*)

    DMTimelinePoints pts;
    char *vstr;

    // Current values (interpolated)
    int vint;
    DMFloat vfloat;
    DMVector vvector;
    DMMatrix vmatrix;
} DMTimelineEventParam;


typedef struct
{
    char name[DT_MAX_NAME_LENGTH];
    Uint32 type;
} DMFTimelineEventParam;


struct DMEngineData;


typedef struct
{
    char *  name;
    int     type;
    
    int     (*init)(struct DMEngineData *, void **data);
    void    (*shutdown)(struct DMEngineData *, void *data);
    int     (*render)(SDL_Surface *screen, void *data, const DMTimelineEventParam *params, const int time);

    int     nparams;
    DMTimelineEventParam params[DT_MAX_EFFECT_PARAMS];
} DMEffect;


#ifdef DM_USE_TIMELINE


typedef struct
{
    Uint32 start, duration;
    char effectName[DT_MAX_NAME_LENGTH];
    Uint32 nparams;
} DMFTimelineEvent;


typedef struct
{
    int start, duration;
    DMEffect *effect;
    int nparams;
    DMTimelineEventParam *params;
} DMTimelineEvent;


typedef struct
{
    BOOL enabled;
    DMTimelinePoints points;
} DMTimelineCurve;


typedef struct
{
    Uint32 index, layer;
    char name[DT_MAX_NAME_LENGTH];
    Uint8 enabled;
    Uint32 nevents;
} DMFTimelineTrack;


typedef struct
{
    int index;         // Track index
    int layer;         // Target render layer (0 = engine.screen in software rendering)
    char *name;        // Name of the timeline track
    BOOL enabled;      // Enabled?

    int nevents, nallocated;    // Number of events
    DMTimelineEvent **events;   // Events

    DMTimelineCurve composite;  // Composite curve (transparency of the "layer")
} DMTimelineTrack;


typedef struct
{
    char   magic[8];
    char   name[DT_MAX_NAME_LENGTH];
    Uint32 ntracks;
    Uint32 duration;
} DMFTimeline;


typedef struct
{
    char *name;
    int ntracks, nallocated, duration;
    DMTimelineTrack **tracks;
} DMTimeline;


typedef struct
{
    DMTimelineTrack *track;
    DMTimelineEvent *event;
} DMPreparedEvent;


typedef struct _DMPreparedEventGroup
{
    int start, end, nevents, nallocated;
    DMPreparedEvent *events;

    struct _DMPreparedEventGroup *next, *prev;
} DMPreparedEventGroup;


typedef struct
{
    int duration, ngroups, pos;
    DMPreparedEventGroup **groups;
} DMPreparedTimeline;

#endif


typedef struct DMEngineData
{
    DMResourceLib *resources;
    void **effectData;

#ifdef DM_USE_TIMELINE
    DMResource *timeline;
    DMTimeline *tl;
    DMPreparedTimeline *ptl;
#endif

    int frameTime, frameCount,
        startTime, endTime,
        pauseTime;

    BOOL pauseFlag, paused, exitFlag;

    SDL_Surface *screen;
    SDL_Event event;

    int optVidWidth, optVidHeight,
        optVidDepth, optVFlags,
        optVidAspect, optVidSetup;
    BOOL optVidNative;

    int optResFlags;
    char *optDataPath, *optPackFilename;

    SDL_AudioSpec optAfmt;
    int optAudioSetup;

    DMMutex *audioStreamMutex;
    Uint8 * audioStreamBuf;
    size_t audioStreamLen;

#ifdef DM_USE_JSS
    JSSMixer *jssDev;
    JSSPlayer *jssPlr;
    int jssFormat;
#endif

#ifdef DM_USE_TREMOR
    DMResource *audioRes;
    size_t audioPos;
#endif

    int    (*demoInit)(struct DMEngineData *);
    int    (*demoInitPreVideo)(struct DMEngineData *);
    int    (*demoInitPostVideo)(struct DMEngineData *);
    int    (*demoRender)(struct DMEngineData *);
    void   (*demoShutdown)(struct DMEngineData *);
    void   (*demoQuit)(struct DMEngineData *);

    // Setup specifics
    DMVector setupMenuPos, setupMenuDim, setupText1Pos;
    BOOL setupMenuCenter, setupTextCondensed;
    char setupTextFullscreen[64], setupTextWindowed[64], setupTextPrefix[64];
} DMEngineData;


extern DMEffect *engineEffects;
extern int nengineEffects, nengineEffectsAlloc;


// Effect registration
int       engineRegisterEffect(const DMEffect *ef);
int       engineInitializeEffects(DMEngineData *);
void      engineShutdownEffects(DMEngineData *);
DMEffect *engineFindEffect(const char *name, const int nparams);
DMEffect *engineFindEffectByName(const char *name);


#ifdef DM_USE_TIMELINE
// Basic timeline handling
int  dmLoadTimeline(DMResource *res, DMTimeline **tl);
void dmFreeTimeline(DMTimeline *tl);

void dmFreeTimelinePoints(DMTimelinePoints *points);
void dmFreeTimelineEventParam(DMTimelineEventParam *param);
void dmFreeTimelineEvent(DMTimelineEvent *event);
void dmFreeTimelineTrack(DMTimelineTrack *track);


// Execution related
int  dmPrepareTimeline(DMTimeline *tl, DMPreparedTimeline *ptl);
void dmFreePreparedTimelineData(DMPreparedTimeline *ptl);

int  dmSeekTimeline(DMPreparedTimeline *tl, int time);
int  dmExecuteTimeline(DMPreparedTimeline *tl, SDL_Surface *screen, int time);


// Editing/saving related functions
int  dmCopyTimelinePoints(const DMTimelinePoints *src, DMTimelinePoints *dst);
int  dmCopyTimelineEventParam(const DMTimelineEventParam *src, DMTimelineEventParam *dst);
int  dmCopyTimelineEvent(const DMTimelineEvent *src, DMTimelineEvent **pdst);
int  dmCopyTimelineCurve(const DMTimelineCurve *src, DMTimelineCurve *dst);
int  dmCopyTimelineTrack(const DMTimelineTrack *src, DMTimelineTrack **pdst);
int  dmCopyTimeline(const DMTimeline *src, DMTimeline **pdst);

int  dmSaveTimeline(DMResource *res, DMTimeline *tl);

int  dmTimelineNew(DMTimeline **tl, const char *name);
int  dmTimelineAddTrack(DMTimeline *tl, DMTimelineTrack **track, const char *name);
int  dmTimelineTrackAddEvent(DMTimelineTrack *track, int start, int duration, DMTimelineEvent **pev);
int  dmTimelineEventSetEffect(DMTimelineEvent *event, DMEffect *ef);
int  dmTimelineEventSetEffectByIndex(DMTimelineEvent *event, const int index);
int  dmTimelineEventSetEffectByName(DMTimelineEvent *event, const char *name);

#endif // DM_USE_TIMELINE


// Resource helpers
#define engineGetResImage(eng, x, name) \
    do { \
        if ((x = (SDL_Surface *) engineGetResource(eng, name)) == NULL) \
            return DMERR_INIT_FAIL; \
    } while (0)

#define engineGetResModule(eng, x, name) \
    do { \
        if ((x = (JSSModule *) engineGetResource(eng, name)) == NULL) \
            return DMERR_INIT_FAIL; \
    } while (0)

#define engineFindResource(eng, name) dmResourceFind((eng)->resources, name)


int    engineClassifier(DMResource *res);
void * engineGetResource(DMEngineData *eng, const char *name);
int    engineGetTime(DMEngineData *eng, int t);
int    engineGetTimeDTi(DMEngineData *eng);
int    engineGetTick(DMEngineData *eng);
float  engineGetTimeDT(DMEngineData *eng);

int    engineGetVideoAspect(int width, int height);


// Implemented in actual demo code
int    demoPreInit(DMEngineData *eng);


#ifdef __cplusplus
}
#endif

#endif // DMENGINE_H
