#ifndef cmSdb_h
#define cmSdb_h

#ifdef __cplusplus
extern "C" {
#endif

  /*
    The CSV file used to initialize a SDB object has the  following column syntax.
    
    Column Name     Type  Description
    ------ -------- ----- -----------------------------------------------------
       1   uuid     uint  Unique integer identifier for this event
       2   baseUuid uint  uuid of channel 0 for this event
       3   chIdx    uint  Channel index (stereo: 1=left 2=right)
       4   obi      uint  Outer onset sample index into 'afn'.
       5   ibi      uint  Inner onset sample index into 'afn'.
       6   iei      uint  Inner offset sample index into 'afn'.
       7   oei      uint  Outer offset sample index into 'afn'.
       8   src      text  Source label for this event (e.g. mcgill, ui )       
       9   midi     uint  MIDI pitch number or -1 if the sample is not pitched
      10   instr    text  Instrument label.
      11   srate    uint  Sample rate of audio file reference by 'afn'.
      10   chCnt    uint  Count of channels for this event.
      *    notes    text  0 or more free form double quoted text notes.
      *    afn      text  File name of the audio file this event occurs in.

     Notes:
     #. Each event represents a mono audio signal.  If the event is drawn
        from a multi-channel audio file then the 'chCnt' field will be
        greater than one.  If 'chCnt' is greater than one then the associated
        samples can be found by collecting all events that share the
        same 'baseUuid'.

     #. There may be zero or more columns of 'notes'. If there are no
        notes then the 'afn' field is in column 11.

     #. The index values (chIdx,obi,ibi,iei,oei) as stored in the CSV file
         are 1 based. These values are decreased by 1 by the cmSdb CSV reader
        so that their cmSdb value is zero based.  See cmSdbLoad().
 
   */

  enum
  {
    kOkSdbRC,
    kLHeapFailSdbRC,
    kCsvFailSdbRC,
    kFileSysFailSdbRC,
    kAudioFileFailSdbRC,
    kSyntaxErrSdbRC,
    kInvalidRspIdxSdbRC,
    kInvalidSeqIdxSdbRC,
    kChPairNotFoundSdbRC,
    kRspEvtNotFoundSdbRC,
    kAssertFailSdbRC,
    kInvalidArgSdbRC
  };

  typedef cmHandle_t cmSdbH_t;
  typedef cmHandle_t cmSdbResponseH_t;
  typedef cmHandle_t cmSdbSeqH_t;

  typedef cmRC_t     cmSdbRC_t;

  extern cmSdbH_t         cmSdbNullHandle;
  extern cmSdbResponseH_t cmSdbResponseNullHandle;
  extern cmSdbSeqH_t      cmSdbSeqNullHandle;
  
  typedef struct
  {
    unsigned         uuid;     // unique id of this sample
    unsigned         baseUuid; // uuid of channel 0
    unsigned         chIdx;   // channel index (0=left,1=right)
    unsigned         obi;     // outer onset
    unsigned         ibi;     // inner onset
    unsigned         iei;     // inner offset
    unsigned         oei;     // outer offset
    unsigned         midi;    // MIDI pitch or -1 for unpitched instruments
    unsigned         srate;   // sample rate
    unsigned         chCnt;   // source channel count
    const cmChar_t*  src;     // sample source (e.g. mcgill, ui )
    const cmChar_t*  instr;   // instrument label
    const cmChar_t*  afn;     // audio file name 
    const cmChar_t** notesV;  // NULL terminated list of terms describing the event.
  } cmSdbEvent_t;


  // Create an SDB object.  If 'csvFn' is non-NULL then an internal call is made to cmSdbLoad().
  cmSdbRC_t cmSdbCreate( cmCtx_t* ctx,  cmSdbH_t* hp, const cmChar_t* csvFn, const cmChar_t* audioDir );

  // Release an SDB object previously created via cmSdbCreate().
  cmSdbRC_t cmSdbDestroy( cmSdbH_t* hp );

  bool      cmSdbIsValid( cmSdbH_t h );

  // Iinitialze the internal database from the CSV file 'csvFn'.
  cmSdbRC_t cmSdbLoad( cmSdbH_t h, const cmChar_t* csvFn, const cmChar_t* audioDir );

  // Time align all channel pairs by setting the onset times to 
  // the minimum time among all the pairs and the offset times to
  // the maximum among all the pairs.  This function is applied to all
  // the events contained in the sample database.
  cmSdbRC_t cmSdbSyncChPairs( cmSdbH_t h );

  // Given a sample event unique id return a pointer to the associated record.
  const cmSdbEvent_t* cmSdbEvent( cmSdbH_t h, unsigned uuid );

  //================================================================================================
  // Query Related functions
  //

  // Select a set of events from the sample database.
  //
  // The possible selection criteria are:
  //   sample rate
  //   instrument label
  //   source label
  //   notes labels
  //   event duration
  //
  // In order to match an event all active criteria
  // must match.  In other words the match implies a
  // logical AND operation on each match criteria.
  // Each of the criteria can be made inactive by 
  // specifying particular key values.
  //   sample rate      = 0
  //   instrument label = NULL
  //   source label     = NULL
  //   notes labels     = NULL
  //   event duration   = minDurSec=0 maxDurSec=0
  //
  // For the text array arguments (instrV,srcV,notesV) 
  // each element of the array is a key which is attempts to
  // match the associated field in each event record.
  // By default a match is triggered if the key text is identical to the 
  // event field text.  The match algorithm can be modified by
  // specifying a '*' as the first character in the key field.
  // In this case a the key need only be a substring of the
  // event field to trigger a match.  For example "*viol" 
  // will return events that match both "violin" and "viola".
  // 
  // To specify a mismatch as a successful match 
  // (i.e. to return events which do not match the key text) 
  // prefix the key with a '!' character. 
  // 
  // Note that it is legal to specify both '!' and '*'. In
  // which case a match will be triggered by fields where
  // the key text is not a substring of the field text.
  //
  // pitchV[] contains an array of pitch values to match.
  // The last value in pitchV[] must be kInvalidMidiPitch.
  // If pitchV == NULL then all pitches match.  Note that
  // to match non-pitched instruments set set one element
  // of pitchV[] to -1.
  //
  // The application should release all query response handles 
  // returned from this function via a call to cmSdbResponseFree().  
  // cmSdbDestroy() will automatically release any response
  // handles not previously release by cmSdbReponseFree().
  cmSdbRC_t cmSdbSelect( 
    cmSdbH_t         h,
    double           srate,      // event sample rate or 0 to ignore
    const cmChar_t** instrV,     // array of instrument labels to match
    const cmChar_t** srcV,       // array of 'src' labels to match
    const cmChar_t** notesV,     // array of text 'notes' to match
    const unsigned*  pitchV,     // array of pitches terminated w/ kInvalidMidiPitch
    double           minDurSec,  // min event duration
    double           maxDurSec,  // max event duration or 0 to ignore
    unsigned         minChCnt,   // min ch cnt or 0 to ignore
    cmSdbResponseH_t* rhp );

  // Given the event 'ep' locate the channel pairs associated with that event.
  // The response handle returned from this function must be released
  // by a call to cmSdbResponseFree().
  cmSdbRC_t cmSdbSelectChPairs( cmSdbH_t h, const cmSdbEvent_t* ep, cmSdbResponseH_t* rhp );
  
  // Return the count of events in a query response.
  unsigned            cmSdbResponseCount(   cmSdbResponseH_t  rh );

  // Return the event at 'index' in from a query response.
  // Legal 'index' range: 0<=index<=cmSdbResponseCount().
  const cmSdbEvent_t* cmSdbResponseEvent(   cmSdbResponseH_t  rh, unsigned index );

  // Return true if the 'rh' is a non-NULL query response handle.
  bool                cmSdbResponseIsValid( cmSdbResponseH_t  rh );

  // Release the resource held by a query response.  
  cmSdbRC_t           cmSdbResponseFree(    cmSdbResponseH_t* rhp );
  void                cmSdbResponsePrint(   cmSdbResponseH_t  rh, cmRpt_t* rpt );
    
  //================================================================================================
  // Sequence Related functions
  //
  typedef struct
  {
    unsigned uuid;     // uuid of sample data base envent.
    double   begSec;   // Event start time in seconds.
    double   durSec;   // Event duration in seconds.
    double   gain;     // Event amplitude scaling factor.
    unsigned outChIdx; // Output channel index.
  } cmSdbSeqEvent_t;

  // Generate a random sequence of events with a programmable
  // density of events per second.
  //
  // 'minEvtPerSec' and 'maxEvtPerSec' specify the min and max count of events
  // which may be initiated per second.
  //
  // The application should release all sequence handles 
  // returned from this function via a call to cmSdbSeqFree().  
  // cmSdbDestroy() will automatically release any sequence
  // handles not previously release by cmSdbSeqFree().
  //
  // Note that the event selection is done with replacement.
  // The same event may therefore be selected more than
  // once.
  cmSdbRC_t cmSdbSeqRand( 
    cmSdbResponseH_t rh, 
    unsigned         seqDurSecs, 
    unsigned         seqChCnt, 
    unsigned         minEvtPerSec, 
    unsigned         maxEvtPerSec, 
    cmSdbSeqH_t*     shp );

  // Generate a sequence of serial events w/ gapSec seconds
  // between each event.  Events longer than 'maxEvtDurSec'
  // seconds are truncated to 'maxEvtDurSec'.
  cmSdbRC_t cmSdbSeqSerial(
    cmSdbResponseH_t rh,
    unsigned         seqChCnt,
    double           gapSec,    
    double           maxEvtDurSec,
    cmSdbSeqH_t*     shp );

  // Generate a chord sequence by randomly selecting one event
  // from each response handle. 
  cmSdbRC_t cmSdbSeqChord(
    cmSdbResponseH_t* rhp,           // one rhp[rn] query resonse per chord note
    unsigned          rn,            // count of response handles in rhp[].
    unsigned          seqChCnt,      // output sequence channel count
    unsigned          maxEvtDurSec,  // maximum event duration or 0 to prevent truncation
    cmSdbSeqH_t*      shp );

  // Release a sequence.
  cmSdbRC_t              cmSdbSeqFree( cmSdbSeqH_t* shp );

  // Return the count of sequence events.
  unsigned               cmSdbSeqCount( cmSdbSeqH_t sh );

  // Return a pointer to a specified cmSdbSeqEvent_t record.
  // where 0 <= index < cmSdbSeqCount(sh)
  const cmSdbSeqEvent_t* cmSdbSeqEvent( cmSdbSeqH_t sh, unsigned index );

  // Given a seqence index return the associate cmSdbEvent_t.
  const cmSdbEvent_t* cmSdbSeqSdbEvent( cmSdbSeqH_t sh, unsigned index );

  // Return the total duration of the sequence in seconds.
  double              cmSdbSeqDurSeconds( cmSdbSeqH_t sh );

  // Return the sample rate of the first event in the sequence that
  // has a non-zero sample rate.  There is no guarantee that all
  // of the other events in the sequence have the same sample rate
  // unless this was enforced by the query response that the 
  // sequence was generated from.
  double              cmSdbSeqSampleRate( cmSdbSeqH_t sh );

  // Generate an audio from a sequence and return it in
  // a signal vector.
  cmSdbRC_t cmSdbSeqToAudio( 
    cmSdbSeqH_t  sh, 
    unsigned     decayMs,        // decay rate for truncated events
    double       noiseDb,        // (-70.0) pad signal with white noise to avoid digital silence
    double       evtNormFact,    // normalize each sample event by normFact / abs(max(x[])) or 0 to skip normalization
    cmSample_t** signalRef,      // *signalRef[*sigSmpCntRef * sh.chCnt] returned audio signal
    unsigned*    sigSmpCntRef ); // count of frames in *signalRef

  // Generate an audio file from a sequence vector.
  cmSdbRC_t cmSdbSeqToAudioFn( 
    cmSdbSeqH_t  sh, 
    unsigned     decayMs,     // decay rate for truncated events
    double       noiseDb,     // (-70.0) pad signal with white noise to avoid digital silence
    double       evtNormFact, // normalize each sample event by normFact / abs(max(x[])) or 0 to skip normalization
    double       normFact,    // total signal norm factor or 0.0 to skip normalization
    const cmChar_t* fn,       // write the output signal to this audio file
    unsigned bitsPerSample    // audio file bits per sample
    ); 


  // Print a sequence event listing.
  void                   cmSdbSeqPrint( cmSdbSeqH_t sh, cmRpt_t* rpt );

  cmSdbRC_t cmSdbTest( cmCtx_t* ctx );

#ifdef __cplusplus
}
#endif

#endif
