//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org> 
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwMath.h"
#include "cwText.h"
#include "cwObject.h"
#include "cwFileSys.h"
#include "cwVectOps.h"
#include "cwMtx.h"
#include "cwDspTypes.h" // real_t, sample_t
#include "cwTime.h"
#include "cwMidiDecls.h"
#include "cwFlowDecl.h"
#include "cwFlow.h"
#include "cwFlowValue.h"
#include "cwFlowTypes.h"


namespace cw
{
  namespace flow
  {

    idLabelPair_t _varDescAttrFlagsA[] =
    {
      { kSrcVarDescFl,       "src" },
      { kSrcOptVarDescFl,    "src_opt" },
      { kNoSrcVarDescFl,     "no_src" },
      { kInitVarDescFl,      "init"  },
      { kMultVarDescFl,      "mult"  },
      { kUdpOutVarDescFl,    "out"   },
      { kUiCreateVarDescFl, "no_ui" }, // even if the proc ui is enabled, don't show this var
      { kUiDisableVarDescFl,"ui_disable" },
      { kUiHideVarDescFl,   "ui_hide" },
      { kNotifyVarDescFl,   "notify" },
      { kInvalidVarDescFl, "<invalid>" }
    };

    
    template< typename T >
    rc_t _val_get_driver( const variable_t* var, T& valRef )
    {
      if( var == nullptr )
        return cwLogError(kInvalidArgRC,"Cannnot get the value of a non-existent variable.");
      
      if( var->value == nullptr )
        return cwLogError(kInvalidStateRC,"No value has been assigned to the variable: %s:%i.%s:%i ch:%i.",cwStringNullGuard(var->proc->label),var->proc->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id,var->chIdx);

      return value_get(var->value,valRef);
    
    }
    

    // Variable lookup: Exact match on vid and chIdx
    rc_t _var_find_on_vid_and_ch( proc_t* proc, unsigned vid, unsigned chIdx, variable_t*& varRef )
    {
      varRef = nullptr;
      
      for(variable_t* var = proc->varL; var!=nullptr; var=var->var_link)
      {
        // the variable vid and chIdx should form a unique pair
        if( var->vid==vid && var->chIdx == chIdx )
        {
          varRef = var;
          return kOkRC;
        }
      }
      return cwLogError(kInvalidIdRC,"The variable matching id:%i ch:%i on proc '%s:%i' could not be found.", vid, chIdx, proc->label, proc->label_sfx_id);
    }

    // Variable lookup: Exact match on label and chIdx
    variable_t* _var_find_on_label_and_ch( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx )
    {
      for(variable_t* var = proc->varL; var!=nullptr; var=var->var_link)
      {
        // the variable vid and chIdx should form a unique pair
        if( var->label_sfx_id==sfx_id && textCompare(var->label,var_label)==0 && var->chIdx == chIdx )
          return var;
      }
      
      return nullptr;
    }
    
    rc_t _var_find_on_label_and_ch( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, variable_t*& var_ref )
    {
      rc_t rc = kOkRC;
      if((var_ref = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx)) == nullptr )
        rc = cwLogError(kEleNotFoundRC,"The variable '%s:%i' cannot be found on the proc:%s.",cwStringNullGuard(var_label),sfx_id,cwStringNullGuard(proc->label));
      return rc;
    }
    
    rc_t _validate_var_assignment( variable_t* var, unsigned typeFl )
    {
      if( cwIsFlag(var->varDesc->flags, kSrcVarDescFl ) )
        return cwLogError(kInvalidStateRC, "The variable '%s:%i' on proc '%s:%i' cannot be set because it is a 'src' variable.", var->label, var->label_sfx_id, var->proc->label,var->proc->label_sfx_id);
      /*
      if( !cwIsFlag(var->varDesc->type, typeFl ) )
        return cwLogError(kTypeMismatchRC, "The variable '%s:%i' on proc '%s:%i' is not a  '%s'.", var->label, var->label_sfx_id, var->proc->label, var->proc->label_sfx_id, _typeFlagToLabel( typeFl ));
      */
      
      return kOkRC;
    }

    void _var_print_addr( const char* title, const variable_t* v )
    { cwLogPrint("%s:%s:%i.%s:%i ",title,v->proc->label,v->proc->label_sfx_id,v->label,v->label_sfx_id); }

    void _var_print( const variable_t* var )
    {
      const char* conn_label  = is_connected_to_source(var) ? "extern" : "      ";
    
      cwLogPrint("  %12s:%3i vid:%3i ch:%3i : %s  : ", var->label, var->label_sfx_id, var->vid, var->chIdx, conn_label );
    
      if( var->value == nullptr )
        value_print( &var->local_value[0] );
      else
        value_print( var->value );

      if( var->src_var != nullptr )
        cwLogPrint(" src:%s:%i.%s:%i",var->src_var->proc->label,var->src_var->proc->label_sfx_id,var->src_var->label,var->src_var->label_sfx_id);

      if( var->dst_head != nullptr )
      {
        for(variable_t* v = var->dst_head; v!=nullptr; v=v->dst_link)
          cwLogPrint(" dst:%s:%i.%s:%i",v->proc->label,v->proc->label_sfx_id,v->label,v->label_sfx_id);
      }

      cwLogPrint("\n");    
    }
    
    
    rc_t _var_broadcast_new_value( variable_t* var )
    {
      rc_t rc = kOkRC;
      
      // notify each connected var that the value has changed
      for(variable_t* con_var = var->dst_head; con_var!=nullptr; con_var=con_var->dst_link)
      {
        // the var->local_value[] slot used by the source variable may have changed - update the destination variable
        // so that it points to the correct value.
        con_var->value = var->value;
        
        cwLogMod("%s:%i %s:%i -> %s:%i %s:%i",
                 var->proc->label,var->proc->label_sfx_id,
                 var->label,var->label_sfx_id,
                 con_var->proc->label,con_var->proc->label_sfx_id,
                 con_var->label,con_var->label_sfx_id );

        // Call the value() function on the connected variable
        if((rc = var_schedule_notification(con_var)) != kOkRC )
          break;
               
      }
      return rc;
    }


    // Incr the var->modN value and put the var pointer in var->proc->modVarMapA[]
    // where it will be picked up by a later call to _mod-var_map_dispatch().
    // This function runs in a multi-thread context.
    rc_t _mod_var_map_update( variable_t* var )
    {
      rc_t rc = kOkRC;
      if( cwIsFlag(var->varDesc->flags,kNotifyVarDescFl ) && var->proc->modVarRecurseFl==false )
      {      
        // if the var is already modVarMapA[] then there is nothing to do
        // (use acquire to prevent rd/wr from moving before this op)
        if( var->modN.load(std::memory_order_acquire) > 0 )
          return kOkRC;
      
        // reserve a slot in proc->modVarMapA[]
        // (use acquire to prevent rd/wr from moving before this op)
        if( var->proc->modVarMapFullCnt.fetch_add(1,std::memory_order_acquire) >= var->proc->modVarMapN )
        {
          rc = cwLogError(kBufTooSmallRC,"The mod var map overflowed on '%s:%i-%s:%i'",cwStringNullGuard(var->proc->label),var->proc->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id);
          goto errLabel;
        }
      
        // Get the next empty slot in proc->modVarMapA[]
        // (use acquire to prevent rd/wr from moving before this op)
        unsigned idx = var->proc->modVarMapHeadIdx.fetch_add(1,std::memory_order_acquire) % var->proc->modVarMapN;      

        var->proc->modVarMapA[ idx ] = var;

        // mark the var as in the list
        var->modN.fetch_add(1,std::memory_order_release);
      }
    errLabel:
      return rc;
    }


    
    
    // 'argTypeFlag' is the type (tflag) of 'val'.
    template< typename T >    
    rc_t _var_set_template( variable_t* var, unsigned argTypeFlag, T val )
    {
      rc_t rc = kOkRC;
      
      // it is not legal to set the value of a variable that is connected to a 'source' variable.
      if( var->src_var != nullptr )
        return cwLogError(kInvalidStateRC, "The variable '%s:%i %s:%i' cannot be set because it is connected to a source variable.", var->proc->label,var->proc->label_sfx_id, var->label, var->label_sfx_id);      

      
      // var->type is the allowable type for this var's value.
      // It may be set to kInvalidTFl if the type has not yet been determined.
      unsigned value_type_flag    = var->type;

      // Pick the slot in local_value[] that we will use to try out this new value.
      unsigned next_local_value_idx = (var->local_value_idx + 1) % kLocalValueN;
      
      // store the pointer to the current value of this variable
      value_t* original_value     = var->value;
      unsigned original_value_idx = var->local_value_idx;
      
      // release the previous value in the next slot
      value_release(&var->local_value[next_local_value_idx]);

      // if the value type of this variable has not been established
      if( value_type_flag == kInvalidTFl  )
      {
        // if the var desc is a single type then use that ....
        if( math::isPowerOfTwo(var->varDesc->type) )
        {
          value_type_flag = var->varDesc->type;
        }
        else // ... Otherwise select a type from the one of the possible flags given by the var desc
        {
          value_type_flag = var->varDesc->type & argTypeFlag;
        }

        // if the incoming type is not in the set of allowable types then it is an error
        if( value_type_flag == 0  )
        {
          rc = cwLogError(kTypeMismatchRC,"The type 0x%x is not valid for the variable: %s:%i %s:%i type:0x%x.",
                          argTypeFlag,var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id,var->varDesc->type);
          goto errLabel;
        }
      }
      
      // set the type of the LHS to force the incoming value to be coerced to this type
      var->local_value[ next_local_value_idx ].tflag = value_type_flag;

      // set the new local value in var->local_value[next_local_value_idx]
      if((rc = value_set(var->local_value + next_local_value_idx, val )) != kOkRC )
      {
        rc = cwLogError(rc,"Value set failed on '%s:%i %s:%i",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id);
        goto errLabel;
      }

      // make the new local value current
      var->value           = var->local_value + next_local_value_idx;
      var->local_value_idx = next_local_value_idx;
      
      // If the proc is fully initialized ...
      if( var->proc->varMapA != nullptr )
      {
        // ... then inform the proc. that the value changed
        // Note 1: We don't want to this call to occur if we are inside or prior to 'proc.create()' 
        // call because calls' to 'proc.value()' will see the proc in a incomplete state)
        // Note 2: If this call returns an error then the value assignment is cancelled
        // and the value does not change.
        rc = var_schedule_notification( var );
      }

      //printf("%p set: %s:%s  0x%x\n",var->value, var->proc->label,var->label,var->value->tflag);

      if( rc == kOkRC )
      {
        if( var->ui_var != nullptr && var->ui_var->user_id != kInvalidId )
          var_send_to_ui(var);
        
        // send the value to connected downstream proc's
        rc = _var_broadcast_new_value( var );
      }
      else
      {
        // cancel the assignment and restore the original value
        var->value           = original_value;
        var->local_value_idx = original_value_idx;
      }
      
    errLabel:
      return rc;
    }
    
    
    

    // 'valueTypeFlag' is the type (tflag) of 'value'
    template< typename T >
    rc_t _var_set_driver( variable_t* var, unsigned valueTypeFlag, T value )
    {
      rc_t rc;

      // if this variable is fed from the output of an external proc - then it's local value cannot be set
      if(is_connected_to_source(var)   )
      {
        return cwLogError(kInvalidStateRC,"Cannot set the value on the connected variable %s:%i-%s:%i.",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id);
      }

      // if this assignment targets a specific channel ...
      if( var->chIdx != kAnyChIdx )
      {
        rc = _var_set_template( var, valueTypeFlag, value ); // ...  then set it alone
      }
      else // ... otherwise set all channels.
      {
        for(; var!=nullptr; var=var->ch_link)
          if((rc = _var_set_template( var, valueTypeFlag, value )) != kOkRC)
            break;
      }

      if(rc != kOkRC )
        rc = cwLogError(rc,"Variable value set failed on '%s:%i %s:%i",cwStringNullGuard(var->proc->label),var->proc->label_sfx_id,cwStringNullGuard(var->label),var->label_sfx_id);
      
      return rc;
    }

    
    rc_t  _var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, abuf_t* abuf )
    {
      rc_t rc;
      variable_t* var = nullptr;
      if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) != kOkRC )
        return rc;

      if( var != nullptr )
        _var_set_driver( var, kABufTFl, abuf );

      return rc;
    }

    rc_t  _var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, mbuf_t* mbuf )
    {
      rc_t rc;
      variable_t* var = nullptr;
      if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) != kOkRC )
        return rc;

      if( var != nullptr )
        _var_set_driver( var, kMBufTFl, mbuf );

      return rc;
    }

    rc_t  _var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, rbuf_t* rbuf )
    {
      rc_t rc;
      variable_t* var = nullptr;
      if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) != kOkRC )
        return rc;

      if( var != nullptr )
        _var_set_driver( var, kRBufTFl, rbuf );

      return rc;
    }
    

    rc_t  _var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, fbuf_t* fbuf )
    {
      rc_t rc;
      variable_t* var = nullptr;
      if((rc = var_register_and_set( proc, var_label, sfx_id, vid, chIdx, var)) != kOkRC )
        return rc;

      if( var != nullptr )
        _var_set_driver( var, kFBufTFl, fbuf );

      return rc;
    }


    rc_t  _var_map_id_to_index(  proc_t* proc, unsigned vid, unsigned chIdx, unsigned& idxRef )
    {
      unsigned idx = vid * proc->varMapChN + (chIdx == kAnyChIdx ? 0 : (chIdx+1));

      // verify that the map idx is valid
      if( idx >= proc->varMapN )
        return cwLogError(kAssertFailRC,"The variable map positioning location %i is out of the range %i on proc '%s:%i' vid:%i ch:%i.", idx, proc->varMapN, proc->label,proc->label_sfx_id,vid,chIdx);

      idxRef = idx;
  
      return kOkRC;
    }

    rc_t  _var_map_label_to_index(  proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, unsigned& idxRef )
    {
      rc_t        rc  = kOkRC;
      variable_t* var = nullptr;
  
      idxRef = kInvalidIdx;
      if((rc = var_find(proc, var_label, sfx_id, chIdx, var )) == kOkRC)
        rc = _var_map_id_to_index( proc, var->vid, chIdx, idxRef );
     
      return rc;
    }

    rc_t _var_add_to_ch_list( proc_t* proc, variable_t* new_var )
    {
      rc_t rc = kOkRC;
      
      variable_t* base_var = nullptr;
      variable_t* v0 = nullptr;
      variable_t* v1 = nullptr;
      
      if( new_var->chIdx == kAnyChIdx )
        return kOkRC;
      
      if((base_var = _var_find_on_label_and_ch( proc, new_var->label, new_var->label_sfx_id, kAnyChIdx )) == nullptr )
      {
        rc = cwLogError(kInvalidStateRC,"The base channel variable does not exist for '%s:%i.%s:%i'. This is an illegal state.", proc->label, proc->label_sfx_id, new_var->label, new_var->label_sfx_id );
        goto errLabel;
      }

      // insert v0 in order by channel number
      for(v0=base_var,v1=base_var->ch_link; v1!=nullptr; v1=v1->ch_link)
      {
        if( v1->chIdx > new_var->chIdx )
          break;
        v0 = v1;
      }

      // the new var channel index should never match the previous or next channel index
      assert( v0->chIdx != new_var->chIdx && (v1==nullptr || v1->chIdx != new_var->chIdx ) );

      new_var->ch_link = v1;
      v0->ch_link      = new_var;
      
      
    errLabel:
      return rc;
      
    }

    rc_t _var_set_type( variable_t* var, unsigned type_flag )
    {
      rc_t rc = kOkRC;

      if( cwIsNotFlag(var->classVarDesc->type,kRuntimeTFl) )
      {
        rc = cwLogError(kOpFailRC,"It is invalid to change the type of a static (non-runtime) type variable.");
        goto errLabel;
      }

      // Duplicate the varDesc with the 'type' field set to type_flag      
      if( var->localVarDesc == nullptr )
      {
        var->localVarDesc    = mem::allocZ<var_desc_t>();
        *(var->localVarDesc) = *(var->classVarDesc);
        var->localVarDesc->link = nullptr;    
      }
  
      var->localVarDesc->type = type_flag;
      var->varDesc            = var->localVarDesc;
      
    errLabel:
      return rc;
    }
    
    // Create a variable and set it's value from 'value_cfg'.
    // If 'value_cfg' is null then use the value from var->varDesc->val_cfg.
    rc_t _var_create( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFl, variable_t*& varRef )
    {
      rc_t        rc  = kOkRC;
      variable_t* var = nullptr;
      var_desc_t* vd  = nullptr;
      
      varRef = nullptr;

      // if this var already exists - it can't be created again
      if((var = _var_find_on_label_and_ch(proc,var_label, sfx_id, chIdx)) != nullptr )
      {
        rc = cwLogError(kInvalidStateRC,"The variable '%s:%i' ch:%i has already been created on the proc: '%s:%i'.",var_label,sfx_id,chIdx,proc->label,proc->label_sfx_id);
        goto errLabel;
      }

      // locate the var desc
      if((vd = var_desc_find( proc->class_desc, var_label)) == nullptr )
      {
        rc = cwLogError(kInvalidIdRC,"Unable to locate the variable '%s:%i' in class '%s'.", var_label, sfx_id, proc->class_desc->label );
        goto errLabel;
      }

      // create the var
      var = mem::allocZ<variable_t>();

      var->varDesc      = vd;
      var->classVarDesc = vd;
      var->proc         = proc;
      var->label        = mem::duplStr(var_label);
      var->label_sfx_id = sfx_id;
      var->vid          = id;
      var->chIdx        = chIdx;
      var->value        = nullptr;
      var->type         = kInvalidTFl;

      if( altTypeFl != kInvalidTFl )
        _var_set_type(var,altTypeFl);
      
      // if no value was given then set the value to the value given in the class
      if( value_cfg == nullptr )
        value_cfg = var->varDesc->val_cfg;
      
      // if value_cfg is valid set the variable value
      if( value_cfg != nullptr && cwIsNotFlag(vd->type,kRuntimeTFl))
        if((rc = var_set_from_cfg( var, value_cfg )) != kOkRC )
          goto errLabel;

      // Add the variable to the end of the chain
      if( proc->varL == nullptr )
        proc->varL = var;
      else
      {
        variable_t* v = proc->varL;
        while( v->var_link != nullptr )
          v=v->var_link;
        v->var_link = var;
        
      }

      // link the new var into the ch_link list
      if((rc = _var_add_to_ch_list(proc, var )) != kOkRC )
        goto errLabel;


    errLabel:
      if( rc != kOkRC )
      {
        var_destroy(var);
        cwLogError(kOpFailRC,"Variable creation failed on '%s:%i.%s:%i' ch:%i.", proc->label, proc->label_sfx_id, var_label, sfx_id, chIdx );
      }
      else
      {
        varRef = var;
        cwLogMod("Created var: %s:%i.%s:%i ch:%i.", proc->label, proc->label_sfx_id, var_label, sfx_id, chIdx );
      }
      
      return rc;
    }

    void _class_desc_print( const class_desc_t* cd )
    {
      const var_desc_t*   vd = cd->varDescL;
      cwLogPrint("%s\n",cwStringNullGuard(cd->label));
        
      for(; vd!=nullptr; vd=vd->link)
      {
        const char* srcFlStr    = vd->flags & kSrcVarDescFl    ? "src"      : "   ";
        const char* srcOptFlStr = vd->flags & kSrcOptVarDescFl ? "srcOpt"   : "      ";
        const char* plyMltFlStr = vd->flags & kMultVarDescFl   ? "mult"     : "    ";
          
        cwLogPrint("  %10s 0x%08x %s %s %s %s\n", cwStringNullGuard(vd->label), vd->type, srcFlStr, srcOptFlStr, plyMltFlStr, cwStringNullGuard(vd->docText) );
      }  
    }

    void _network_preset_print( const network_preset_t* net_preset )
    {
      switch( net_preset->tid )
      {
        case kPresetVListTId:
          {
            const preset_value_t* net_val = net_preset->u.vlist.value_head;
            for(; net_val!=nullptr; net_val=net_val->link)
            {
              switch( net_val->tid )
              {
                case kNetRefPresetValueTId:
                  _network_preset_print( net_val->u.npv.net_preset );
                  break;
                  
                case kDirectPresetValueTId:
                  cwLogPrint("    %s:%i %s:%i ch:%i pidx:%i",cwStringNullGuard(net_val->u.pvv.proc->label),net_val->u.pvv.proc->label_sfx_id,cwStringNullGuard(net_val->u.pvv.var->label),net_val->u.pvv.var->label_sfx_id,net_val->u.pvv.var->chIdx,net_val->u.pvv.pairTblIdx);
                  value_print( &net_val->u.pvv.value );
                  cwLogPrint("\n");
                  break;
                  
                default:
                  cwLogError(kInvalidIdRC,"The preset value type id %i is unknown.",net_val->tid);
                  goto errLabel;
              }
            }
          }
          break;
          
        case kPresetDualTId:
          cwLogPrint("     %s %s %f",net_preset->u.dual.pri->label,net_preset->u.dual.sec->label,net_preset->u.dual.coeff);
          break;
      }

    errLabel:
      return;
    }

    
    
  } // flow
} // cw


cw::flow::var_desc_t*  cw::flow::var_desc_create( const char* label, const object_t* cfg )
{
  var_desc_t* vd = mem::allocZ<var_desc_t>();
  vd->label = label;
  vd->cfg   = cfg;
  return vd;
}

void cw::flow::var_desc_destroy( var_desc_t* var_desc )
{
  if( var_desc != nullptr )
  {
    if( var_desc_has_recd_format(var_desc) )
      recd_format_destroy(var_desc->fmt.recd_fmt);
    
    mem::release(var_desc->proxyProcLabel);
    mem::release(var_desc->proxyVarLabel);
    mem::release(var_desc);
  }
}

unsigned cw::flow::var_desc_attr_label_to_flag( const char* attr_label )
{
  return labelToId(_varDescAttrFlagsA,attr_label,kInvalidVarDescFl);
}

const char*  cw::flow::var_desc_flag_to_attribute( unsigned flag )
{
  return idToLabel(_varDescAttrFlagsA,flag,kInvalidVarDescFl);
}

const cw::idLabelPair_t* cw::flow::var_desc_flag_array( unsigned& array_cnt_ref )
{
  array_cnt_ref = sizeof(_varDescAttrFlagsA)/sizeof(_varDescAttrFlagsA[0]);
  return _varDescAttrFlagsA;
}

void cw::flow::class_desc_destroy( class_desc_t* class_desc)
{
  // release the var desc list
  var_desc_t*   vd0 = class_desc->varDescL;
  var_desc_t*   vd1 = nullptr;        
  while( vd0 != nullptr )
  {
    vd1 = vd0->link;
    var_desc_destroy(vd0);
    vd0 = vd1;
  }

  // release the preset list
  class_preset_t* pr0 = class_desc->presetL;
  class_preset_t* pr1 = nullptr;
  while( pr0 != nullptr )
  {
    pr1 = pr0->link;
    mem::release(pr0);
    pr0 = pr1;
  }
  class_desc->presetL = nullptr;

  if( class_desc->ui != nullptr )
  {
    mem::release(class_desc->ui->presetA);
    mem::release(class_desc->ui);
  }
  
}

cw::flow::class_desc_t* cw::flow::class_desc_find( flow_t* p, const char* label )
{
  for(unsigned i=0; i<p->classDescN; ++i)
    if( textIsEqual(p->classDescA[i].label,label))
      return p->classDescA + i;

  for(unsigned i=0; i<p->udpDescN; ++i)
    if( textIsEqual(p->udpDescA[i].label,label))
      return p->udpDescA + i;
  
  return nullptr;
}

const cw::flow::class_desc_t* cw::flow::class_desc_find(  const flow_t* p, const char* class_desc_label )
{  return class_desc_find( const_cast<flow_t*>(p), class_desc_label ); }


const cw::flow::var_desc_t* cw::flow::var_desc_find( const class_desc_t* cd, const char* label )
{
  const var_desc_t* vd = cd->varDescL;
      
  for(; vd != nullptr; vd=vd->link )
    if( textCompare(vd->label,label) == 0 )
      return vd;
  return nullptr;
}

cw::flow::var_desc_t* cw::flow::var_desc_find( class_desc_t* cd, const char* label )
{
  return (var_desc_t*)var_desc_find( (const class_desc_t*)cd, label);
}

cw::rc_t cw::flow::var_desc_find( class_desc_t* cd, const char* label, var_desc_t*& vdRef )
{
  if((vdRef = var_desc_find(cd,label)) == nullptr )
    return cwLogError(kInvalidArgRC,"The variable desc. named '%s' could not be found on the class '%s'.",label,cd->label);
  return kOkRC;
}

bool cw::flow::var_desc_has_recd_format( var_desc_t* vd )
{
  return vd!=nullptr && vd->type & kRBufTFl && vd->fmt_cfg != nullptr && vd->fmt.recd_fmt != nullptr;
}

const cw::flow::class_preset_t* cw::flow::class_preset_find( const class_desc_t* cd, const char* preset_label )
{
  const class_preset_t* pr;
  for(pr=cd->presetL; pr!=nullptr; pr=pr->link)
    if( textCompare(pr->label,preset_label) == 0 )
      return pr;
  
  return nullptr;
}

cw::rc_t cw::flow::class_preset_value_channel_count( const class_preset_t* class_preset, const char* var_label, unsigned& ch_cnt_ref )
{
  rc_t rc = kOkRC;
  const object_t* var_cfg;
      
  ch_cnt_ref = 0;

  if( class_preset->cfg == nullptr )
  {
    rc = cwLogError(rc,"The preset '%s' is not valid.", cwStringNullGuard(class_preset->label));
    goto errLabel;
  }
      
  if((var_cfg = class_preset->cfg->find(var_label)) == nullptr )
  {
    rc = cwLogError(rc,"Preset variable '%s' not found on preset '%s'.",cwStringNullGuard(var_label),cwStringNullGuard(class_preset->label));
    goto errLabel;
  }

  if( var_cfg->is_list() )
    ch_cnt_ref = var_cfg->child_count();
  else
    ch_cnt_ref = 1;
      
errLabel:
  return rc;
}

cw::rc_t cw::flow::class_preset_value_channel_count( const class_desc_t* class_desc, const char* class_preset_label, const char* var_label, unsigned& ch_cnt_ref )
{
  rc_t rc = kOkRC;
  const class_preset_t* class_preset = nullptr;

  ch_cnt_ref = 0;
  
  if((class_preset = class_preset_find(class_desc, class_preset_label)) == nullptr )
  {
    rc = cwLogError(kEleNotFoundRC,"The preset '%s' could not be found on the class description: '%s'.",cwStringNullGuard(class_preset_label),cwStringNullGuard(class_desc->label));
    goto errLabel;
  }

  rc = class_preset_value_channel_count( class_preset, var_label, ch_cnt_ref );
  
errLabel:
  return rc;
}

cw::rc_t cw::flow::class_preset_value_channel_count( const flow_t* p, const char* class_desc_label, const char* class_preset_label, const char* var_label, unsigned& ch_cnt_ref )
{
  rc_t rc = kOkRC;
  const class_desc_t* class_desc;

  ch_cnt_ref = 0;
  
  if((class_desc = class_desc_find(p,class_desc_label)) == nullptr )
  {
    rc = cwLogError(kEleNotFoundRC,"The class description '%s' could not be found.",cwStringNullGuard(class_desc_label));
    goto errLabel;
  }

  rc = class_preset_value_channel_count(class_desc, class_preset_label, var_label, ch_cnt_ref );

errLabel:
  return rc;
}

cw::rc_t cw::flow::class_preset_has_var( const class_preset_t* class_preset, const char* var_label, bool& fl_ref )
{
  rc_t rc = kOkRC;
  
  fl_ref = false;

  if( class_preset->cfg == nullptr )
  {
    rc = cwLogError(rc,"The preset '%s' is not valid.", cwStringNullGuard(class_preset->label));
    goto errLabel;
  }
      
  fl_ref = class_preset->cfg->find(var_label) != nullptr;
      
errLabel:
  return rc;
  
}

cw::rc_t cw::flow::class_preset_has_var( const class_desc_t* class_desc, const char* class_preset_label, const char* var_label, bool& fl_ref )
{
  rc_t rc = kOkRC;
  const class_preset_t* class_preset = nullptr;

  fl_ref = false;
  
  if((class_preset = class_preset_find(class_desc, class_preset_label)) == nullptr )
  {
    rc = cwLogError(kEleNotFoundRC,"The preset '%s' could not be found on the class description: '%s'.",cwStringNullGuard(class_preset_label),cwStringNullGuard(class_desc->label));
    goto errLabel;
  }

  rc = class_preset_has_var( class_preset, var_label, fl_ref );
  
errLabel:
  return rc;
}

cw::rc_t cw::flow::class_preset_has_var( const flow_t* p, const char* class_desc_label, const char* class_preset_label, const char* var_label, bool& fl_ref )
{
  rc_t rc = kOkRC;
  const class_desc_t* class_desc;

  fl_ref = false;

  if((class_desc = class_desc_find(p,class_desc_label)) == nullptr )
  {
    rc = cwLogError(kEleNotFoundRC,"The class description '%s' could not be found.",cwStringNullGuard(class_desc_label));
    goto errLabel;
  }

  rc = class_preset_has_var(class_desc, class_preset_label, var_label, fl_ref );

errLabel:
  return rc;
}


void cw::flow::class_dict_print( flow_t* p )
{
  for(unsigned i=0; i<p->classDescN; ++i)
    _class_desc_print(p->classDescA+i);
  
  for(unsigned i=0; i<p->udpDescN; ++i)
    _class_desc_print(p->udpDescA+i);
}


cw::flow::external_device_t* cw::flow::external_device_find( flow_t* p, const char* device_label, unsigned typeId, unsigned inOrOutFl, const char* midiPortLabel )
{
  for(unsigned i=0; i<p->deviceN; ++i)
    if( (device_label==nullptr || cw::textIsEqual(p->deviceA[i].devLabel,device_label))
        && p->deviceA[i].typeId==typeId
        && cwIsFlag(p->deviceA[i].flags,inOrOutFl)
        && (midiPortLabel==nullptr || cw::textIsEqual(p->deviceA[i].portLabel,midiPortLabel)) )
      return p->deviceA + i;
  
  cwLogError(kInvalidArgRC,"The %s device named '%s' could not be found.", cwIsFlag(inOrOutFl,kInFl) ? "in" : "out", device_label );
  
  return nullptr;
}


void cw::flow::network_print( const network_t& net )
{
  // for each proc in the network
  for(unsigned i=0; i<net.procN; ++i)
  {
    proc_t* proc = net.procA[i];
    proc_print(proc);

    // if this proc has an  internal network
    if( proc->internal_net != nullptr )
    {
      cwLogPrint("------- Begin Nested Network: %s -------\n",cwStringNullGuard(proc->label));
      for(const network_t* n = proc->internal_net; n!=nullptr; n=n->poly_link)      
        network_print(*n);
      cwLogPrint("------- End Nested Network: %s -------\n",cwStringNullGuard(proc->label));
    }
        
  }

  if(net.presetN > 0 )
  {
    cwLogPrint("Presets:\n");
    for(unsigned i=0; i<net.presetN; ++i)
    {
      const network_preset_t* net_preset = net.presetA + i;
      cwLogPrint("%i %s\n",i,cwStringNullGuard(net_preset->label));
      
      _network_preset_print(net_preset);
    }
    cwLogPrint("\n");
  }
}

const cw::flow::network_preset_t* cw::flow::network_preset_from_label( const network_t& net, const char* preset_label )
{
  for(unsigned i=0; i<net.presetN; ++i)
    if( textIsEqual(net.presetA[i].label,preset_label))
      return net.presetA + i;
  return nullptr;
}


unsigned cw::flow::proc_mult_count( const network_t& net, const char* proc_label )
{
  unsigned multN = 0;
  for(unsigned i=0; i<net.procN; ++i)
    if( textIsEqual(net.procA[i]->label,proc_label) )
      multN += 1;

  return multN;
}
    
cw::rc_t cw::flow::proc_mult_sfx_id_array( const network_t& net, const char* proc_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref )
{
  rc_t     rc    = kOkRC;
  unsigned multN = 0;

  idN_ref = 0;
  
  for(unsigned i=0; i<net.procN; ++i)
    if( textIsEqual(net.procA[i]->label,proc_label) )
    {
      if( multN >= idAllocN )
      {
        rc = cwLogError(kBufTooSmallRC,"The mult-sfx-id result array is too small for the proc:'%s'.",cwStringNullGuard(proc_label));
        goto errLabel;
      }
      
      idA[multN] = net.procA[i]->label_sfx_id;
      multN += 1;
    }
  
  idN_ref = multN;

errLabel:
  
  return rc;  
}

void cw::flow::proc_destroy( proc_t* proc )
{
  if( proc == nullptr )
    return;
      
  if( proc->class_desc->members != nullptr && proc->class_desc->members->destroy != nullptr && proc->userPtr != nullptr )
    proc->class_desc->members->destroy( proc );

  // destroy the proc instance variables
  variable_t* var0 = proc->varL;
  variable_t* var1 = nullptr;      
  while( var0 != nullptr )
  {
    var1 = var0->var_link;
    var_destroy(var0);
    var0 = var1;
  }

  // release the preset list
  class_preset_t* pr0 = proc->presetL;
  class_preset_t* pr1 = nullptr;
  while( pr0 != nullptr )
  {
    pr1 = pr0->link;
    mem::release(pr0);
    pr0 = pr1;
  }
  proc->presetL = nullptr;
      
  proc->varL = nullptr;
      
  mem::release(proc->label);
  mem::release(proc->varMapA);
  mem::release(proc->modVarMapA);
  mem::release(proc);
}

cw::rc_t cw::flow::proc_validate( proc_t* proc )
{
  rc_t rc = kOkRC;

  for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link)
  {
    if( var->label == nullptr )
    {
      rc = cwLogError(kInvalidStateRC,"A var with no label was encountered.");
      continue;      
    }
    
    if( var->value == nullptr )
    {
      rc = cwLogError(kInvalidStateRC,"The var '%s:%i' has no value.",var->label,var->label_sfx_id);
      continue;      
    }

    // the assigned value must have exactly one type
    if(!math::isPowerOfTwo( var->value->tflag ) )
    {
      rc = cwLogError(kInvalidStateRC,"The var '%s:%i' has the invalid type flag:0x%x",var->label,var->label_sfx_id,var->value->tflag);
      continue;
    }

    // if var is using a local value (not connected to a source variable) then the type of the value must be valid with the variable class
    if( !is_connected_to_source(var) && !(var->varDesc->type & var->value->tflag) )
    {
      rc = cwLogError(kInvalidStateRC, "The value type flag '%s' (0x%x) of '%s:%i-%s:%i' is not found in the variable class type flags: '%s' (0x%x)",
                      value_type_flag_to_label(var->value->tflag),var->value->tflag,
                      var->proc->label,var->proc->label_sfx_id,
                      var->label,var->label_sfx_id,
                      value_type_flag_to_label(var->varDesc->type),var->varDesc->type);
      continue;
    }

    // By setting the var->type field all future assignments to this variable
    // must be coercible to this type.  See _var_set_template()
    var->type = var->value->tflag;
  }
  
  return rc;
}


cw::flow::proc_t* cw::flow::proc_find( network_t& net, const char* proc_label, unsigned sfx_id )
{
  
  for(unsigned i=0; i<net.procN; ++i)
  {
    assert( net.procA[i] != nullptr );
    
    if( net.procA[i]->label_sfx_id==sfx_id && textIsEqual(proc_label,net.procA[i]->label) )
      return net.procA[i];
  }

  if( net.poly_link != nullptr )
    return proc_find(*net.poly_link, proc_label, sfx_id );

  return nullptr;
}

cw::rc_t cw::flow::proc_find( network_t& net, const char* proc_label, unsigned sfx_id, proc_t*& procPtrRef )
{
  rc_t rc = kOkRC;
      
  if((procPtrRef = proc_find(net,proc_label,sfx_id)) != nullptr )
    return rc;
      
  return cwLogError(kInvalidArgRC,"The proc '%s:%i' was not found.", proc_label, sfx_id );
}

const cw::flow::class_preset_t* cw::flow::proc_preset_find( const proc_t* proc, const char* preset_label )
{
  const class_preset_t* pr;
  for(pr=proc->presetL; pr!=nullptr; pr=pr->link)
    if( textCompare(pr->label,preset_label) == 0 )
      return pr;
  
  return nullptr;
}


void*    cw::flow::global_var( proc_t* proc, const char* var_label )
{
  global_var_t* gv;
  
  assert( proc->net != nullptr );
  
  for(gv=proc->ctx->globalVarL; gv!=nullptr; gv=gv->link )
    if( textIsEqual(proc->class_desc->label,gv->class_label) && textIsEqual(gv->var_label,var_label) )
      return gv->blob;
  
  return nullptr;
}

cw::rc_t  cw::flow::global_var_alloc( proc_t* proc, const char* var_label, const void* blob, unsigned blobByteN )
{
  rc_t rc = kOkRC;
  global_var_t* gv;
  void* v;

  unsigned allocWordN = 0;

  if((v = global_var(proc,var_label)) != nullptr )
  {
    rc = cwLogError(kInvalidArgRC,"The global variable '%s:%s' already exists.",cwStringNullGuard(proc->class_desc->label),cwStringNullGuard(var_label));
    goto errLabel;
  }

  gv = mem::allocZ<global_var_t>();

  allocWordN = std::max(blobByteN/sizeof(unsigned),1ul);  
  
  gv->class_label = proc->class_desc->label;
  gv->var_label   = mem::duplStr(var_label);
  gv->blob        = mem::allocZ<unsigned>(allocWordN);
  gv->blobByteN   = blobByteN;
  memcpy(gv->blob,blob,blobByteN);
  
  gv->link = proc->ctx->globalVarL;
  proc->ctx->globalVarL = gv;
    
errLabel:
  return rc;
}


void cw::flow::proc_print( proc_t* proc )
{
  cwLogPrint("%s:%i\n", proc->label,proc->label_sfx_id);
  for(variable_t* var = proc->varL; var!=nullptr; var=var->var_link)
    if( var->chIdx == kAnyChIdx )
      for(variable_t* v0 = var; v0!=nullptr; v0=v0->ch_link)
        _var_print(v0);      
  
  if( proc->class_desc->members->report )
    proc->class_desc->members->report( proc );
}

unsigned cw::flow::proc_var_count( proc_t* proc )
{
  unsigned n = 0;
  for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link)
    ++n;

  return n;
}

char* cw::flow::proc_expand_filename( const proc_t* proc, const char* fname )
{
  bool useProjDirFl = proc->ctx->proj_dir != nullptr && textLength(fname) > 1 && fname[0] == '$';
  char* fn0 = nullptr;
  
  if( useProjDirFl )
    fn0 = filesys::makeFn(proc->ctx->proj_dir,fname+1,nullptr,nullptr);

  char* fn1 = filesys::expandPath(useProjDirFl ? fn0 : fname);

  mem::release(fn0);
  
  return fn1;
}  

// Call proc->proc_desc->value() on every var in the proc->modVarMapA[].
// This function is called inside proc->proc_desc->exec() and is therefore guaranteed to be executed without
// contention from other threads.
cw::rc_t cw::flow::proc_notify( proc_t* proc, unsigned flags )
{
  rc_t rc = kOkRC;
  unsigned modN = 0;
  
  if( proc->modVarMapN == 0 )
  {
    if( cwIsNotFlag(flags,kQuietPnFl) )
      rc =cwLogError(kInvalidStateRC,"Calling proc_notify() on the processor '%s:%i' is invalid because it does not have any variables marked for notification.",cwStringNullGuard(proc->label),proc->label_sfx_id);
    goto errLabel;
  }
  
  // get the count of variables to be updated
  modN = proc->modVarMapFullCnt.load( std::memory_order_acquire );

  if( modN )
  {
    if( cwIsFlag(flags,kCallbackPnFl) )
    {
      for(unsigned i=0; i<modN; ++i)
      {
        // get a pointer to the var that has been marked as modified
        variable_t* var = proc->modVarMapA[ proc->modVarMapTailIdx ];

        // callback to inform the proc that the var has changed
        proc->class_desc->members->notify( var->proc, var );

        // mark this var as having been removed from the modVarMapA[]
        var->modN.store(0,std::memory_order_release );

        // increment modVarMapA[]'s tail index
        proc->modVarMapTailIdx = (proc->modVarMapTailIdx + 1) % proc->modVarMapN;        
      }
    }
        
    // decrement the count of elemnts in the modVarMapA[]
    proc->modVarMapFullCnt.fetch_sub(modN, std::memory_order_release );
  }

errLabel:
  return rc;
}

cw::rc_t cw::flow::var_create( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned id, unsigned chIdx, const object_t* value_cfg, unsigned altTypeFl, variable_t*& varRef )
{
  rc_t rc = kOkRC;

  rc = _var_create( proc, var_label, sfx_id, id, chIdx, value_cfg, altTypeFl, varRef );

  return rc;
}

void cw::flow::var_destroy( variable_t* var )
{
  if( var != nullptr )
  {
    for(unsigned i=0; i<kLocalValueN; ++i)
      value_release(var->local_value+i);

    if( var->localVarDesc != nullptr )
      mem::release(var->localVarDesc);
    
    mem::release(var->label);
    mem::release(var);
  }
}

cw::rc_t  cw::flow::var_channelize( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const object_t* value_cfg, unsigned vid, variable_t*& varRef )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  variable_t* base_var = nullptr;
  varRef = nullptr;

  if((base_var = _var_find_on_label_and_ch( proc, var_label, sfx_id, kAnyChIdx)) == nullptr)
  {
    rc = cwLogError(kInvalidStateRC,"The base ('any') channel variable could not be located on '%s:%i.%s:%i'.",proc->label,proc->label_sfx_id,var_label,sfx_id);
    goto errLabel;
  }
 
  // locate the variable with the stated chIdx
  var = _var_find_on_label_and_ch( proc, var_label, sfx_id, chIdx );
  
  // 'src' variables cannot be channelized
  /*
  if( cwIsFlag(base_var->varDesc->flags,kSrcVarFl) )
  {
    rc = cwLogError(rc,"'src' variables cannot be channelized.");
    goto errLabel;
  }
  */
  
  // if the requested var was not found then create a new variable with the requested channel index
  if( var == nullptr && chIdx != kAnyChIdx )
  {
    // create the channelized var
    if((rc = _var_create( proc, var_label, sfx_id, vid, chIdx, value_cfg, kInvalidTFl, var )) != kOkRC )
      goto errLabel;

    // if no value was set then set the value from the 'any' channel
    if( value_cfg == nullptr )
    {
      // if the base-var is connected to a source ...
      if( is_connected_to_source(base_var) )
      {
        // ... then connect the new var to a source also
        
        // Attempt to find a matching channel on the source
        variable_t* src_ch_var      = base_var->src_var;
        variable_t* last_src_ch_var = base_var->src_var;
        unsigned    src_ch_cnt      = 0;
        bool        anyChFl         = false;
        bool        zeroChFl        = false;
        
        for(; src_ch_var!=nullptr; src_ch_var=src_ch_var->ch_link)
        {
          last_src_ch_var = src_ch_var;
          if( src_ch_var->chIdx == var->chIdx )
            break;

          if( src_ch_var->chIdx == kAnyChIdx )
            anyChFl = true;

          if( src_ch_var->chIdx == 0 )
            zeroChFl = true;
          
          src_ch_cnt += 1;
        }

        // If there is more than one channel available, in addition to the kAnyCh, and the src and dst var's do not have matching ch indexes
        // then there is a possibility that this is an unexpected connection between different channels.
        // However if there only any-ch and/or ch=0 is available then this is simply a one channel to many channels which is common.
        if( src_ch_var == nullptr && (anyChFl==false || (src_ch_cnt>1 && zeroChFl==false))  && last_src_ch_var->chIdx != var->chIdx )
        {
          cwLogWarning("A connection is being made where channel src and dst. channels don't match and more than one src channel is available. src:%s:%i-%s:%i ch:%i of %i dst:%s:%i-%s:%i ch:%i",
                       last_src_ch_var->proc->label, last_src_ch_var->proc->label_sfx_id,
                       last_src_ch_var->label,       last_src_ch_var->label_sfx_id, last_src_ch_var->chIdx, src_ch_cnt,
                       var->proc->label,             var->proc->label_sfx_id,
                       var->label,                   var->label_sfx_id,             var->chIdx );
        }
        
        // if no matching channel is found connect to the last valid source channel
        // (Connecting to the last valid source is better than connecting to base_var->src_var
        //  because if a var has more than a base var it is unlikely to update the base_var.)
        var_connect( last_src_ch_var, var );
            
      }
      else // the base-var is not connected, and no value was provided for the new var
      {
      
        // Set the value of the new variable to the value of the 'any' channel
        value_duplicate( var->local_value[ var->local_value_idx], base_var->local_value[ base_var->local_value_idx ] );

        // If the 'any' channel value was set to point to it's local value then do same with this value
        if( base_var->local_value + base_var->local_value_idx == base_var->value )
          var->value = var->local_value + var->local_value_idx;
      }
    }
    
  }
  else // the var was found - set the value
  {
    
    // a correctly channelized var was found - but we still may need to set the value
    if( value_cfg != nullptr )
    {
      rc = var_set_from_cfg( var, value_cfg );
    }
    else
    {
      cwLogWarning("An existing var (%s:%i.%s:%i ch:%i) was specified for channelizing but no value was provided.", cwStringNullGuard(proc->label), proc->label_sfx_id, cwStringNullGuard(var_label), sfx_id, chIdx );
    }
  }

  assert( var != nullptr );
  varRef = var;
  
 errLabel:
  if( rc != kOkRC )
    rc = cwLogError(rc,"Channelize failed for variable '%s:%i' on proc '%s:%i' ch:%i.", var_label, sfx_id, proc->label, proc->label_sfx_id, chIdx );
  
  return rc;
}

unsigned cw::flow::var_channel_count( proc_t* proc, const char* var_label, unsigned sfx_id )
{
  rc_t rc = kOkRC;
  unsigned chN = kInvalidCnt;
  variable_t* base_var = nullptr;

  if((rc = _var_find_on_label_and_ch( proc, var_label, sfx_id, kAnyChIdx, base_var)) != kOkRC || base_var==nullptr)
  {
    cwLogError(rc,"Var. channel count calc failed.");
    goto errLabel;
  }

  chN = 0;
  for(base_var=base_var->ch_link; base_var!=nullptr; base_var=base_var->ch_link)
    if( base_var->chIdx+1 > chN )
      chN = base_var->chIdx + 1;
     

errLabel:
  return chN;
}

cw::rc_t cw::flow::var_schedule_notification( variable_t* var )
{
  rc_t rc;
  if((rc = _mod_var_map_update( var )) != kOkRC )
    goto errLabel;

  if( var->flags & kLogVarFl )
  {
    if( var->proc->ctx->printLogHdrFl )
    {
      cwLogPrint("%s","exe cycle:    process:   id:       variable: id     vid     ch :         : : type:value  : destination\n");
      cwLogPrint("%s","---------- ----------- ----- --------------- --     ---    -----             ------------: -------------\n");
        //:        0 :          a:    0:            out:  0 vid:  2 ch: -1 :         : : <invalid>: 
        var->proc->ctx->printLogHdrFl = false;
    }
    
    cwLogPrint("%8i ",var->proc->ctx->cycleIndex);
    cwLogPrint("%10s:%5i", var->proc->label,var->proc->label_sfx_id);
    
    if( var->chIdx == kAnyChIdx )
    {
      _var_print(var);
    }
    else
    {
      cwLogPrint("\n");
      for(variable_t* ch_var = var; ch_var!=nullptr; ch_var=ch_var->ch_link)
      {
        _var_print(ch_var);
      }
      
    }
  }
  
errLabel:
  return rc;
  
}


cw::rc_t cw::flow::var_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned& flags_ref )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  flags_ref = 0;
  
  if((rc = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx,var)) != kOkRC )
    goto errLabel;

  flags_ref = var->flags;

errLabel:
  return rc;
}
    
cw::rc_t cw::flow::var_set_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flag )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx,var)) != kOkRC )
    goto errLabel;
  

  var->flags |= flag;

errLabel:
  return rc;
}
    
cw::rc_t cw::flow::var_clr_flags( proc_t* proc, unsigned chIdx, const char* var_label, unsigned sfx_id, unsigned flag )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx,var)) != kOkRC )
    goto errLabel;

  var->flags = cwClrFlag(var->flags,flag);
errLabel:
  return rc;
}

bool cw::flow::var_exists( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx )
{ return _var_find_on_label_and_ch(proc,label,sfx_id,chIdx) != nullptr; }

bool cw::flow::var_has_value( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx )
{
  variable_t* varPtr = nullptr;
  rc_t rc;
  
  if((rc = var_find( proc, label, sfx_id, chIdx, varPtr )) != kOkRC )
    return false;

  return varPtr->value != nullptr;
}

bool  cw::flow::var_is_a_source( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx )
{
  rc_t rc;
  variable_t* varPtr = nullptr;
  if((rc = var_find( proc, label, sfx_id, chIdx, varPtr)) != kOkRC )
  {
    cwLogError(kEleNotFoundRC,"The variable '%s:%i' was not found on proc:'%s:%i'. 'source' state query is invalid.",cwStringNullGuard(label),sfx_id,cwStringNullGuard(proc->label),proc->label_sfx_id);
    return false;
  }
  
  return is_a_source_var(varPtr);
}

bool  cw::flow::var_is_a_source( proc_t* proc, unsigned vid, unsigned chIdx )
{
  rc_t rc;
  variable_t* varPtr = nullptr;
  if((rc = var_find( proc, vid, chIdx, varPtr)) != kOkRC )
  {
    cwLogError(kEleNotFoundRC,"The variable with vid '%i' was not found on proc:'%s:%i'. 'source' state query is invalid.",vid,cwStringNullGuard(proc->label),proc->label_sfx_id);
    return false;
  }
  
  return is_a_source_var(varPtr);
}

cw::rc_t cw::flow::var_find( proc_t* proc, unsigned vid, unsigned chIdx, variable_t*& varRef )
{
  rc_t        rc  = kOkRC;
  unsigned    idx = kInvalidIdx;
  variable_t* var = nullptr;
      
  varRef = nullptr;

  // if the varMapA[] has not yet been formed (we are inside the proc constructor) then do a slow lookup of the variable
  if( proc->varMapA == nullptr )
  {
    if((rc = _var_find_on_vid_and_ch(proc,vid,chIdx,var)) != kOkRC )
      goto errLabel;
  }
  else
  {
    // otherwise do a fast lookup using proc->varMapA[]
    if((rc = _var_map_id_to_index(proc, vid, chIdx, idx )) == kOkRC && (idx != kInvalidIdx ))
      var = proc->varMapA[idx];
    else
    {
      rc = cwLogError(kInvalidIdRC,"The index of variable vid:%i chIdx:%i on proc '%s:%i' could not be calculated and the variable value could not be retrieved.", vid, chIdx, proc->label,proc->label_sfx_id);
      goto errLabel;
    }
  }

  // if we get here var must be non-null - (was the var registered?)
  assert( var != nullptr && rc == kOkRC );
  varRef = var;
  
 errLabel:
      
  return rc;
}

cw::rc_t cw::flow::var_find( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx, variable_t*& vRef )
{
  variable_t* var;
  vRef = nullptr;
  
  if((var = _var_find_on_label_and_ch(proc,label,sfx_id,chIdx)) != nullptr )
  {
    vRef = var;
    return kOkRC;
  }

  return cwLogError(kInvalidIdRC,"The proc '%s:%i' does not have a variable named '%s:%i'.", proc->label, proc->label_sfx_id, label, sfx_id );  
}

cw::rc_t cw::flow::var_find( proc_t* proc, const char* label, unsigned sfx_id, unsigned chIdx, const variable_t*& vRef )
{
  variable_t* v = nullptr;
  rc_t        rc = var_find(proc,label,sfx_id,chIdx,v);
  vRef = v;
  return rc;
}


cw::rc_t  cw::flow::var_channel_count( proc_t* proc, const char* label, unsigned sfx_id, unsigned& chCntRef )
{
  rc_t rc = kOkRC;
  const variable_t* var= nullptr;
  if((rc = var_find(proc,label,sfx_id,kAnyChIdx,var)) != kOkRC )
    return cwLogError(rc,"Channel count was not available because the variable '%s:%i.%s:%i' does not exist.",cwStringNullGuard(proc->label),proc->label_sfx_id,cwStringNullGuard(label),sfx_id);

  return var_channel_count(var,chCntRef);
}

cw::rc_t  cw::flow::var_channel_count( const variable_t* var, unsigned& chCntRef )
{
  rc_t rc = kOkRC;
  const variable_t* v;
  
  chCntRef = 0;
  
  if((rc = var_find( var->proc, var->label, var->label_sfx_id, kAnyChIdx, v )) != kOkRC )
  {
    rc = cwLogError(kInvalidStateRC,"The base channel variable proc could not be found for the variable '%s:%i.%s:%i'.",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id);
    goto errLabel;
  }

  for(v = v->ch_link; v!=nullptr; v=v->ch_link)
    chCntRef += 1;

 errLabel:
  return rc;
}



cw::rc_t cw::flow::var_register( proc_t* proc, const char* var_label, unsigned sfx_id,  unsigned vid, unsigned chIdx, const object_t* value_cfg, variable_t*& varRef )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;

  varRef = nullptr;

  // TODO: check for duplicate 'vid'-'chIdx' pairs on this proc
  // The concatenation of 'vid' and 'chIdx' should be unique 

  // if an exact match to label/chIdx was found
  if((var = _var_find_on_label_and_ch(proc,var_label,sfx_id,chIdx)) != nullptr )
  {
    // if a value was given - then update the value
    if( value_cfg != nullptr )
      if((rc = var_set_from_cfg( var, value_cfg )) != kOkRC )
        goto errLabel;    
  }
  else // an exact match was not found - channelize the variable
  {

    // if the kAnyChIdx has not been created for this variable  ...
    if((var = _var_find_on_label_and_ch(proc,var_label,sfx_id,kAnyChIdx)) == nullptr )
    {
      variable_t* dum = nullptr;

      // ... then create it here
      if((rc = var_create( proc, var_label, sfx_id, kInvalidId, kAnyChIdx, nullptr, kInvalidTFl, dum )) != kOkRC )
      {
        rc = cwLogError(rc,"An attempt to create the 'any-channel' for '%s:%i' failed.",cwStringNullGuard(var_label),sfx_id);
        goto errLabel;
      }

      // if the var being registered is on channel kAnyChIdx 
      if( chIdx == kAnyChIdx )
        var = dum;

    }

    // don't channelize kAnyChIdx because it must already exist
    if( chIdx != kAnyChIdx )
      if((rc = var_channelize(proc,var_label,sfx_id,chIdx,value_cfg,vid,var)) != kOkRC )
        goto errLabel;
  }

  assert( var != nullptr );
  
  var->vid = vid;
  varRef   = var;

  // The kAnyChIdx shares the 'vid' with channelized variables - this is by design (vids are unique across variables, but shared across channels)
  if((var = _var_find_on_label_and_ch(proc,var_label,sfx_id,kAnyChIdx)) == nullptr )
    rc = cwLogError(kInvalidStateRC,"The variable '%s:%i' proc '%s:%i' has no base channel.", var_label, sfx_id, proc->label, proc->label_sfx_id, chIdx);
  else
  {
    var->vid = vid;  // ... this guarantee's that the kAnyChIdx variable has a valid vid
  }
  
 errLabel:
  if( rc != kOkRC )
    rc = cwLogError(rc,"Registration failed on variable '%s:%i' proc '%s:%i' ch: %i.", var_label, sfx_id, proc->label, proc->label_sfx_id, chIdx);
  
  return rc;
}

bool cw::flow::is_connected_to_source( const variable_t* var )
{
  // if this var does not have a 'src_ptr' then it can't be connected to an external proc
  if( var->src_var == nullptr || var->value == nullptr )
    return false;

  // if this var is using a local value then it can't be connected to an external proc
  for(unsigned i=0; i<kLocalValueN; ++i)
    if( var->value == var->local_value + i )
      return false;

  return true;
}

bool cw::flow::is_a_source_var( const variable_t* var )
{ return var->dst_head != nullptr; }

bool cw::flow::var_has_recd_format( const variable_t* var )
{ return var!=nullptr && var_desc_has_recd_format(var->varDesc); }


void cw::flow::var_connect( variable_t* src_var, variable_t* in_var )
{
  assert( in_var->dst_link == nullptr );

  // connect in_var into src_var's outgoing var chain   
  if( src_var->dst_head == nullptr )
    src_var->dst_head = in_var;
  else
    src_var->dst_tail->dst_link = in_var;

  src_var->dst_tail = in_var;
  
  in_var->value    = src_var->value;
  in_var->src_var = src_var;  
}

void cw::flow::var_disconnect( variable_t* in_var )
{
  if( in_var->src_var != nullptr )
  {
    // remote the in_var from the src var's output list
    variable_t* v0 = nullptr;
    variable_t* v = in_var->src_var->dst_head;
    for(; v!=nullptr; v=v->dst_link)
    {
      if( v == in_var )
      {
        if( v0 == nullptr )
          in_var->src_var->dst_head = v->dst_link;
        else
          v0->dst_link = v->dst_link;
        break;
      }

      v0 = v;
    }
    
    // the in_var is always in the src-var's output list
    assert(v == in_var );

    in_var->src_var = nullptr;
  }
}

unsigned  cw::flow::var_mult_count( proc_t* proc, const char* var_label )
{
  unsigned n = 0;
  for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link)
    if( textIsEqual(var->label,var_label) )
      ++n;
  
  return n;
}

cw::rc_t  cw::flow::var_mult_sfx_id_array( proc_t* proc, const char* var_label, unsigned* idA, unsigned idAllocN, unsigned& idN_ref )
{
  rc_t rc = kOkRC;

  idN_ref = 0;

  // for each variable whose 'label' is 'var_label'
  for(variable_t* var=proc->varL; var!=nullptr; var=var->var_link)
    if( textIsEqual(var->label,var_label) )
    {
      // scan idA[] for a matching sfx_id
      unsigned i=0;
      for(; i<idN_ref; ++i)
        if( idA[i] == var->label_sfx_id )
          break;

      // if the sfx_id of this var has not yet been included in idA[]
      if( i == idN_ref )
      {
        // ... and we still have space left in the output arrau
        if( idN_ref >= idAllocN )
        {
          rc = cwLogError(kBufTooSmallRC,"The mult-sfx-id result array is too small for the var:'%s'.",cwStringNullGuard(var_label));
          goto errLabel;
        }

        // store this sfx_id in idA[]
        idA[idN_ref++] = var->label_sfx_id;
      }
    }
  
errLabel:
  if( rc != kOkRC )
    idN_ref = 0;
  
  return rc;
}

cw::rc_t  cw::flow::var_send_to_ui( variable_t* var )
{

  // var->ui_var_link is set to null when the var is removed from the list
  
  // 1. Atomically set _head to the new node and return 'old-head'
  variable_t* prev   = var->proc->ctx->ui_var_head.exchange(var,std::memory_order_acq_rel);  

  // Note that at this point only the new node may have the 'old-head' as it's predecssor.
  // Other threads may therefore safely interrupt at this point.
      
  // 2. Set the old-head next pointer to the new node (thereby adding the new node to the list)
  prev->ui_var_link.store(var,std::memory_order_release); // RELEASE 'next' to consumer            

  return kOkRC;
}

cw::rc_t  cw::flow::var_send_to_ui( proc_t* proc, unsigned vid,  unsigned chIdx )
{
  rc_t rc = kOkRC;
  variable_t* var = nullptr;

  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = var_send_to_ui(var);
  
  return rc;
}

cw::rc_t  cw::flow::var_send_to_ui_enable( proc_t* proc, unsigned vid,  unsigned chIdx, bool enable_fl )
{
  rc_t rc = kOkRC;
  variable_t* var = nullptr;

  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
  {
    var->ui_var->new_disable_fl = !enable_fl;
    rc = var_send_to_ui(var);
  }
  return rc;
}

cw::rc_t  cw::flow::var_send_to_ui_show(   proc_t* proc, unsigned vid,  unsigned chIdx, bool show_fl )
{
  rc_t rc = kOkRC;
  variable_t* var = nullptr;

  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
  {
    var->ui_var->new_hide_fl = !show_fl;
    rc = var_send_to_ui(var);
  }
  return rc;
}


cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, variable_t*& varRef )
{
  return var_register( proc, var_label, sfx_id, vid, chIdx, nullptr, varRef );
}

cw::rc_t        cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned frameN )
{
  rc_t rc = kOkRC;
  abuf_t* abuf;
  
  if((abuf = abuf_create( srate, chN, frameN )) == nullptr )
    return cwLogError(kOpFailRC,"abuf create failed on proc:'%s:%i' variable:'%s:%i'.", proc->label, proc->label_sfx_id, var_label,sfx_id);

  if((rc = _var_register_and_set( proc, var_label, sfx_id, vid, chIdx, abuf )) != kOkRC )
    abuf_destroy(abuf);

  return rc;
}

cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, const unsigned* maxBinN_V, const unsigned* binN_V, const unsigned* hopSmpN_V, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV )
{
  rc_t rc = kOkRC;
  fbuf_t* fbuf;
  if((fbuf = fbuf_create( srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV )) == nullptr )
    return cwLogError(kOpFailRC,"fbuf create failed on proc:'%s:%i' variable:'%s:%i'.", proc->label, proc->label_sfx_id, var_label,sfx_id);

  if((rc = _var_register_and_set( proc, var_label, sfx_id, vid, chIdx, fbuf )) != kOkRC )
    fbuf_destroy(fbuf);

  return rc;
}

cw::rc_t        cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, midi::ch_msg_t* msgA, unsigned msgN  )
{
  rc_t rc = kOkRC;
  mbuf_t* mbuf;
  
  if((mbuf = mbuf_create(msgA,msgN)) == nullptr )
    return cwLogError(kOpFailRC,"mbuf create failed on proc:'%s:%i' variable:'%s:%i'.", proc->label, proc->label_sfx_id, var_label, sfx_id);

  if((rc = _var_register_and_set( proc, var_label, sfx_id, vid, chIdx, mbuf )) != kOkRC )
    mbuf_destroy(mbuf);

  return rc;
}


cw::rc_t cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, srate_t srate, unsigned chN, unsigned maxBinN, unsigned binN, unsigned hopSmpN, const fd_sample_t** magV, const fd_sample_t** phsV, const fd_sample_t** hzV )
{
  unsigned maxBinN_V[ chN ];
  unsigned binN_V[ chN ];
  unsigned hopSmpN_V[ chN ];
  vop::fill(maxBinN_V,chN,maxBinN);
  vop::fill(binN_V,chN,binN);
  vop::fill(hopSmpN_V,chN, hopSmpN );
  return var_register_and_set(proc,var_label,sfx_id,vid,chIdx,srate, chN, maxBinN_V, binN_V, hopSmpN_V, magV, phsV, hzV);
}

cw::rc_t   cw::flow::var_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const recd_type_t* recd_type, recd_t* recdA, unsigned recdN )
{
  rc_t rc = kOkRC;

  rbuf_t* rbuf;
  if((rbuf = rbuf_create(recd_type,recdA,recdN)) == nullptr )
    return cwLogError(kOpFailRC,"rbuf create failed on proc:'%s:%i' variable:'%s:%i'.", proc->label, proc->label_sfx_id, var_label, sfx_id);
    
  if((rc = _var_register_and_set( proc, var_label, sfx_id, vid, chIdx, rbuf )) != kOkRC )
    rbuf_destroy(rbuf);

  return rc;
}



cw::rc_t   cw::flow::var_alloc_register_and_set( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned vid, unsigned chIdx, const recd_type_t* base, recd_array_t*& recd_array_ref, unsigned recdN )
{
  rc_t rc = kOkRC;
  recd_array_t* recd_array = nullptr;
  recd_array_ref = nullptr;
  
  if((rc = var_alloc_record_array(proc,var_label,sfx_id,chIdx,base,recd_array,recdN)) != kOkRC )
    goto errLabel;
  
  if((rc = var_register_and_set(proc, var_label, sfx_id, vid, chIdx, recd_array->type, recd_array->recdA, recd_array->allocRecdN )) != kOkRC )
    goto errLabel;

  recd_array_ref = recd_array;
        
errLabel:
  if( rc != kOkRC )
  {
    rc = cwLogError(rc,"Record array create failed on the variable '%s:%i ch:%i.",cwStringNullGuard(var_label),sfx_id,chIdx);
    if( recd_array != nullptr )
      recd_array_destroy(recd_array);
  }     
  return rc;
  
}

cw::rc_t   cw::flow::var_alloc_record_array( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned chIdx, const recd_type_t* base, recd_array_t*& recd_array_ref, unsigned recdN )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;

  recd_array_ref = nullptr;
        
  // find the record variable
  if((rc = var_find( proc, var_label, sfx_id, chIdx, var )) != kOkRC )
  {
    rc = cwLogError(rc,"The record variable '%s:%i' could was not found.",cwStringNullGuard(var_label),sfx_id);
    goto errLabel; 
  }

  // verify that the variable has a record format
  if( !var_has_recd_format(var) )
  {
    rc = cwLogError(kInvalidArgRC,"The variable does not have a valid record format.");
    goto errLabel;
  }
  else
  {
    recd_fmt_t*   recd_fmt  = var->varDesc->fmt.recd_fmt;
    unsigned      alloc_cnt = recdN==0 ? recd_fmt->alloc_cnt : recdN;

    // verify that a non-zero length was given to the count of records in the array
    if( alloc_cnt == 0 )
    {
      rc = cwLogError(kInvalidArgRC,"A non-zero record array length has not been assigned to the the varaible '%s'.",cwStringNullGuard(var_label));
      goto errLabel;
    }

    // create the recd_array
    if((rc = recd_array_create( recd_array_ref, recd_fmt->recd_type, base, alloc_cnt )) != kOkRC )
    {
      goto errLabel;
    }
  }

errLabel:
  
  if( rc != kOkRC )
    rc = cwLogError(rc,"Record array alloc, based on the variable '%s:%i ch:%i, failed.",cwStringNullGuard(var_label),sfx_id,chIdx);

  return rc;
}

cw::rc_t  cw::flow::var_get( const variable_t* var, bool& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, uint_t& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, int_t& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, float& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, double& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, const char*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, const abuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( variable_t* var, abuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, const fbuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( variable_t* var, fbuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, const mbuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( variable_t* var, mbuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, const rbuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( variable_t* var, rbuf_t*& valRef )
{ return _val_get_driver(var,valRef); }

cw::rc_t  cw::flow::var_get( const variable_t* var, const object_t*& valRef )
{ return _val_get_driver(var,valRef); }


cw::rc_t cw::flow::var_set_from_cfg( variable_t* var, const object_t* cfg_value )
{
  rc_t rc = kOkRC;
  value_t v;

  if((rc = value_from_cfg(cfg_value, v)) != kOkRC )
    goto errLabel;

  if((rc = var_set(var,&v)) != kOkRC )
    goto errLabel;
  
errLabel:
  if( rc != kOkRC )
    rc = cwLogError(kSyntaxErrorRC,"The %s:%i.%s:%i could not extract a type:%s from a configuration value.",var->proc->label,var->proc->label_sfx_id,var->label,var->label_sfx_id,value_type_flag_to_label(var->varDesc->type & kTypeMask));
      
  return rc;
      
}

cw::rc_t cw::flow::var_set( variable_t* var, const value_t* val )
{
  rc_t rc = kOkRC;
  
  switch( val->tflag )
  {
    case kBoolTFl:   rc = _var_set_driver(var,val->tflag,val->u.b); break;
    case kUIntTFl:   rc = _var_set_driver(var,val->tflag,val->u.u); break;
    case kIntTFl:    rc = _var_set_driver(var,val->tflag,val->u.i); break;
    case kFloatTFl:  rc = _var_set_driver(var,val->tflag,val->u.f); break;
    case kDoubleTFl: rc = _var_set_driver(var,val->tflag,val->u.d); break;
    case kStringTFl: rc = _var_set_driver(var,val->tflag,val->u.s); break;
    case kCfgTFl:    rc = _var_set_driver(var,val->tflag,val->u.cfg); break;
    case kABufTFl:   rc = _var_set_driver(var,val->tflag,val->u.abuf); break;
    case kFBufTFl:   rc = _var_set_driver(var,val->tflag,val->u.fbuf); break;
    case kMBufTFl:   rc = _var_set_driver(var,val->tflag,val->u.mbuf); break;
    case kRBufTFl:   rc = _var_set_driver(var,val->tflag,val->u.rbuf); break;
    default:
      rc = cwLogError(kNotImplementedRC,"The var_set() from value_t has not been implemented for the type 0x%x.",val->tflag);
  }

  return rc;
}


cw::rc_t cw::flow::var_set( variable_t* var, bool val )            { return _var_set_driver(var,kBoolTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, uint_t val )          { return _var_set_driver(var,kUIntTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, int_t val )           { return _var_set_driver(var,kIntTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, float val )           { return _var_set_driver(var,kFloatTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, double val )          { return _var_set_driver(var,kDoubleTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, const char* val )     { return _var_set_driver(var,kStringTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, abuf_t* val )         { return _var_set_driver(var,kABufTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, fbuf_t* val )         { return _var_set_driver(var,kFBufTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, mbuf_t* val )         { return _var_set_driver(var,kMBufTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, rbuf_t* val )         { return _var_set_driver(var,kRBufTFl,val); }
cw::rc_t cw::flow::var_set( variable_t* var, const object_t* val ) { return _var_set_driver(var,kCfgTFl,val); }

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, const value_t* val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = var_set(var,val);
  
  return rc;
}    

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, bool val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver( var, kBoolTFl, val );
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, uint_t val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver( var, kUIntTFl, val );
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, int_t val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver( var, kIntTFl, val );
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, float val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver( var, kFloatTFl, val );
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, double val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver( var, kDoubleTFl, val );
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, const char* val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver( var, kStringTFl, val );
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, abuf_t* val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver(var,kABufTFl,val);
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, fbuf_t* val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver(var,kFBufTFl,val);
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, mbuf_t* val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver(var,kMBufTFl,val);
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, rbuf_t* val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver(var,kRBufTFl,val);
  
  return rc;    
}

cw::rc_t cw::flow::var_set( proc_t* proc, unsigned vid, unsigned chIdx, const object_t* val )
{
  rc_t        rc  = kOkRC;
  variable_t* var = nullptr;
  
  if((rc = var_find(proc, vid, chIdx, var )) == kOkRC )
    rc = _var_set_driver( var, kCfgTFl, val );
  
  return rc;    
}