//| 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 <type_traits>

#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwFile.h"
#include "cwTest.h"
#include "cwLex.h"
#include "cwText.h"
#include "cwNumericConvert.h"
#include "cwObject.h"
#include "cwObjectTemplate.h"


namespace cw
{
  enum
  {
   kLCurlyLexTId = cw::lex::kUserLexTId+1, 
   kRCurlyLexTId,
   kLHardLexTId,
   kRHardLexTId,
   kColonLexTId,
   kCommaLexTId,
   kTrueLexTId,
   kFalseLexTId,
   kNullLexTId,
   kSegmentedIdLexTId,  // id with embedded periods
  };

  idLabelPair_t _objTokenArray[] =
  {
   { kLCurlyLexTId, "{" },
   { kRCurlyLexTId, "}" },
   { kLHardLexTId,  "[" },
   { kRHardLexTId,  "]" },
   { kColonLexTId,  ":" },
   { kCommaLexTId,  "," },
   { kTrueLexTId,   "true"},
   { kFalseLexTId,  "false"},
   { kNullLexTId,   "null" },
   { lex::kErrorLexTId,""}  
  };

  unsigned _udiff( unsigned n, unsigned i )
  {
    return i>=n ? 0 : n-i;
  }

  unsigned _lexSegmentedIdMatcher( const char* cp, unsigned cn )
  {
      unsigned i = 0;
      if( isalpha(cp[0]) || (cp[0]== '_'))
      {
        i = 1;
        for(; i<cn; ++i)
          if( !isalnum(cp[i]) && (cp[i] != '_') && (cp[i] != '.') )
            break;
      }
      return i;    
  }

  
  void _objTypeFree( object_t* o )
  {
    o->type->free_value(o);
    mem::release(o);
  }
  
  
  void _objTypeFreeValue( object_t* o )
  {}

  void _objTypeFreeValueString( object_t* o )
  {
    mem::release( o->u.str );
  }


  
  const char* _objTypeIdToLabel( objTypeId_t tid );

  
  rc_t _objTypeValueFromChar(     const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.c,tid,  dst,o->type->label); }
  rc_t _objTypeValueFromInt8(     const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i8,tid, dst,o->type->label); }
  rc_t _objTypeValueFromUInt8(    const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u8,tid, dst,o->type->label); }
  rc_t _objTypeValueFromInt16(    const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i16,tid,dst,o->type->label); }
  rc_t _objTypeValueFromUInt16(   const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u16,tid,dst,o->type->label); }
  rc_t _objTypeValueFromInt32(    const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i32,tid,dst,o->type->label); }
  rc_t _objTypeValueFromUInt32(   const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u32,tid,dst,o->type->label); }
  rc_t _objTypeValueFromInt64(    const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i64,tid,dst,o->type->label); }
  rc_t _objTypeValueFromUInt64(   const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u64,tid,dst,o->type->label); }
  rc_t _objTypeValueFromFloat(    const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.f,tid,  dst,o->type->label); }
  rc_t _objTypeValueFromDouble(   const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.d,tid,  dst,o->type->label); }
  rc_t _objTypeValueFromBool(     const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.b,tid,dst,o->type->label); }

  rc_t _objTypeValueFromNonValue( const object_t* o, unsigned tid, void* dst )
  {
    switch(tid)
    {
      case kCStringTId:
        *(const char**)dst = nullptr;
        return kOkRC;
        
      case kStringTId:
        *(char**)dst = nullptr;
        return kOkRC;
    }
    
    return cwLogError(kInvalidArgRC, "There is no conversion from '%s' to '%s'.", _objTypeIdToLabel(tid), o->type->label);
  }

  rc_t _objTypeValueFromCString(   const object_t* o, unsigned tid, void* dst )
  {
    if( tid == kCStringTId )
    {
      *(const char**)dst = o->u.str;
      return kOkRC;
    }

    return _objTypeValueFromNonValue(o,tid,dst);
  }
  
  rc_t _objTypeValueFromString(   const object_t* o, unsigned tid, void* dst )
  {
    // When objects are parsed all strings are non-const. therefore when a string is retrieved
    // from an object the string will always be non-const - but the type of the variable
    // to receive the value may be const - we detect this here and redirect to the 'const char*'
    // version of the function.
    if( tid == kCStringTId )
      return _objTypeValueFromCString(o,tid,dst);
    
    if( tid == kStringTId )
    {
      *(char**)dst = o->u.str;
      return kOkRC;
    }

    if( tid == kNullTId )
    {
      *(char**)dst = nullptr;
      return kOkRC;
    }
    
    return _objTypeValueFromNonValue(o,tid,dst);
  }

  
  rc_t _objTypeValueFromVect(     const object_t* o, unsigned tid, void* dst )
  { return _objTypeValueFromNonValue(o,tid,dst); }

  void _objTypePrintIndent( const char* text, unsigned indent, const char* indentStr=" " )
  {
    for(unsigned i=0; i<indent; ++i)
      cwLogPrint("%s",indentStr);
    cwLogPrint("%s",text); 
  }

  void _objTypePrintChild( const object_t* o, print_ctx_t& c, const char* eolStr=",\n", const char* indentStr=" " )
  {
    _objTypePrintIndent(" ",c.indent,indentStr);
    o->type->print(o,c);
    cwLogPrint("%s",eolStr);
  }
  
  void _objTypePrintNull(   const object_t* o, print_ctx_t& c ) { cwLogPrint("NULL "); }
  void _objTypePrintError(  const object_t* o, print_ctx_t& c ) { cwLogPrint("Error "); }
  void _objTypePrintChar(   const object_t* o, print_ctx_t& c ) { cwLogPrint("%c",o->u.c); }
  void _objTypePrintInt8(   const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i8); }
  void _objTypePrintUInt8(  const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u8); }
  void _objTypePrintInt16(  const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i16); }
  void _objTypePrintUInt16( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u16); }
  void _objTypePrintInt32(  const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i32); }
  void _objTypePrintUInt32( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u32); }
  void _objTypePrintInt64(  const object_t* o, print_ctx_t& c ) { cwLogPrint("%lli", o->u.i64); }
  void _objTypePrintUInt64( const object_t* o, print_ctx_t& c ) { cwLogPrint("%lli", o->u.u64); }
  void _objTypePrintBool(   const object_t* o, print_ctx_t& c ) { cwLogPrint("%s",o->u.b ? "true" : "false"); }
  void _objTypePrintFloat(  const object_t* o, print_ctx_t& c ) { cwLogPrint("%f",o->u.f); }
  void _objTypePrintDouble( const object_t* o, print_ctx_t& c ) { cwLogPrint("%f",o->u.d); }
  void _objTypePrintString( const object_t* o, print_ctx_t& c ) { cwLogPrint("%s",o->u.str); }
  void _objTypePrintVect(   const object_t* o, print_ctx_t& c ) { cwLogPrint("<vect>"); }
  void _objTypePrintPair(   const object_t* o, print_ctx_t& c )
  {
    o->u.children->type->print(o->u.children,c);
    cwLogPrint(": ");
    o->u.children->sibling->type->print(o->u.children->sibling,c);    
  }
  
  void _objTypePrintList( const object_t* o, print_ctx_t& c )
  {
    const char* indentStr  = c.listOnOneLineFl ? ""    : " ";

    char bracketStr[] = { '[','\0','\0' };
    char eoValStr[]   = { ',','\0','\0' };

    if(!c.listOnOneLineFl)
    {
      bracketStr[1] = '\n';
      eoValStr[1] = '\n';
    }
      
    
    _objTypePrintIndent(bracketStr,0);
    c.indent += 2;
    
    for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
    {
      if( ch->sibling == nullptr )
        eoValStr[0] = ' ';
      
      _objTypePrintChild(ch,c,eoValStr,indentStr);
    }
    
    c.indent -= 2;
    _objTypePrintIndent("]",c.listOnOneLineFl ? 0 : c.indent);
  }
  
  void _objTypePrintDict( const object_t* o, print_ctx_t& c )
  {
    _objTypePrintIndent("{\n",0);
    c.indent += 2;
    
    for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
      _objTypePrintChild(ch,c);

    c.indent -= 2;
    _objTypePrintIndent("}",c.indent);
  }
  
  void _objTypePrintRoot( const object_t* o, print_ctx_t& c )
  {
    _objTypePrintDict(o,c);
  }

  
  unsigned _objTypeToStringNull(   const object_t* o, char* buf, unsigned n ) { return snprintf(buf,n,"NULL "); }
  unsigned _objTypeToStringError(  const object_t* o, char* buf, unsigned n ) { return snprintf(buf,n,"Error "); }
  unsigned _objTypeToStringChar(   const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.c); }
  unsigned _objTypeToStringInt8(   const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i8); }
  unsigned _objTypeToStringUInt8(  const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u8); }
  unsigned _objTypeToStringInt16(  const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i16); }
  unsigned _objTypeToStringUInt16( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u16); }
  unsigned _objTypeToStringInt32(  const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i32); }
  unsigned _objTypeToStringUInt32( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u32); }
  unsigned _objTypeToStringInt64(  const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i64); }
  unsigned _objTypeToStringUInt64( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u64); }
  unsigned _objTypeToStringBool(   const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.b); }
  unsigned _objTypeToStringFloat(  const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.f); }
  unsigned _objTypeToStringDouble( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.d); }
  unsigned _objTypeToStringVect(   const object_t* o, char* buf, unsigned n ) { return snprintf(buf,n,"<vect>"); }

  unsigned _objTypeToStringString( const object_t* o, char* buf, unsigned n )
  {
    unsigned i = snprintf(buf,n,"\"");
    const char* str = o->u.str;

    if( str == nullptr )
    {
      cwLogWarning("Unexpected empty string while generating text output.");
    }
    
    i += toText(buf+i,_udiff(n,i),str);
    
    return i + snprintf(buf+i,_udiff(n,i),"\"");
  }
  
  unsigned _objTypeToStringPair(   const object_t* o, char* buf, unsigned n )
  {
    unsigned i = o->u.children->type->to_string(o->u.children,buf,n);
    i += snprintf(buf+i,_udiff(n,i)," : ");
    return i + o->u.children->sibling->type->to_string(o->u.children->sibling,buf+i,_udiff(n,i));    
  }
  
  unsigned _objTypeToStringList( const object_t* o, char* buf, unsigned n )
  {
    unsigned i = snprintf(buf,n," [ ");
    
    for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
    {

      i += ch->type->to_string(ch,buf+i,_udiff(n,i));

      if( ch->sibling != nullptr )
        i += snprintf(buf+i,_udiff(n,i),", ");
      
    }

    i += snprintf(buf+i,_udiff(n,i)," ] ");

    return i;
  }
  
  unsigned _objTypeToStringDict( const object_t* o, char* buf, unsigned n )
  {

    unsigned i = snprintf(buf,n," { " );
    
    for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
    {      
      i += ch->type->to_string(ch,buf+i,_udiff(n,i));
      
      if( ch->sibling != nullptr )
        i += snprintf(buf+i,_udiff(n,i),", ");
    }
    
    i += snprintf(buf+i,_udiff(n,i)," } ");

    return i;
  }
  
  unsigned _objTypeToStringRoot( const object_t* o, char* buf, unsigned n )
  {
    return _objTypeToStringDict(o,buf,n);
  }



  object_t* _objTypeDuplContainer( const struct object_str* src, struct object_str* parent )
  {
    object_t* o = _objAppendRightMostNode( parent, _objAllocate( src->type->id, parent ));
    for(object_t* ch=src->u.children; ch!=nullptr; ch=ch->sibling)
      ch->type->duplicate(ch,o);

    return o;
  }
  
  object_t* _objTypeDuplNull(   const struct object_str* src, struct object_str* parent ) { return _objAllocate( src->type->id, parent); }
  object_t* _objTypeDuplError(  const struct object_str* src, struct object_str* parent ) { return _objAllocate( src->type->id, parent); }
  object_t* _objTypeDuplChar(   const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<char>(parent,src->u.c); }
  object_t* _objTypeDuplInt8(   const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int8_t>(parent,src->u.i8); }
  object_t* _objTypeDuplUInt8(  const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<uint8_t>(parent,src->u.u8); }
  object_t* _objTypeDuplInt16(  const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int16_t>(parent,src->u.i16); }
  object_t* _objTypeDuplUInt16( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int16_t>(parent,src->u.u16); }
  object_t* _objTypeDuplInt32(  const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int32_t>(parent,src->u.i32); }
  object_t* _objTypeDuplUInt32( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int32_t>(parent,src->u.u32); }
  object_t* _objTypeDuplInt64(  const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int64_t>(parent,src->u.i64); }
  object_t* _objTypeDuplUInt64( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<uint64_t>(parent,src->u.u64); }
  object_t* _objTypeDuplBool(   const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<bool   >(parent,src->u.b  ); }
  object_t* _objTypeDuplFloat(  const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<float  >(parent,src->u.f  ); }
  object_t* _objTypeDuplDouble( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<double >(parent,src->u.d  ); }
  object_t* _objTypeDuplString( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<char*  >(parent,src->u.str); }
  object_t* _objTypeDuplCString(const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<const char*>(parent,src->u.str);}    
  object_t* _objTypeDuplVect(   const struct object_str* src, struct object_str* parent ) { assert(0); return nullptr; }
  object_t* _objTypeDuplPair(   const struct object_str* src, struct object_str* parent ) {  return _objTypeDuplContainer(src,parent); }
  object_t* _objTypeDuplList(   const struct object_str* src, struct object_str* parent ) {  return _objTypeDuplContainer(src,parent); }
  object_t* _objTypeDuplDict(   const struct object_str* src, struct object_str* parent ) {  return _objTypeDuplContainer(src,parent); }
  object_t* _objTypeDuplRoot(   const struct object_str* src, struct object_str* parent ) {  return _objTypeDuplContainer(src,parent); }
  
  
  objType_t _objTypeArray[] =
  {
   { kNullTId,    "null",      0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromNonValue, _objTypePrintNull,   _objTypeToStringNull,   _objTypeDuplNull },
   { kErrorTId,   "error",     0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromNonValue, _objTypePrintError,  _objTypeToStringError,  _objTypeDuplError },
   { kCharTId,    "char",      0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromChar,     _objTypePrintChar,   _objTypeToStringChar,   _objTypeDuplChar },
   { kInt8TId,    "int8",      0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromInt8,     _objTypePrintInt8,   _objTypeToStringInt8,   _objTypeDuplInt8 },
   { kUInt8TId,   "uint8",     0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromUInt8,    _objTypePrintUInt8,  _objTypeToStringUInt8,  _objTypeDuplUInt8 },
   { kInt16TId,   "int16",     0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromInt16,    _objTypePrintInt16,  _objTypeToStringInt16,  _objTypeDuplInt16 },
   { kUInt16TId,  "uint16",    0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromUInt16,   _objTypePrintUInt16, _objTypeToStringUInt16, _objTypeDuplUInt16 },
   { kInt32TId,   "int32",     0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromInt32,    _objTypePrintInt32,  _objTypeToStringInt32,  _objTypeDuplInt32 },
   { kUInt32TId,  "uint32",    0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromUInt32,   _objTypePrintUInt32, _objTypeToStringUInt32, _objTypeDuplUInt32 },
   { kInt64TId,   "int64",     0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromInt64,    _objTypePrintInt64,  _objTypeToStringInt64,  _objTypeDuplInt64 },
   { kUInt64TId,  "uint64",    0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromUInt64,   _objTypePrintUInt64, _objTypeToStringUInt64, _objTypeDuplUInt64 },
   { kBoolTId,    "bool",      0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromBool,     _objTypePrintBool,   _objTypeToStringBool,   _objTypeDuplBool },
   { kFloatTId,   "float",     0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromFloat,    _objTypePrintFloat,  _objTypeToStringFloat,  _objTypeDuplFloat },
   { kDoubleTId,  "double",    0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromDouble,   _objTypePrintDouble, _objTypeToStringDouble, _objTypeDuplDouble },
   { kStringTId,  "string",    0,                                _objTypeFree,  _objTypeFreeValueString, _objTypeValueFromString,   _objTypePrintString, _objTypeToStringString, _objTypeDuplString },
   { kCStringTId, "cstring",   0,                                _objTypeFree,  _objTypeFreeValueString, _objTypeValueFromCString,  _objTypePrintString, _objTypeToStringString, _objTypeDuplCString },
   { kVectTId,    "vect",      0,                                _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromVect,     _objTypePrintVect,   _objTypeToStringVect,   _objTypeDuplVect },
   { kPairTId,    "pair",      kContainerFl | kValueContainerFl, _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromNonValue, _objTypePrintPair,   _objTypeToStringPair,   _objTypeDuplPair },
   { kListTId,    "list",      kContainerFl | kValueContainerFl, _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromNonValue, _objTypePrintList,   _objTypeToStringList,   _objTypeDuplList },
   { kDictTId,    "dict",      kContainerFl,                     _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromNonValue, _objTypePrintDict,   _objTypeToStringDict,   _objTypeDuplDict },
   { kRootTId,    "root",      kContainerFl | kValueContainerFl, _objTypeFree,  _objTypeFreeValue,       _objTypeValueFromNonValue, _objTypePrintRoot,   _objTypeToStringRoot,   _objTypeDuplRoot },
   { kInvalidTId, "<invalid>", 0,                                nullptr,       nullptr,                 nullptr,                   nullptr,             nullptr,              nullptr }   
  };



  objType_t* _objIdToType( objTypeId_t tid )
  {
    unsigned i;
    for(i=0; _objTypeArray[i].id != kInvalidTId; ++i)
      if( _objTypeArray[i].id == tid )
        return _objTypeArray + i;

    cwLogError(kInvalidIdRC,"The object type id %i is not valid.",tid);
    return nullptr;
  }

  const char* _objTypeIdToLabel( objTypeId_t tid )
  {
    const objType_t* type;
    if((type = _objIdToType(tid)) == nullptr )
      return "<invalid>";
    
    return type->label;
  }

  
  object_t* _objAllocate( objTypeId_t tid, object_t* parent )
  {
    objType_t* type = nullptr;
    if( tid != kInvalidTId )
    {
      if((type = _objIdToType(tid)) == nullptr )
      {
        cwLogError(kObjAllocFailRC,"Object allocation failed.");
        return nullptr;
      }
    }
    
    object_t* o =  mem::allocZ<object_t>();
    o->type   = type;
    o->parent = parent;
    
    return o;
  }

  rc_t _objSyntaxError( lex::handle_t lexH, const char* fmt, ... )
  {
    va_list vl;
    va_start(vl,fmt);

    cwLogVError( kSyntaxErrorRC, fmt, vl );
    cwLogError(  kSyntaxErrorRC, "Error on line: %i.", lex::currentLineNumber(lexH));
    va_end(vl);
    return kSyntaxErrorRC;
  }
 
  
  rc_t _objVerifyParentIsValueContainer( lex::handle_t lexH, const object_t* parent, const char* msg )
  {
    if( parent == nullptr )
      return _objSyntaxError(lexH,"The parent node must always be valid.");
    
    // it is legal for a parent of a value to be null when the value is the root element.
    if( !(parent->is_value_container()))
      return _objSyntaxError(lexH,"Value nodes of type '%s' must be contained by 'root', 'pair' or 'array' node.",msg);
    
    return kOkRC;
  }
  
  object_t* _objAppendRightMostNode( object_t* parent, object_t* newNode )
  {
    if( newNode == nullptr )
      return nullptr;

    if( parent != nullptr )
    {
      assert( parent->is_container() );
      
      object_t* child = parent->u.children;

      if( parent->u.children == nullptr )
        parent->u.children = newNode;
      else
      {
        while( child->sibling != nullptr )
          child = child->sibling;

        child->sibling = newNode;
      }
    }
      
    newNode->parent = parent;
    return newNode;
  }

  object_t* _objCreateConainerNode( lex::handle_t lexH, object_t* parent, objTypeId_t tid )
  {
    if( _objVerifyParentIsValueContainer(lexH,parent,_objTypeIdToLabel(tid)) == kOkRC )
      return _objAppendRightMostNode( parent, _objAllocate( tid, parent ));
    
    return nullptr;            
  }

  template< typename T >
  rc_t _objSetValue( object_t* obj, const T& v )
  {
    cw::rc_t rc = kOkRC;
    if(_objCallSetLeafValue( obj, v ) == nullptr)
      rc = cwLogError(kOpFailRC,"Object value assignment failed.");
    return rc;
  }
  
}


void cw::object_t::unlink()
{
  // if this node has no parent then there it is not part of a tree
  // and therefore cannot be unlinked
  if( parent == nullptr )
    return;
  
  object_t* c0 = nullptr;
  object_t* c = parent->u.children;
  for(; c!=nullptr; c=c->sibling)
  {
    if( c == this )
    {
      if( c0 == nullptr )
        parent->u.children = c->sibling;
      else
        c0->sibling = c->sibling;

      c->parent  = nullptr;
      c->sibling = nullptr;
      return;
    }
    c0 = c;
  }

  // if a child has a parent then it must be in that parent's child list
  cwAssert(0);  
}

void cw::object_t::free()
{
  unlink();
  
  if( is_container() )
  {
    object_t* o1 = nullptr;
    for(object_t* o = u.children; o != nullptr; o=o1 )
    {
      o1 = o->sibling;
      o->free();
    }
  }
  
  type->free(this);
}

cw::rc_t cw::object_t::append_child( struct object_str* child )
{
  if( !is_container() )
    return cwLogError(kInvalidDataTypeRC,"The parent of a child object node must be a 'container'.");
  
  _objAppendRightMostNode( this, child );
  return kOkRC;
}


unsigned cw::object_t::child_count() const
{
  unsigned n = 0;
  if( is_container() && u.children != nullptr)
  {
    object_t* o = u.children;
    
    for(n=1; o->sibling != nullptr; o=o->sibling)
      ++n;
  }
  return n;
}

cw::rc_t cw::object_t::value( void* dst, unsigned dstTypeId ) { return type->value(this,dstTypeId,dst); }
cw::rc_t cw::object_t::value( char& v )     const { return type->value(this,kCharTId,&v); }
cw::rc_t cw::object_t::value( int8_t&  v )  const { return type->value(this,kInt8TId,&v); }
cw::rc_t cw::object_t::value( uint8_t& v )  const { return type->value(this,kUInt8TId,&v); }
cw::rc_t cw::object_t::value( int16_t&  v ) const { return type->value(this,kInt16TId,&v); }
cw::rc_t cw::object_t::value( uint16_t& v ) const { return type->value(this,kUInt16TId,&v); }
cw::rc_t cw::object_t::value( int32_t&  v ) const { return type->value(this,kInt32TId,&v); }
cw::rc_t cw::object_t::value( uint32_t& v ) const { return type->value(this,kUInt32TId,&v); }
cw::rc_t cw::object_t::value( int64_t&  v ) const { return type->value(this,kInt64TId,&v); }
cw::rc_t cw::object_t::value( uint64_t& v ) const { return type->value(this,kUInt64TId,&v); }
cw::rc_t cw::object_t::value( float&  v )   const { return type->value(this,kFloatTId,&v); }
cw::rc_t cw::object_t::value( double& v )   const { return type->value(this,kDoubleTId,&v); }
cw::rc_t cw::object_t::value( bool& v )     const { return type->value(this,kBoolTId,&v); }
cw::rc_t cw::object_t::value( char*& v )    const { return type->value(this,kStringTId,&v); }
cw::rc_t cw::object_t::value( const char*& v ) const { return type->value(this,kCStringTId,&v); }



cw::rc_t cw::object_t::set_value( char v )     { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int8_t  v )  { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint8_t v )  { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int16_t  v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint16_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int32_t  v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint32_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int64_t  v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint64_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( float  v )   { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( double v )   { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( bool v )     { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( char* v )    { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( const char* v ) { return _objSetValue(this,v); }

  

const char* cw::object_t::pair_label() const
{
  cwAssert( is_pair() );
  if( is_pair() )
    return u.children->u.str;
  return nullptr;
}

const struct cw::object_str* cw::object_t::pair_value() const
{
  cwAssert( is_pair() );
  if( is_pair() )
    return u.children->sibling;
  return nullptr;
}

struct cw::object_str* cw::object_t::pair_value() 
{
  cwAssert( is_pair() );
  if( is_pair() )
    return u.children->sibling;
  return nullptr;
}


const struct cw::object_str* cw::object_t::find( const char* label, unsigned flags ) const
{
  if( is_container() )
  {
    for(object_t* o=u.children; o!=nullptr; o=o->sibling)
    {
      if( o->is_pair() && textCompare(o->pair_label(),label) == 0 )
        return o->pair_value();

      const object_t* ch; 
      if( cwIsFlag(flags,kRecurseFl) )
        if((ch = o->find(label)) != nullptr )
          return ch;
    }     
  }  
  return nullptr;
}

struct cw::object_str* cw::object_t::find( const char* label, unsigned flags ) 
{
  return const_cast<struct object_str*>(((const object_t*)this)->find(label,flags));
}

const struct cw::object_str* cw::object_t::child_ele( unsigned idx ) const
{
  if( is_container() )
  {
    unsigned i = 0;
    for(object_t* o=u.children; o!=nullptr; o=o->sibling,++i)
      if( i == idx )
        return o;            
  }
  return nullptr;
}

struct cw::object_str* cw::object_t::child_ele( unsigned idx )
{
  return const_cast<struct object_str*>(((const object_t*)this)->child_ele(idx));
}

const struct cw::object_str* cw::object_t::next_child_ele( const struct object_str* ele ) const
{
  if( is_container() )
  {
    if( ele == nullptr )
      return u.children;

    return ele->sibling;
  }
  return nullptr;
}

struct cw::object_str* cw::object_t::next_child_ele( struct object_str* ele  )
{
  return const_cast<struct object_str*>(((const object_t*)this)->next_child_ele(ele));
}

unsigned cw::object_t::to_string( char* buf, unsigned bufByteN ) const
{
  return type->to_string(this,buf,bufByteN );
}

char* cw::object_t::to_string() const
{
  unsigned n = 1024;
  char* buf = mem::alloc<char>(n);
  do
  {
    unsigned actual_bytes = to_string(buf,n);
    if( actual_bytes < n )
      break;

    n *= 2;
    buf = mem::resize(buf,n);
    
  }while(1);

  return buf;
}


void cw::object_t::print(const print_ctx_t* c) const
{
  print_ctx_t ctx;
  if( c != nullptr )
    ctx = *c;
  type->print(this,ctx); 
}

cw::object_t* cw::object_t::duplicate() const
{ return type->duplicate(this,nullptr);  }

cw::object_t* cw::newObject( std::uint8_t v, object_t* parent)
{ return _objCreateValueNode<uint8_t>( parent, v ); }

cw::object_t* cw::newObject( std::int8_t v, object_t* parent)
{ return _objCreateValueNode<int8_t>( parent, v ); }

cw::object_t* cw::newObject( std::uint16_t v, object_t* parent)
{ return _objCreateValueNode<uint16_t>( parent, v ); }

cw::object_t* cw::newObject( std::int16_t v, object_t* parent)
{ return _objCreateValueNode<int16_t>( parent, v ); }

cw::object_t* cw::newObject( std::uint32_t v, object_t* parent)
{ return _objCreateValueNode<uint32_t>( parent, v ); }

cw::object_t* cw::newObject( std::int32_t v, object_t* parent)
{ return _objCreateValueNode<int32_t>( parent, v ); }

cw::object_t* cw::newObject( std::uint64_t v, object_t* parent)
{ return _objCreateValueNode<uint64_t>( parent, v ); }

cw::object_t* cw::newObject( std::int64_t v, object_t* parent)
{ return _objCreateValueNode<uint64_t>( parent, v ); }

cw::object_t* cw::newObject( bool v, object_t* parent)
{ return _objCreateValueNode<bool>( parent, v ); }

cw::object_t* cw::newObject( float v, object_t* parent)
{ return _objCreateValueNode<float>( parent, v ); }

cw::object_t* cw::newObject( double v, object_t* parent)
{ return _objCreateValueNode<double>( parent, v ); }
 
cw::object_t* cw::newObject( char* v, object_t* parent)
{ return _objCreateValueNode<const char*>( parent, v ); }

cw::object_t* cw::newObject( const char* v, object_t* parent)
{ return _objCreateValueNode<const char*>( parent, v ); }

cw::object_t* cw::newDictObject( object_t* parent )
{ return _objAppendRightMostNode(parent,  _objAllocate( kDictTId, parent) ); }
    
cw::object_t* cw::newListObject( object_t* parent )
{ return _objAppendRightMostNode(parent,  _objAllocate( kListTId, parent) ); }

cw::object_t* cw::newPairObject( const char* label, object_t* value, object_t* parent)
{
  object_t* pair = _objAppendRightMostNode(parent, _objAllocate( kPairTId, parent) );

  _objCreateValueNode<const char*>( pair, label );

  pair->append_child(value);

  return value;
}

cw::object_t* cw::newPairObject( const char* label, std::uint8_t v, object_t* parent)
{ return _objCreatePairNode<uint8_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, std::int8_t v, object_t* parent)
{ return _objCreatePairNode<int8_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, std::uint16_t v, object_t* parent)
{ return _objCreatePairNode<uint16_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, std::int16_t v, object_t* parent)
{ return _objCreatePairNode<int16_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, std::uint32_t v, object_t* parent)
{ return _objCreatePairNode<uint32_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, std::int32_t v, object_t* parent)
{ return _objCreatePairNode<int32_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, std::uint64_t v, object_t* parent)
{ return _objCreatePairNode<uint64_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, std::int64_t v, object_t* parent)
{ return _objCreatePairNode<uint64_t>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, bool v, object_t* parent)
{ return _objCreatePairNode<bool>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, float v, object_t* parent)
{ return _objCreatePairNode<float>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, double v, object_t* parent)
{ return _objCreatePairNode<double>( parent, label, v ); }
 
cw::object_t* cw::newPairObject( const char* label, char* v, object_t* parent)
{ return _objCreatePairNode<const char*>( parent, label, v ); }

cw::object_t* cw::newPairObject( const char* label, const char* v, object_t* parent)
{ return _objCreatePairNode<const char*>( parent, label, v ); }


cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
{
  lex::handle_t lexH;
  rc_t          rc;
  unsigned      lexFlags = 0;
  unsigned      lexId    = lex::kErrorLexTId;
  object_t*     cnp      = _objAllocate(kRootTId,nullptr);
  object_t*     root     = cnp;
  objRef = nullptr;

  if((rc = lex::create(lexH,s,textLength(s), lexFlags )) != kOkRC )
    return rc;

  // setup the lexer with additional tokens
  for(unsigned i=0; _objTokenArray[i].id != lex::kErrorLexTId; ++i)
    if((rc = lex::registerToken( lexH, _objTokenArray[i].id, _objTokenArray[i].label )) != kOkRC )
    {
      rc = cwLogError(rc,"Object lexer token registration failed on token id:%i : '%s'",_objTokenArray[i].id, _objTokenArray[i].label);
      goto errLabel;
    }

  // register the segmented token matcher
  if((rc = lex::registerMatcher( lexH, kSegmentedIdLexTId, _lexSegmentedIdMatcher)) != kOkRC )
  {
      rc = cwLogError(rc,"Object lexer token matcher registration failed");
      goto errLabel;
  }

  // main parser loop
  while((lexId = lex::getNextToken(lexH)) != lex::kErrorLexTId && (lexId != lex::kEofLexTId) && (rc == kOkRC))
  {
    
    switch( lexId )
    {
      case kLCurlyLexTId:
        cnp = _objCreateConainerNode( lexH, cnp, kDictTId );
        break;

      case kRCurlyLexTId:
        if( cnp == nullptr )
          _objSyntaxError(lexH,"An end of 'object' was encountered without an associated 'object' start.");
        else
          cnp = cnp->parent;
        break;

      case kLHardLexTId:        
        cnp = _objCreateConainerNode( lexH, cnp, kListTId );
        break;

      case kRHardLexTId:
        if( cnp == nullptr )
          rc = _objSyntaxError(lexH,"An end of 'array' was encountered without an associated 'array' start.");
        else
          cnp = cnp->parent;
        break;

      case kColonLexTId:
        if( cnp == nullptr || !cnp->is_pair() )
          rc = _objSyntaxError(lexH,"A colon was encountered outside a 'pair' node.");
        break;

      case kCommaLexTId:
        if( cnp == nullptr || (!cnp->is_list() && !cnp->is_dict()) )
          rc = _objSyntaxError(lexH,"Unexpected comma outside of 'array' or 'object'.");
        break;
        
      case lex::kRealLexTId:
        if( tokenIsSinglePrecision(lexH) )
          _objCreateValueNode( cnp, lex::tokenFloat(lexH),"float" );
         else
          _objCreateValueNode( cnp, lex::tokenDouble(lexH), "double" );
        break;
        
      case lex::kIntLexTId:
        if( tokenIsUnsigned(lexH) )
          _objCreateValueNode( cnp, lex::tokenUInt(lexH), "uint" );
        else
          _objCreateValueNode( cnp, lex::tokenInt(lexH), "int" );
        break;
        
      case lex::kHexLexTId:
        _objCreateValueNode( cnp, lex::tokenInt(lexH), "int", kHexFl );
        break;
        
      case kTrueLexTId:
        _objCreateValueNode( cnp, true, "true" );        
        break;

      case kFalseLexTId:
        _objCreateValueNode( cnp, false, "false" );        
        break;

      case kNullLexTId:
        _objAppendRightMostNode( cnp, _objAllocate( kNullTId, cnp ));
        break;        

      case kSegmentedIdLexTId:
      case lex::kIdentLexTId:
      case lex::kQStrLexTId:
        {
          
          // if the parent is an object then this string must be a pair label
          if( cnp->is_dict() )
            cnp = _objAppendRightMostNode( cnp, _objAllocate( kPairTId, cnp ));

          unsigned n = lex::tokenCharCount(lexH);
          char s[ n + 1 ];
          memcpy(s,lex::tokenText(lexH),n);
          s[n] = 0;
          
          //char*    v       = mem::duplStr(lex::tokenText(lexH),lex::tokenCharCount(lexH));
          unsigned identFl = lexId != lex::kQStrLexTId ? kIdentFl    : 0;          
          
          _objCreateValueNode<char*>( cnp, s, "string", identFl );
        }
        break;

      case lex::kEofLexTId:
        break;

      default:
        _objSyntaxError(lexH,"Unknown token type (%i) in text.", int(lexId) );
    }

    if( cnp == nullptr )
    {
      rc = _objSyntaxError( lexH, "Node parse failed." );
      goto errLabel;
    }

    // if this is a pair node and it now has both values 
    // then make the parent 'object' the current node
    if( cnp->is_pair() && cnp->child_count()==2 )
      cnp = cnp->parent;
  }

  if( lexId == lex::kErrorLexTId )
  {
    rc = cwLogError(kSyntaxErrorRC,"A lexical element was not recognized.");
    goto errLabel;    
  }
  
  // if the root has only one child then make the child the root
  if( root != nullptr && root->child_count() == 1 )
  {
    cnp = root->u.children;
    cnp->unlink();
    root->free();
    root = cnp;
  }
  
  objRef = root;
  
 errLabel:
  rc_t rc0 = lex::destroy(lexH);

  return rc != kOkRC ? rc : rc0;
  
}

cw::rc_t cw::objectFromFile( const char* fn, object_t*& objRef )
{
  rc_t     rc         = kOkRC;
  unsigned bufByteCnt = 0;
  char*    buf        = NULL;

  objRef = nullptr;
  
  if(( buf = file::fnToStr(fn, &bufByteCnt)) == NULL )
    rc = cwLogError(kOpFailRC,"File to text buffer conversion failed on '%s'.",cwStringNullGuard(fn));
  else
  {
    rc = objectFromString( buf, objRef );
    mem::release(buf);
  }

  return rc;
}


void cw::objectPrintTypes( object_t* o0 )
{
  if( o0->is_container() )
    for(object_t* o = o0->u.children; o!=nullptr; o=o->sibling)
      objectPrintTypes(o);

  printf("%s ",o0->type->label);
}




cw::rc_t cw::objectToFile( const char* fn, const object_t* obj )
{
  rc_t rc = kOkRC;
  
  char* buf;
  if((buf = obj->to_string()) == nullptr )
  {
    rc = cwLogError(kOpFailRC,"Unable to convert object to string.");
    goto errLabel;
  }

  if((rc = file::fnWrite(fn,buf,textLength(buf))) != kOkRC )
  {
    rc = cwLogError(rc,"Object write failed on write to '%s'.",cwStringNullGuard(fn));
    goto errLabel;
  }
 errLabel:

  mem::release(buf);
  
  return rc;
}

namespace cw
{
  rc_t _object_test_basic( const test::test_args_t& args )
  {
    rc_t          rc   = kOkRC;  
    cw::object_t* o    = nullptr;
    const char    s [] = "{ a:1, b:2, c:[ 1.23, 4.56 ], d:true, e:false, f:true }";

    int a = 0;
    int b = 0;
    const cw::object_t* c = nullptr;
    bool d,e,f;

    const unsigned bufN = 128;
    char buf[bufN];

    unsigned i = 0;

    cw::object_t* oo = nullptr;
  
    if((rc = cw::objectFromString(s,o)) != kOkRC )
      goto errLabel;

    int v;
    if((rc = o->get("b",v)) != kOkRC )
      goto errLabel;
  
    cwLogPrint("value:%i\n",v);
  
    o->print();

  
    if((rc = o->getv("a",a,"b",b)) != kOkRC )
      goto errLabel;
  
    cwLogPrint("G: %i %i\n",a,b);


    if((rc = o->readv("a",0,a,
                      "b",0,b,
                      "c",cw::kOptFl | cw::kListTId,c,
                      "d",0,d,
                      "e",0,e,
                      "f",0,f)) != kOkRC )
    {
      goto errLabel;
    }
  
    cwLogPrint("R: %i %i : %i %i %i\n",a,b,d,e,f);

  
    i = o->to_string(buf,bufN);
    cwLogPrint("%i : %s\n",i, buf);
  
    oo = o->duplicate();

    oo->print();
  
    oo->free();
  
    
    o->free();

  errLabel:
    return rc;
  }

  rc_t _object_test_to_json( const test::test_args_t& args )
  {
    double   v0[] = {1.23,2.34,3.45};
    unsigned v0N  = sizeof(v0)/sizeof(v0[0]);
    int      v1[] = {-1,0,1,2,3,4};
    unsigned v1N  = sizeof(v1)/sizeof(v1[0]);
  
    cw::object_t* d = cw::newDictObject();

    d->putv("A","Abc","B",1.234);
    d->put_numeric_list("v0",v0,v0N);
    d->put_numeric_list("v1",v1,v1N);

    char* s = d->to_string();
    cwLogPrint("%s\n",s);
    cw::mem::release(s);

    d->free();

    return kOkRC;
  
  }

}


cw::rc_t cw::object_test( const test::test_args_t& args )
{
  rc_t rc = kOkRC;

  if( textIsEqual(args.test_label,"basic") )
  {
    rc = _object_test_basic(args);
    goto errLabel;
  }
  
  if( textIsEqual(args.test_label,"to_json") )
  {
    rc = _object_test_to_json(args);
    goto errLabel;
  }

  rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label);
  
errLabel:
  return rc;
}

