//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org> 
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
#ifndef cwSfTracker_h
#define cwSfTracker_h

namespace cw
{
  namespace sftrack
  {
    typedef struct result_str
    {
      unsigned index;     // index of this record into the internal result[] array or kInvalidIdx if it is not in the array.
      unsigned oLocId;    // index into cwSfMatch_t.loc[]
      unsigned scEvtIdx;  // score event index 
      unsigned mni;       // index of the performed MIDI event associated with this score location
      double   sec;       // seconds of the performed MIDI event
      unsigned smpIdx;    // sample time index of performed MIDI event
      unsigned muid;      // MIDI file event msg unique id (See cwMidiFile trackMsg_t.uid)
      unsigned pitch;     // performed pitch 
      unsigned vel;       // performed velocity
      unsigned flags;     // See kSm???Fl
      double   cost;      // match cost
    } result_t;


    struct sftrack_str;
    typedef void (*callback_func_t)(  void* arg, result_t* rp );
    
    typedef handle<struct sftrack_str> handle_t;

    enum {
      // Set this flag to overwrite results in the internal result[] array when
      // the matcher backtracks.  If this flag is set then the result[] array  
      // will have one record for each unique MIDI event (count of calls to exec()).
      // If it is cleared then the result array will have a unique entry for
      // each alignment and re-alignment. In this case a given performed
      // event may have multiple entries in the result[] array if it is
      // re-aligned.  result[] records with identical 'muid' fields indicate
      // that the performed event represented by the 'muid' was re-aligned..
      kBacktrackResultsFl = 0x01,
      kPrintFl            = 0x02
    };
    
    rc_t create( handle_t&         hRef,
                 sfscore::handle_t scH,      // Score handle.  See cwSfScore.h.
                 unsigned          scWndN,   // Length of the scores active search area. ** See Notes.
                 unsigned          midiWndN, // Length of the MIDI active note buffer.    ** See Notes.
                 unsigned          flags,    // See k???Fl
                 callback_func_t   cbFunc,   // A function to be called to notify the recipient of changes in the score matcher status.
                 void*             cbArg );  // User argument to 'cbFunc'.

    // Notes:
    // The cwSfTrack maintains an internal cwSfMatch object which is used to attempt to find the
    // best match between the current MIDI active note buffer and the current score search area.
    //
    // 'scWndN' is used to set the cwSfMatch 'locN' argument. It defines the length of the
    // score window over which the MIDI event window will slide, while searching for the best match.
    //
    // 'midiWndN' sets the length of the MIDI FIFO winodw which is used to match to the score with
    // each recceived MIDI note.  'midiWndN' must be <= 'scWndN'.

    rc_t destroy( handle_t& hRef );

    // Set the starting position of the tracker and clear the internal 'result' array.
    // 'scLocIdx' is a score index as used by cmScoreLoc(scH) not into p->mp->loc[].
    rc_t reset( handle_t h, unsigned scLocIdx );

    // Slide a score window 'hopCnt' times, beginning at 'bli' (an
    // index into p->mp->loc[]) looking for the best match to p->midiBuf[].  
    // The score window contain scWndN (p->mp->mcn-1) score locations.
    // Returns the index into p->mp->loc[] of the start of the best
    // match score window. The score associated
    // with this match is stored in s_opt.
    //unsigned   _scan( handle_t h, unsigned bli, unsigned hopCnt );

    // Step forward/back by p->stepCnt from p->eli.
    // p->eli must therefore be valid prior to calling this function.
    // If more than p->maxMissCnt consecutive MIDI events are 
    // missed then automatically run _scan ().
    // Return kEofRC if the end of the score is encountered.
    // Return cmSubSysFailRC if an internal scan resync. failed.
    //rc_t _step( handle_t h );

    // This function calls _scan() and _step() internally.
    //
    // If 'status' is not kNonMidiMdId then the function returns without changing the
    // state of the object. (i.e. the matcher only recognizes MIDI note-on messages.)
    //
    // If the MIDI note passed by the call results in a successful match then
    // p->eli will be updated to the location in p->sfMatch.loc[] of the latest 
    // match, the MIDI note in p->midiBuf[] associated with this match
    // will be assigned a valid locIdx and scLocIdx values, and *scLocIdxPtr
    // will be set with the matched scLocIdx of the match.
    //
    // If this call does not result in a successful match *scLocIdxPtr is set
    // to kInvalidIdx.
    //
    // Calling exec() may result in alignment/re-alignment of both the incoming note
    // and/or previous notes.  (i.e. previous note that did not align may be found
    // to align or previous notes that were found to align may now not align).
    // The exact status of the alignment and re-alignments are held in the
    // internal 'result' array.
    //
    // Every alignment/realignment of an event results in a call to the callback function.
    // This means that multiple callbacks may occur as the result of a single
    // call to exec().
    
    // 'muid' is the unique id associated with this MIDI event under the circumstances
    // that the event came from a MIDI file.  See cwMidiFile.h trackMsg_t.uid.
    //
    // Return:
    //   cmOkRC  - Continue processing MIDI events.
    //   cmEofRC - The end of the score was encountered.
    //   cmInvalidArgRC - scan failed or the object was in an invalid state to attempt a match.
    //   cmSubSysFailRC - a scan resync failed in cmScMatcherStep().
    rc_t exec( handle_t h,
               double   sec,
               unsigned smpIdx,
               unsigned muid,
               unsigned status,
               midi::byte_t d0,
               midi::byte_t d1,
               unsigned* scLocIdxPtr );


    unsigned        result_count( handle_t h );
    const result_t* result_base( handle_t h );
    
    void print( handle_t h );

    rc_t test( const object_t* cfg, sfscore::handle_t scoreH );
    
  }
}


#endif
