#ifndef cmTimeLine_h
#define cmTimeLine_h

#ifdef __cplusplus
extern "C" {
#endif


  typedef cmHandle_t cmTlH_t;

  typedef cmRC_t cmTlRC_t;

  enum
  {
    kOkTlRC = cmOkRC,
    kLHeapFailTlRC,
    kParseFailTlRC,
    kJsonFailTlRC,
    kDuplNameTlRC,
    kRefNotFoundTlRC,
    kAudioFileFailTlRC,
    kMidiFileFailTlRC,
    kTypeCvtFailTlRC,
    kUnknownRecdTypeTlRC,
    kFinalizeFailTlRC,
    kInvalidSeqIdTlRC,
    kOnsetFailTlRC,
    kAssertFailTlRC
  };

  typedef enum
  {
    kMidiFileTlId  = 0x01,
    kMidiEvtTlId   = 0x02,
    kAudioFileTlId = 0x03,
    kAudioEvtTlId  = 0x04,
    kMarkerTlId    = 0x05
  } cmTlObjTypeId_t;

  enum
  {
    kReservedTlFl = 0x01,
    kNoWriteTlFl  = 0x02  // do not write this object in cmTimeLineWrite()
  };

  // marker types
  enum
  {
    kAudioMarkTlId,
    kAudioOnsetMarkTlId,
    kMidiOnsetMarkTlId
  }; 

  typedef void (*cmTlCb_t)( void* arg, const void* data, unsigned byteCnt );

  typedef struct cmTlObj_str
  {
    void*               reserved;    // pt's to _cmTlObj_t 
    unsigned            seqId;       // sequence this object is assigned to
    const cmChar_t*     name;        // text name of this object
    unsigned            uid;         // generated unique id for this object
    cmTlObjTypeId_t     typeId;      // type of the object
    struct cmTlObj_str* ref;         // time reference object
    int                 begSmpIdx;   // start time of this object as an offset from the start time of the reference object
    unsigned            durSmpCnt;   // duration of this object
    int                 seqSmpIdx;   // absolute start time of this object within the sequence
    const cmChar_t*     text;        // points to text assoc'd with this node (file name for audio/midi file, marker text)
    unsigned            flags;       // see kXXXTlFl 
    void*               userDataPtr; // user customizable data pointer 
  } cmTlObj_t;

  typedef struct
  {
    cmTlObj_t     obj;
    cmMidiFileH_t h;
    unsigned      noteOnCnt;
    cmChar_t*     fn;
  } cmTlMidiFile_t;

  // MIDI events generated from MIDI files reference the previous
  // MIDI event and the first event in the file references the 
  // file object. The 'begSmpIdx' is therefore a delta value
  // from the previous event.
  typedef struct
  {
    cmTlObj_t               obj; //
    unsigned                midiFileObjId;
    const cmMidiTrackMsg_t* msg;       // w/ dticks converted to samples
  } cmTlMidiEvt_t;


  typedef struct
  {
    cmTlObj_t         obj;
    cmAudioFileH_t    h;
    cmAudioFileInfo_t info;
    cmChar_t*         fn;
  } cmTlAudioFile_t;

  typedef struct
  {
    cmTlObj_t       obj;
    cmAudioFileH_t  h;
    unsigned        smpIdx;
    unsigned        smpCnt;
    cmChar_t*       text;
  } cmTlAudioEvt_t;

  typedef struct
  {
    cmTlObj_t        obj;
    unsigned         typeId;         // marker type see kXXXMarkTlId above.
    const cmChar_t*  text;
    unsigned         bar;            // measure (bar) in which this marker starts
    const cmChar_t*  sectionStr;     // the section in which this marker starts
  } cmTlMarker_t;

  extern cmTlH_t cmTimeLineNullHandle;

  // 
  cmTlRC_t        cmTimeLineInitialize( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg, const cmChar_t* prefixPath );
  cmTlRC_t        cmTimeLineInitializeFromFile( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg, const cmChar_t* fn, const cmChar_t* prefixPath );

  const cmChar_t* cmTimeLineFileName( cmTlH_t h );
  const cmChar_t* cmTimeLinePrefixPath( cmTlH_t h );

  cmTlRC_t        cmTimeLineFinalize( cmTlH_t* hp );

  bool            cmTimeLineIsValid( cmTlH_t h );
  double          cmTimeLineSampleRate( cmTlH_t h );

  // Convert global (sequence) time to a time relative to an object.
  int             cmTimeLineSeqToLocalSampleIndex( int seqSmpIdx, cmTlObj_t* localObjPtr );

  // Given cmTlObj_t.uid return a pointer to the associated record.
  // seqId is optional (dflt:cmInvalidId)
  cmTlObj_t*      cmTimeLineIdToObj( cmTlH_t h, unsigned seqId, unsigned uid );

  // Return the object following 'p' assigned to 'seqId'.
  // If 'p' is NULL then return the first object assigned to seqId.
  // If 'seqId' is set to cmInvalidId then return the next object on any seq.
  // If no objects follow 'p' on the specified sequence then return NULL.
  cmTlObj_t*       cmTimeLineNextObj( cmTlH_t h, cmTlObj_t* p, unsigned seqId );

  // Same as cmTimeLineNextObj() but returns next object whose type matches 'typeId'.
  cmTlObj_t*       cmTimeLineNextTypeObj( cmTlH_t h, cmTlObj_t* p, unsigned seqId, unsigned typeId );

  cmTlMidiFile_t*  cmTlNextMidiFileObjPtr(  cmTlH_t h, cmTlObj_t* op, unsigned seqId );
  cmTlAudioFile_t* cmTlNextAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId );
  cmTlMidiEvt_t*   cmTlNextMidiEvtObjPtr(   cmTlH_t h, cmTlObj_t* op, unsigned seqId );
  cmTlAudioEvt_t*  cmTlNextAudioEvtObjPtr(  cmTlH_t h, cmTlObj_t* op, unsigned seqId );
  cmTlMarker_t*    cmTlNextMarkerObjPtr(    cmTlH_t h, cmTlObj_t* op, unsigned seqId );

  cmTlObj_t*       cmTlIdToObjPtr( cmTlH_t h, unsigned uid );

  // Cast a genereic cmTlObj_t pointer to a specific type.
  cmTlMidiFile_t*  cmTimeLineMidiFileObjPtr(  cmTlH_t h, cmTlObj_t* op );
  cmTlAudioFile_t* cmTimeLineAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op );
  cmTlMidiEvt_t*   cmTimeLineMidiEvtObjPtr(   cmTlH_t h, cmTlObj_t* op );
  cmTlAudioEvt_t*  cmTimeLineAudioEvtObjPtr(  cmTlH_t h, cmTlObj_t* op );
  cmTlMarker_t*    cmTimeLineMarkerObjPtr(    cmTlH_t h, cmTlObj_t* op );

  // Same as cmTimeLineXXXObjPtr() but does not generate an error when
  // 'op' does not point to the correct type. These function quietly
  // return NULL if the requested type does not match or 'op' == NULL.
  cmTlMidiFile_t*  cmTlMidiFileObjPtr(  cmTlH_t h, cmTlObj_t* op );
  cmTlAudioFile_t* cmTlAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op );
  cmTlMidiEvt_t*   cmTlMidiEvtObjPtr(   cmTlH_t h, cmTlObj_t* op );
  cmTlAudioEvt_t*  cmTlAudioEvtObjPtr(  cmTlH_t h, cmTlObj_t* op );
  cmTlMarker_t*    cmTlMarkerObjPtr(    cmTlH_t h, cmTlObj_t* op );

  cmTlAudioFile_t* cmTimeLineFindAudioFile( cmTlH_t h, const cmChar_t* fn );
  cmTlMidiFile_t*  cmTimeLineFindMidiFile(  cmTlH_t h, const cmChar_t* fn );

  
  cmTlAudioFile_t* cmTimeLineAudioFileAtTime( cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );
  cmTlMidiFile_t*  cmTimeLineMidiFileAtTime(  cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );
  cmTlMidiEvt_t*   cmTimeLineMidiEvtAtTime(   cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );
  cmTlMarker_t*    cmTimeLineMarkerAtTime(    cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );


  cmTlAudioFile_t* cmTimeLineAudioFileAtTime( cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );
  cmTlMidiFile_t*  cmTimeLineMidiFileAtTime(  cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );
  cmTlMidiEvt_t*   cmTimeLineMidiEvtAtTime(   cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );
  cmTlMarker_t*    cmTimeLineMarkerAtTime(    cmTlH_t h, unsigned seqId, unsigned seqSmpIdx );

  cmTlMarker_t*    cmTimeLineMarkerFind( cmTlH_t h, const cmChar_t* markText );

  // 'typeId' = kAudioFileTlId, kMidiFileTId, kMarkerTlId.
  // 'nameStr' and 'refObjNameStr' may be NULL.
  cmTlRC_t cmTimeLineInsert( 
    cmTlH_t         h, 
    const cmChar_t* nameStr, 
    unsigned        typeId, 
    const cmChar_t* fn_or_markerStr, 
    int             begSmpIdx, 
    unsigned        durSmpCnt, 
    const cmChar_t* refObjNameStr, 
    unsigned        seqId ); 
  
  // See src/data/tl0.json for an example JSON file.
  cmTlRC_t cmTimeLineReadJson(  cmTlH_t* hp, const cmChar_t* ifn );

  // Return a count of sequences contained within this timeline.
  unsigned cmTimeLineSeqCount( cmTlH_t h );

  // Make notifications for all records belonging to the sequence.
  cmTlRC_t cmTimeLineSeqNotify( cmTlH_t h, unsigned seqId );

  // Create and make notification for audio/MIDI onset marks.
  cmTlRC_t cmTimeLineGenOnsetMarks( cmTlH_t h, unsigned seqId );
  cmTlRC_t cmTimeLineDeleteOnsetMarks( cmTlH_t h, unsigned seqId );

  cmTlRC_t cmTimeLineWrite( cmTlH_t h, const cmChar_t* fn );

  cmTlRC_t cmTimeLinePrint( cmTlH_t h, cmRpt_t* rpt );
  cmTlRC_t cmTimeLinePrintFn( cmCtx_t* ctx, const cmChar_t* tlFn, const cmChar_t* prefixPath, cmRpt_t* rpt );

  cmTlRC_t cmTimeLineTest( cmCtx_t* ctx, const cmChar_t* tlFn, const cmChar_t* prefixPath  );

  // The time-line notifies listeners of initialization and finalization
  // events via calling a cmTlCbFunc_t function.  The argument to this 
  // function is a serialized cmTlUiMsg_t.  The recipient of the callback
  // can extract information from this message using cmTimeLineDecode()
  // to form a cmTlUiMsg_t record. Note that all pointers internal to the
  // cmTlUiMsg_t point into the message buffer itself.
  
  // id's used to indicate the type of a serialized object
  typedef enum
  {
    kInvalidMsgTlId,
    kInitMsgTlId,      // A a set of time-line objects is about to be transmitted
    kFinalMsgTlId,     // A time-line object is being finalized.
    kDoneMsgTlId,      // All the objects assoc'd with a time line seq-notify have been sent.
    kInsertMsgTlId,    // A time-line object was inserted.
  } cmTlUiMsgTypeId_t;

  typedef struct
  {
    cmTlUiMsgTypeId_t       msgId;         // See cmTlUiMsgTypeId_t.
    unsigned                objId;         // Set to cmTlObj_t.uid
    unsigned                seqId;         // Sequence id
    double                  srate;         // Only valid with kInitMsgTlId.
    unsigned                seqCnt;        // Only valid with kInitMsgTlId.
  } cmTlUiMsg_t;

  // Decode a serialized cmTlObj_t as passed to the cmTlCb_t listener
  // callback function.
  cmTlRC_t cmTimeLineDecode( const void* msg, unsigned msgByteCnt, cmTlUiMsg_t* uiMsg );

#ifdef __cplusplus
}
#endif

#endif
