#ifndef cwFlowValue_h
#define cwFLowValue_h

namespace cw
{
  namespace flow
  {
    typedef dsp::coeff_t     coeff_t;
    typedef dsp::sample_t    sample_t;
    typedef dsp::fd_sample_t fd_sample_t;
    typedef dsp::srate_t     srate_t;
    typedef dsp::ftime_t     ftime_t;
    typedef unsigned         uint_t;
    typedef int              int_t;
    
    typedef unsigned vid_t;

    enum {
      kBaseSfxId = 0,
      kFbufVectN = 3,  // count of signal vectors in fbuf (mag,phs,hz)
      kAnyChIdx = kInvalidIdx,
      kLocalValueN = 2,
      kDefaultFramesPerCycle=64,
      kDefaultSampleRate=48000
    };
        
    typedef struct abuf_str
    {
      srate_t            srate;        // Signal sample rate
      unsigned           chN;          // Count of channels
      unsigned           frameN;       // Count of sample frames per channel
      unsigned           bufAllocSmpN; // Size of allocated buf[] in samples.
      sample_t*          buf;          // buf[ chN * frameN ] ch0: 0:frameN, ch1: frameN:2*frame, ...
    } abuf_t;


    typedef struct fbuf_str
    {
      unsigned          memByteN;  // Count of bytes in mem[].
      void*             mem;       // mem[ memByteN ] All dynamically allocated memory used by this fbuf.
      
      srate_t           srate;     // signal sample rate
      unsigned          flags;     // See kXXXFbufFl
      unsigned          chN;       // count of channels
      unsigned*         maxBinN_V; // maxBinN_V[chN] max value that binN_V[i] is allowed to take
      unsigned*         binN_V;    // binN_V[ chN ] count of sample frames per channel
      unsigned*         hopSmpN_V; // hopSmpN_V[ chN ] hop sample count 
      fd_sample_t**     magV;      // magV[ chN ][ binN ]
      fd_sample_t**     phsV;      // phsV[ chN ][ binN ]
      fd_sample_t**     hzV;       // hzV[ chN ][ binN ]
      bool*             readyFlV;  // readyFlV[chN] true if this channel is ready to be processed (used to sync. fbuf rate to abuf rate)
    } fbuf_t;

    typedef struct mbuf_str
    {
      const midi::ch_msg_t* msgA;
      unsigned              msgN;
    } mbuf_t;

    enum
    {
      kInvalidTFl  = 0x00000000,
      kBoolTFl     = 0x00000001,
      kUIntTFl     = 0x00000002,
      kIntTFl      = 0x00000004,
      kFloatTFl    = 0x00000008,
      kDoubleTFl   = 0x00000010,
      
      kBoolMtxTFl  = 0x00000020,
      kUIntMtxTFl  = 0x00000040,
      kIntMtxTFl   = 0x00000080,
      kFloatMtxTFl = 0x00000100,
      kDoubleMtxTFl= 0x00000200,
      
      kABufTFl     = 0x00000400,
      kFBufTFl     = 0x00000800,
      kMBufTFl     = 0x00001000,
      kRBufTFl     = 0x00002000,
      kStringTFl   = 0x00004000,
      kTimeTFl     = 0x00008000,
      kCfgTFl      = 0x00010000,
      kMidiTFl     = 0x00020000,

      kTypeMask    = 0x0003ffff,

      kRuntimeTFl  = 0x80000000,  // The type of the value associated with this variable will be set by the proc instances during instantiation

      kNumericTFl = kBoolTFl | kUIntTFl | kIntTFl | kFloatTFl | kDoubleTFl,
      kMtxTFl     = kBoolMtxTFl | kUIntMtxTFl | kIntMtxTFl | kFloatMtxTFl | kDoubleMtxTFl,
      kAllTFl     = kTypeMask
    };

    typedef struct mtx_str
    {
      union {
        struct mtx::mtx_str< unsigned >* u;
        struct mtx::mtx_str< int >*      i;
        struct mtx::mtx_str< float >*    f;
        struct mtx::mtx_str< double >*   d;
      } u;
    } mtx_t;

    struct recd_type_str;
    struct recd_str;
    typedef struct rbuf_str
    {
      const struct recd_type_str*  type;  // all msgs are formed from this type      
      const struct recd_str* recdA; // recdA[ recdN ] 
      unsigned               recdN; // 
    } rbuf_t;

    typedef struct value_str
    {
      unsigned tflag;
      
      union {
        bool            b;
        uint_t          u;
        int_t           i;
        float           f;
        double          d;
        
        mtx_t*          mtx;        
        abuf_t*         abuf;
        fbuf_t*         fbuf;
        mbuf_t*         mbuf;
        rbuf_t*         rbuf;
        
        char*           s;
        
        const object_t* cfg;
        midi::ch_msg_t* midi;
        void*           p;
        

      } u;
      
      struct value_str* link;
      
    } value_t;

    

    //------------------------------------------------------------------------------------------------------------------------
    //
    // Value Only
    //

    inline void set_null( value_t& v, unsigned tflag ) { v.tflag=tflag; v.u.p=nullptr; }
    inline bool is_numeric( const value_t* v ) { return cwIsFlag(v->tflag,kNumericTFl); }
    inline bool is_matrix(  const value_t* v ) { return cwIsFlag(v->tflag,kMtxTFl); }

    // if all of the src flags are set in the dst flags then the two types are convertable.
    inline bool can_convert( unsigned src_tflag, unsigned dst_tflag ) { return (src_tflag&dst_tflag)==src_tflag; }

    
    abuf_t*         abuf_create( srate_t srate, unsigned chN, unsigned frameN );
    void            abuf_destroy( abuf_t*& buf );
    
    // If 'dst' is null then a new abuf is allocated, filled with the contents of 'src'.
    // If 'dst' is non-null and there is enough space for the contents of 'src' then only a copy is executed.
    // If there is not enough space then dst is reallocated.
    abuf_t*         abuf_duplicate( abuf_t* dst, const abuf_t* src );
    void            abuf_zero(        abuf_t* buf );
    rc_t            abuf_set_channel( abuf_t* buf, unsigned chIdx, const sample_t* v, unsigned vN );
    const sample_t* abuf_get_channel( abuf_t* buf, unsigned chIdx );

    fbuf_t*        fbuf_create( srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
    fbuf_t*        fbuf_create( srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV=nullptr, const fd_sample_t** phsV=nullptr, const fd_sample_t** hzV=nullptr );
    void           fbuf_zero( fbuf_t* fbuf );
    void           fbuf_destroy( fbuf_t*& buf );

    // Memory allocation will only occur if dst is null, or the size of dst's internal buffer are too small.
    fbuf_t*        fbuf_duplicate( fbuf_t* dst, const fbuf_t* src );

    mbuf_t*        mbuf_create( const midi::ch_msg_t* msgA=nullptr, unsigned msgN=0 );
    void           mbuf_destroy( mbuf_t*& buf );
    mbuf_t*        mbuf_duplicate( const mbuf_t* src );

    rbuf_t*        rbuf_create( const struct recd_type_str* type=nullptr, const struct recd_str* recdA=nullptr, unsigned recdN=0 );
    void           rbuf_destroy( rbuf_t*& buf );
    rbuf_t*        rbuf_duplicate( const rbuf_t* src );
    void           rbuf_setup( rbuf_t* rbuf, struct recd_type_str* type, struct recd_str* recdA, unsigned recdN );

    
    inline bool    value_is_abuf( const value_t* v ) { return v->tflag & kABufTFl; }
    inline bool    value_is_fbuf( const value_t* v ) { return v->tflag & kFBufTFl; }

    unsigned       value_type_label_to_flag( const char* type_desc );
    const char*    value_type_flag_to_label( unsigned flag );

    void           value_release( value_t* v );
    void           value_duplicate( value_t& dst, const value_t& src );

    // For all numeric types this function set's the type of 'value_ref' to the type of cfg.
    // For all other types sets the type of 'value_ref' to kCfgTFl and stores 'cfg' to value_ref.u.cfg;
    rc_t           value_from_cfg( const object_t* cfg, value_t& value_ref );

    // Assigns src to dst. If dst has a value type then src is converted to this type.
    // If the conversion is not possible then the function fail.s
    rc_t           value_from_value( const value_t& src, value_t& dst );
    
    void           value_print( const value_t* value, bool info_fl=false);

    rc_t value_get( const value_t* val, bool& valRef );
    rc_t value_set(       value_t* val, bool v );
    
    rc_t value_get( const value_t* val, uint_t& valRef );
    rc_t value_set(       value_t* val, uint_t v );
    
    rc_t value_get( const value_t* val, int_t& valRef );
    rc_t value_set(       value_t* val, int_t v );
    
    rc_t value_get( const value_t* val, float& valRef );
    rc_t value_set(       value_t* val, float v );
    
    rc_t value_get( const value_t* val, double& valRef );
    rc_t value_set(       value_t* val, double v );
    
    rc_t value_get( const value_t* val, const char*& valRef );
    rc_t value_set(       value_t* val, const char* v );
    
    rc_t value_get(       value_t* val, abuf_t*& valRef );
    rc_t value_get(       value_t* val, const abuf_t*& valRef );
    rc_t value_set(       value_t* val, abuf_t* v );
    
    rc_t value_get(       value_t* val, fbuf_t*& valRef );
    rc_t value_get(       value_t* val, const fbuf_t*& valRef );
    rc_t value_set(       value_t* val, fbuf_t* v );
    
    rc_t value_get(       value_t* val, mbuf_t*& valRef );
    rc_t value_get(       value_t* val, const mbuf_t*& valRef );
    rc_t value_set(       value_t* val, mbuf_t* v );
    
    rc_t value_get(       value_t* val, rbuf_t*& valRef );
    rc_t value_get(       value_t* val, const rbuf_t*& valRef );
    rc_t value_set(       value_t* val, rbuf_t* v );
    
    rc_t value_get(       value_t* val, const object_t*& valRef );
    rc_t value_set(       value_t* val, const object_t* v );

    rc_t value_get( const value_t* val, midi::ch_msg_t*& valRef );
    rc_t value_get( const value_t* val, const midi::ch_msg_t*& valRef );
    rc_t value_set(       value_t* val, midi::ch_msg_t* v );



    //------------------------------------------------------------------------------------------------------------------------
    //
    // Record
    //
    
    typedef struct recd_field_str
    {
      bool        group_fl; // set if this field record is a group
      char*       label;    // field or group label
      value_t     value;    // default value for this field
      char*       doc;      // documentation field for this field
      union
      {
        unsigned               index; // index into recd_t.valA of the value associated with this field
        struct recd_field_str* group_fieldL; 
      } u;
        
      struct recd_field_str* link;
    } recd_field_t;

    typedef struct recd_type_str
    {
      recd_field_t*               fieldL;  // linked list of field spec's
      unsigned                    fieldN;  // length of fieldL list   (fieldN + base->fieldN) is total field count
      const struct recd_type_str* base;    // base recd type that this field inherits from
    } recd_type_t;

    typedef struct recd_fmt_str
    {
      unsigned        alloc_cnt; // count of records to pre-allocate
      const object_t* req_fieldL; // label of required fields
      recd_type_t*    recd_type; // record type for this variable
    } recd_fmt_t;

    typedef struct recd_array_str
    {
      recd_type_t*     type;       // recd_type_t of this record array
      value_t*         valA;       // valA[ allocRecdN * type->fieldN ]
      struct recd_str* recdA;      // recdA[ allocRecdN ]
      unsigned         allocRecdN; // 
    } recd_array_t;

    typedef struct recd_str
    {
      struct value_str*       valA;   // varA[ recd_type_t.fieldN ] array of field values
      const struct recd_str*  base;   // Pointer to the records inherited fields.
    } recd_t;


    // Create/destroy a recd_format_t object.
    rc_t recd_format_create( recd_fmt_t*& recd_fmt_ref, const object_t* cfg, unsigned dflt_alloc_cnt=32 );
    void recd_format_destroy( recd_fmt_t*& recd_fmt_ref );

    // Create a recd_type_t instance from a cfg. description.
    rc_t recd_type_create( recd_type_t*& recd_type_ref, const recd_type_t* base_type, const object_t* cfg );
    void recd_type_destroy( recd_type_t*& recd_type );

    // Count of fields combined local and base record types. 
    rc_t recd_type_max_field_count( const recd_type_t* recd_type );

    // Get the field index associated with a named field.
    // Use '.' notation to separate groups from fields.
    // Note if this is a 'local' field then the high bit in the returned index will be set.    
    unsigned recd_type_field_index( const recd_type_t* recd_type, const char* field_label);

    // Given a field index return the field label.
    const char* recd_type_field_index_to_label( const recd_type_t* recd_type, unsigned field_idx );
    
    // Set the record base pointer and the value of all fields with default values.
    rc_t recd_init( const recd_type_t* recd_type, const recd_t* base, recd_t* r );

    // Print the recd_type info. to the console.
    void recd_type_print( const recd_type_t* recd_type );


    // Read the value from a single record field
    template< typename T >
    rc_t recd_get( const recd_type_t* type, const recd_t* recd, unsigned field_idx, T& val_ref )
    {
      if( field_idx < type->fieldN )
        return value_get( recd->valA + field_idx, val_ref );

      return recd_get( type->base, recd->base, field_idx - type->fieldN, val_ref );
    }

    inline rc_t _recd_get(const recd_type_t* recd_type, recd_t* r ) { return kOkRC; }
    
    template< typename T1, typename... ARGS >
    rc_t _recd_get( const recd_type_t* recd_type, const recd_t* recd, unsigned field_idx, T1& val, ARGS&&... args )
    {
      rc_t rc = kOkRC;
      
      if((rc = recd_get(recd_type,recd,field_idx,val)) != kOkRC )
        return rc;

      return _recd_get(recd_type,recd,std::forward<ARGS>(args)...);      
    }

    // Read the value of multiple record fields.
    template< typename T1, typename... ARGS >
    rc_t recd_get( const recd_type_t* recd_type, const recd_t* recd, unsigned field_idx, T1& val, ARGS&&... args )
    {
      return _recd_get(recd_type,recd,field_idx,val,args...);
    }

    // Set the base record pointer for a record with an inherited base
    inline rc_t recd_set_base( const recd_type_t* type, recd_t* recd, const recd_t* base )
    {
      // if we are setting base then the type must have a base type
      assert( (type->base == nullptr && base==nullptr) || (type->base!=nullptr && base!=nullptr) );
      
      recd->base = base;
      return kOkRC;
    }

    template< typename T >
    rc_t recd_set( const recd_type_t* type, const recd_t* base, recd_t* recd, unsigned field_idx, const T& val )
    {
      if( field_idx >= type->fieldN )
        return cwLogError(kInvalidArgRC,"Only 'local' record value may be set.");
      
      // set the base of this record
      recd_set_base(type,recd,base);
      
      return value_set( recd->valA + field_idx, val );
    }

    inline rc_t _recd_set( const recd_type_t* recd_type, recd_t* recd ) { return kOkRC; }

    template< typename T1, typename... ARGS >
    rc_t _recd_set( const recd_type_t* recd_type, recd_t* recd, unsigned field_idx, const T1& val, ARGS&&... args )
    {
      rc_t rc = kOkRC;

      if( field_idx >= recd_type->fieldN )
        return cwLogError(kInvalidArgRC,"Fields in the inherited record may not be set.");
              
      if((rc = value_set( recd->valA + field_idx, val)) != kOkRC )
      {
        rc = cwLogError(rc,"Field set failed on '%s'.", cwStringNullGuard(recd_type_field_index_to_label( recd_type, field_idx )));
        goto errLabel;
      }
      
      return _recd_set(recd_type,recd,std::forward<ARGS>(args)...);

    errLabel:
      return rc;
    }

    // Set multiple fields of a record.
    template< typename T1, typename... ARGS >
    rc_t recd_set( const recd_type_t* recd_type, const recd_t* base, recd_t* recd, unsigned field_idx, const T1& val, ARGS&&... args )
    {
      rc_t rc = kOkRC;
      if((rc = recd_init( recd_type, base, recd )) != kOkRC )
        goto errLabel;
      

      if((rc = _recd_set(recd_type,recd,field_idx,val,args...)) != kOkRC )
        goto errLabel;

    errLabel:
      return rc;

    }

    // Print a record to the console.
    rc_t recd_print( const recd_type_t* recd_type, const recd_t* r );

    // Create/destroy a buffer of records.
    rc_t recd_array_create( recd_array_t*& recd_array_ref, recd_type_t* recd_type, const recd_type_t* base,  unsigned allocRecdN );
    rc_t recd_array_destroy( recd_array_t*& recd_array_ref );

    // Copy records into a recd_array.  This function fails if there are less than
    // 'src_recdN' records already allocated in 'dest_recd_array'.
    // The source and destination record types should be the same, but this
    // function does very little to verify that the actually are.
    rc_t recd_copy( const recd_type_t* src_recd_type, const recd_t* src_recdA, unsigned src_recdN, recd_array_t* dest_recd_array );
    
    rc_t value_test( const test::test_args_t& args );
 
    
  }
}


#endif