/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
/* ************************************************************************/
#include "cxml_internal.h"
#include <nw_wbxml_parse.h>
#include <nw_wbxml_document.h>
#include <nw_encoder_wbxmlwriter.h>
#include "nw_encoder_tinydom2wbxml.h"
#include <nw_tinytree.h>
#include <nw_tinydom.h>

/* Create an empty tree */

NW_Status_t
NW_TinyDom_Tree_create(NW_TinyDom_Tree_t *dom_tree,
                       NW_TinyDom_Parser_t *dom_parser,
                       NW_WBXML_Document_t *doc,
                       NW_WBXML_Parser_t *parser,
                       NW_WBXML_Writer_t* writer,
                       NW_Uint16 init_node_count,
                       NW_Bool enableStringTable)
{
  NW_WBXML_Dictionary_t* dictionary = NULL;

  /* First get the dictionary */

  if (doc->publicid != 0) {
    dictionary = NW_WBXML_Dictionary_getByPublicId (doc->publicid);
    parser->dictionary = NW_WBXML_Dictionary_getIndexByPublicId(doc->publicid);
  }
  else if (doc->doc_type != NULL) {
    dictionary = NW_WBXML_Dictionary_getByDocType (doc->doc_type, doc->charset);
    parser->dictionary = NW_WBXML_Dictionary_getIndexByDocType(doc->doc_type, doc->charset);
  }

  if (dictionary == NULL) {
    return NW_STAT_FAILURE;
  }

  /* TODO: make dictionary a member of dom_parser ?? */

  /* Initialize the writer */

  NW_WBXML_Writer_Initialize(writer,
                             0, NULL,
                             NULL,
                             dictionary,
                             dictionary,
                             ((enableStringTable == NW_TRUE) ?
                              NW_Encoder_StringTable_getStringTableOffset :
                              NULL),
                             ((enableStringTable == NW_TRUE) ?
                              NW_Encoder_StringTable_addToStringTable :
                              NULL),
                             ((enableStringTable == NW_TRUE) ?
                              doc->strtbl_extension :
                              NULL),
                             ((enableStringTable == NW_TRUE) ?
                              NW_Encoder_StringTable_StringTableIterateInit :
                              NULL),
                             ((enableStringTable == NW_TRUE) ?
                              NW_Encoder_StringTable_StringTableIterateNext :
                              NULL),
                             NW_TRUE);

  /* Construct the dom tree object */
  NW_TinyDom_Tree_construct(dom_tree, parser, doc, writer);

  /* Construct the dom parser object */
  NW_TinyDom_Parser_construct(dom_parser, dom_tree);

  /* Construct the tiny tree object, passing the dom parser as the
     context argument */
  
  
  if( (NW_TinyTree_construct(&(dom_tree->tree),
                        (CXML_Vector_Metric_t)(init_node_count + 1),
                        0,
                        0,
                        dom_parser,
                        NW_TRUE) ) == NW_STAT_FAILURE)
  {
	  return NW_STAT_OUT_OF_MEMORY;
  }

  /* The root node we create here actually has no valid buffer storage yet */
  NW_TinyTree_setRoot(&(dom_tree->tree), 0);

  /* TODO: Why does dom_tree need a doc member if parser has one too ???? */
  parser->doc = doc;

  dom_tree->root_node = NW_TinyTree_getRoot(&(dom_tree->tree));

  /* Write the doc header block. This will fill in the root node storage */
  dom_tree->root_node = NW_TinyDom_writeDocHeader(dom_tree,
                                                  doc->version,
                                                  doc->publicid,
                                                  doc->charset);
  if (dom_tree->root_node == NULL) {
    return NW_STAT_FAILURE;
  }
  /* Mark the root node as the doc node */
  NW_TinyTree_Node_setUserFlags(dom_tree->root_node, T_DOM_NODE_DOC);
  return NW_STAT_SUCCESS;
}

NW_TinyTree_Node_t *
NW_TinyDom_writeDocHeader(NW_TinyDom_Tree_t *dom_tree,
                          NW_Uint8 version,
                          NW_Uint32 publicid,
                          NW_Uint32 encoding)
{
  NW_Status_t status;
  NW_TinyTree_Offset_t offset;
  NW_TinyTree_Node_t * node = dom_tree->root_node;
  CXML_Vector_Metric_t size;

  /* If the root node isn't set, the tree wasn't properly inited */
  if (node != NULL) {
    NW_Uint8* buffer;
    /* Set up the writer for a sizing pass */
    NW_WBXML_Writer_SetToSizing(dom_tree->writer);
    /* Call the writer to run the sizing pass */
    status = NW_WBXML_Writer_Header(dom_tree->writer,
                                    version,
                                    publicid,
                                    encoding,
                                    0);


    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Allocate a buffer of the correct size */
    size = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(dom_tree->writer);
    buffer = NW_TinyTree_GetWritableBlock(&(dom_tree->tree), size, &offset);

	 if(buffer == NULL)
	 {
		 return NULL;
	 }

    /* Set up the writer for actual writing */
    NW_WBXML_Writer_SetToWrite(dom_tree->writer, size, buffer);
    /* Do the write */
    status = NW_WBXML_Writer_Header(dom_tree->writer,
                                    version,
                                    publicid,
                                    encoding,
                                    0);

    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Point the root node source offset at the new block */
    node->source_offset = offset;
  }
  return node;
}


NW_TinyTree_Node_t *
NW_TinyDom_createAttributeByToken(NW_TinyDom_Tree_t *dom_tree,
                                  NW_Uint16 token,
                                  NW_TinyDom_AttrVal_t *value)
{
  NW_Status_t status;
  NW_TinyTree_Offset_t offset;

  NW_TinyTree_Node_t * node = NW_TinyTree_createNode(&(dom_tree->tree),0);

  if (node != NULL) {
    NW_Uint8* buffer;
    CXML_Vector_Metric_t size;

    /* Set up the writer for a sizing pass */
    NW_WBXML_Writer_SetToSizing(dom_tree->writer);
    /* Call the writer to run the sizing pass */
    status = NW_Encoder_writeAttributeByToken(dom_tree->writer, token, value,
                                              dom_tree->doc->charset);

    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Allocate a buffer of the correct size */
    size = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(dom_tree->writer);
    buffer = NW_TinyTree_GetWritableBlock(&(dom_tree->tree), size, &offset);
    if (buffer == NULL)
    {
      return NULL;
    }

    /* Set up the writer for actual writing */
    NW_WBXML_Writer_SetToWrite(dom_tree->writer, size, buffer);
    /* Do the write */
    status = NW_Encoder_writeAttributeByToken(dom_tree->writer, token, value,
                                              dom_tree->doc->charset);
    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Point the node source offset at the new block */
    node->source_offset = offset;
    /* Mark node as attribute node */
    NW_TinyTree_Node_setUserFlags(node, T_DOM_NODE_ATTR);
    return node;
  }
  return NULL;
}

NW_TinyTree_Node_t *
NW_TinyDom_createAttributeByName(NW_TinyDom_Tree_t *dom_tree,
                                 NW_String_t *name,
                                 NW_TinyDom_AttrVal_t *value)
{
  NW_Status_t status;
  NW_TinyTree_Offset_t offset;

  NW_TinyTree_Node_t * node = NW_TinyTree_createNode(&(dom_tree->tree),0);

  if (node != NULL) {
    NW_Uint8* buffer;
    CXML_Vector_Metric_t size;

    /* Set up the writer for a sizing pass */
    NW_WBXML_Writer_SetToSizing(dom_tree->writer);
    /* Call the writer to run the sizing pass */
    status = NW_Encoder_writeAttributeByName(dom_tree->writer, name, value,
                                             dom_tree->doc->charset);

    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Allocate a buffer of the correct size */
    size = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(dom_tree->writer);
    buffer = NW_TinyTree_GetWritableBlock(&(dom_tree->tree), size, &offset);
    if (buffer == NULL)
    {
      return NULL;
    }

    /* Set up the writer for actual writing */
    NW_WBXML_Writer_SetToWrite(dom_tree->writer, size, buffer);
    /* Do the write */
    status = NW_Encoder_writeAttributeByName(dom_tree->writer, name, value,
                                             dom_tree->doc->charset);
    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Point the node source offset at the new block */
    node->source_offset = offset;
    /* Mark node as attribute node */
    NW_TinyTree_Node_setUserFlags(node, T_DOM_NODE_ATTR);
    return node;
  }
  return NULL;
}

NW_Status_t
NW_TinyDom_AttributeHandle_initWithStartToken(NW_TinyDom_AttributeHandle_t* tinyHandle,
                                              NW_TinyTree_Node_t** ppNode,
                                              NW_TinyDom_Parser_t* parser,
                                              NW_Uint16 fqToken)
{
  NW_Status_t status;
  NW_TinyTree_Offset_t offset;
  NW_Uint8* buffer;
  CXML_Vector_Metric_t size;
  NW_TinyDom_Tree_t* dom_tree = parser->dom_tree;

  /* Set up the writer for a sizing pass */
  NW_WBXML_Writer_SetToSizing(dom_tree->writer);
  /* Call the writer to run the sizing pass */
  status = NW_WBXML_Writer_AttributeToken(dom_tree->writer, fqToken);
  if (status != NW_STAT_SUCCESS) {
    return status;
  }
  status = NW_WBXML_Writer_End(dom_tree->writer);
  if (status != NW_STAT_SUCCESS) {
    return status;
  }
  /* Allocate a buffer of the correct size */
  size = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(dom_tree->writer);
  buffer = NW_TinyTree_GetWritableBlock(&(dom_tree->tree), size, &offset);
  if (buffer == NULL)
  {
    return NW_STAT_OUT_OF_MEMORY;
  }

  *ppNode = NW_TinyTree_createNode(&(dom_tree->tree), offset);
  if (*ppNode == NULL) {
    NW_Mem_Free(buffer);
    return NW_STAT_OUT_OF_MEMORY;
  }
  NW_TinyTree_Node_setUserFlags(*ppNode, T_DOM_NODE_ATTR);

  /* Set up the writer for actual writing */
  NW_WBXML_Writer_SetToWrite(dom_tree->writer, size, buffer);
  /* Do the write */
  status = NW_WBXML_Writer_AttributeToken(dom_tree->writer, fqToken);
  if (status != NW_STAT_SUCCESS) {
    return status;
  }
  status = NW_WBXML_Writer_End(dom_tree->writer);
  if (status != NW_STAT_SUCCESS) {
    return status;
  }
  /* initialize the attribute handle */
  NW_TinyDom_AttributeHandle_init(tinyHandle, parser, offset);

  return NW_STAT_SUCCESS;
}

NW_Status_t
NW_TinyDom_AttributeHandle_addVal(NW_TinyDom_AttributeHandle_t* tinyHandle,
                                  NW_TinyTree_Node_t* node,
                                  NW_TinyDom_AttrVal_t* val)
{
  NW_TinyDom_AttrVal_t av;
  NW_TinyTree_t* tinyTree = &(tinyHandle->tlit.tiny_parser->dom_tree->tree);
  NW_TinyDom_Tree_t* tinyDomTree = tinyHandle->tlit.tiny_parser->dom_tree;
  NW_Uint32 encoding;
  void* existingBuffer;
  NW_Uint8* buffer;
  CXML_Vector_Metric_t start = 0;
  CXML_Vector_Metric_t valSize;
  CXML_Vector_Metric_t existingSize;
  CXML_Vector_Metric_t offset;
  NW_Status_t status = NW_STAT_FAILURE;

  encoding = NW_TinyDom_getDocHeader(tinyTree)->charset;

  switch (val->type) {
  case NW_WBXML_ATTR_COMPONENT_TOKEN:
  case NW_WBXML_ATTR_COMPONENT_STRING:
  case NW_WBXML_ATTR_COMPONENT_EXT:
  case NW_WBXML_ATTR_COMPONENT_ENTITY:
  case NW_WBXML_ATTR_COMPONENT_OPAQUE:
    NW_WBXML_Writer_SetToSizing(tinyDomTree->writer);
    start
      = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(tinyDomTree->writer);
    status = NW_Encoder_writeAttrVal(tinyDomTree->writer, val,
                                     encoding);
    if (status != NW_STAT_SUCCESS) {
      return status;
    }
    status = NW_WBXML_Writer_End(tinyDomTree->writer);
    break;
  }
  if (status != NW_STAT_SUCCESS) {
    return status;
  }
  valSize
    = (CXML_Vector_Metric_t)(NW_WBXML_Writer_GetSize(tinyDomTree->writer)
       - start);

  /* existing content size */
  start = node->source_offset;
  NW_TinyDom_AttributeHandle_init(tinyHandle, tinyHandle->tlit.tiny_parser,
                                  node->source_offset);
  tinyHandle->value = &av; /* required to hold value results */
  while (NW_TinyDom_AttributeHandle_valsIterate(&(tinyHandle->tlit)) != 0) {
    /* empty loop body: we just want parser to advance */
  }
  existingSize = (CXML_Vector_Metric_t)(tinyHandle->tlit.offset - start);
  existingBuffer = NW_TinyTree_Node_getSourceAddress(tinyTree, node);

  buffer = NW_TinyTree_GetWritableBlock(tinyTree,
                                        ((CXML_Vector_Metric_t)
                                         (valSize + existingSize)),
                                        &offset);
  if (buffer == NULL) {
    return NW_STAT_OUT_OF_MEMORY;
  }
  NW_Mem_memcpy(buffer, existingBuffer, existingSize);
  NW_WBXML_Writer_SetToWrite(tinyDomTree->writer,
                             valSize,
                             buffer + existingSize);
  (void)NW_Encoder_writeAttrVal(tinyDomTree->writer, val, encoding);
  (void)NW_WBXML_Writer_End(tinyDomTree->writer);

  /* TBD there is no way to "free" the old storage */
  node->source_offset = offset;
  return NW_STAT_SUCCESS;
}


NW_TinyTree_Node_t *
NW_TinyDom_createElementByToken(NW_TinyDom_Tree_t *dom_tree, NW_Uint16 token) {

  NW_Status_t status;
  NW_TinyTree_Offset_t offset;

  NW_TinyTree_Node_t * node = NW_TinyTree_createNode(&(dom_tree->tree),0);

  if (node != NULL) {
    NW_Uint8* buffer;
    CXML_Vector_Metric_t size;

    /* Set up the writer for a sizing pass */
    NW_WBXML_Writer_SetToSizing(dom_tree->writer);
    /* Call the writer to run the sizing pass */
    status = NW_Encoder_writeElementByToken(dom_tree->writer, token);

    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Allocate a buffer of the correct size */
    size = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(dom_tree->writer);
    buffer = NW_TinyTree_GetWritableBlock(&(dom_tree->tree), size, &offset);
    if (buffer == NULL)
    {
      return NULL;
    }

    /* Set up the writer for actual writing */
    NW_WBXML_Writer_SetToWrite(dom_tree->writer, size, buffer);
    /* Do the write */
    status = NW_Encoder_writeElementByToken(dom_tree->writer, token);
    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }

    /* Point the node source offset at the new block */
    node->source_offset = offset;
    /* Mark node as attribute node */
    NW_TinyTree_Node_setUserFlags(node, T_DOM_NODE_TAG);
    return node;
  }
  return NULL;
}


NW_TinyTree_Node_t *
NW_TinyDom_createElementByName(NW_TinyDom_Tree_t *dom_tree, NW_String_t *name)
{
  NW_Status_t status;
  NW_TinyTree_Offset_t offset;

  NW_TinyTree_Node_t * node = NW_TinyTree_createNode(&(dom_tree->tree),0);

  if (node != NULL) {
    NW_Uint8* buffer;
    CXML_Vector_Metric_t size;

    /* Set up the writer for a sizing pass */
    NW_WBXML_Writer_SetToSizing(dom_tree->writer);
    /* Call the writer to run the sizing pass */
    status = NW_Encoder_writeElementByName(dom_tree->writer, name,
                                           dom_tree->doc->charset);
    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Allocate a buffer of the correct size */
    size = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(dom_tree->writer);
    buffer = NW_TinyTree_GetWritableBlock(&(dom_tree->tree), size, &offset);
    if (buffer == NULL)
    {
      return NULL;
    }

    /* Set up the writer for actual writing */
    NW_WBXML_Writer_SetToWrite(dom_tree->writer, size, buffer);
    /* Do the write */
    status = NW_Encoder_writeElementByName(dom_tree->writer, name,
                                           dom_tree->doc->charset);
    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }

    /* Point the node source offset at the new block */
    node->source_offset = offset;
    /* Mark node as attribute node */
    NW_TinyTree_Node_setUserFlags(node, T_DOM_NODE_TAG);
    return node;
  }
  return NULL;
}

NW_TinyTree_Node_t *
NW_TinyDom_createTextNode(NW_TinyDom_Tree_t *dom_tree,
                          NW_TinyDom_Text_t *text)
{
  NW_Status_t status;
  NW_TinyTree_Offset_t offset;

  NW_TinyTree_Node_t * node = NW_TinyTree_createNode(&(dom_tree->tree),0);

  if (node != NULL) {
    NW_Uint8* buffer;
    CXML_Vector_Metric_t size;

    /* Set up the writer for a sizing pass */
    NW_WBXML_Writer_SetToSizing(dom_tree->writer);
    /* Call the writer to run the sizing pass */
    status = NW_Encoder_writeText(dom_tree->writer, text,
                                  dom_tree->doc->charset);
    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Allocate a buffer of the correct size */
    size = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(dom_tree->writer);
    buffer = NW_TinyTree_GetWritableBlock(&(dom_tree->tree), size, &offset);
    if (buffer == NULL)
    {
      return NULL;
    }

    /* Set up the writer for actual writing */
    NW_WBXML_Writer_SetToWrite(dom_tree->writer, size, buffer);
    /* Do the write */
    status = NW_Encoder_writeText(dom_tree->writer, text,
                                  dom_tree->doc->charset);
    if (status != NW_STAT_SUCCESS) {
      return NULL;
    }
    /* Point the node source offset at the new block */
    node->source_offset = offset;
    /* Mark node as attribute node */
    NW_TinyTree_Node_setUserFlags(node, T_DOM_NODE_TEXT);
    return node;
  }
  return NULL;
}

NW_Status_t
NW_TinyDom_addDataFromTextItem(NW_TinyTree_t* tinyTree,
                               NW_TinyDom_Tree_t* tinyDomTree,
                               NW_DOM_TextNode_t* node,
                               NW_TinyDom_AttrVal_t* val,
                               NW_Uint32 encoding)
{
  void* existingBuffer;
  NW_Uint8* buffer;
  NW_TinyDom_TextHandle_t iterator;
  NW_TinyDom_Text_t textItem;
  CXML_Vector_Metric_t start = 0;
  CXML_Vector_Metric_t valSize;
  CXML_Vector_Metric_t existingSize;
  CXML_Vector_Metric_t offset;
  NW_Status_t status = NW_STAT_FAILURE;

  switch (val->type) {
  case NW_WBXML_ATTR_COMPONENT_STRING:
  case NW_WBXML_ATTR_COMPONENT_EXT:
  case NW_WBXML_ATTR_COMPONENT_ENTITY:
  case NW_WBXML_ATTR_COMPONENT_OPAQUE:
    NW_WBXML_Writer_SetToSizing(tinyDomTree->writer);
    start
      = (CXML_Vector_Metric_t)NW_WBXML_Writer_GetSize(tinyDomTree->writer);
    status = NW_Encoder_writeAttrVal(tinyDomTree->writer, val,
                                     encoding);
    if (status != NW_STAT_SUCCESS) {
      return status;
    }
    status = NW_WBXML_Writer_End(tinyDomTree->writer);
    break;
  }
  if (status != NW_STAT_SUCCESS) {
    return status;
  }
  valSize
    = (CXML_Vector_Metric_t)(NW_WBXML_Writer_GetSize(tinyDomTree->writer)
       - start);

  /* existing content size */
  NW_TinyDom_TextHandle_init(&iterator,
                             NW_TinyDom_getParser(tinyTree),
                             NW_TinyTree_Node_getSourceOffset(node));
  start = (CXML_Vector_Metric_t)iterator.tlit.offset;
  while (NW_TinyDom_TextHandle_iterate(&iterator, &textItem) != 0) {
    /* empty loop body: we just want parser to advance */
  }
  existingSize = (CXML_Vector_Metric_t)(iterator.tlit.offset - start);
  existingBuffer = NW_TinyTree_Node_getSourceAddress(tinyTree, node);

  buffer = NW_TinyTree_GetWritableBlock(tinyTree,
                                        ((CXML_Vector_Metric_t)
                                         (valSize + existingSize)),
                                        &offset);
  if (buffer == NULL) {
    return NW_STAT_OUT_OF_MEMORY;
  }
  NW_Mem_memcpy(buffer, existingBuffer, existingSize);
  NW_WBXML_Writer_SetToWrite(tinyDomTree->writer,
                             valSize,
                             buffer + existingSize);
  (void)NW_Encoder_writeAttrVal(tinyDomTree->writer, val, encoding);
  (void)NW_WBXML_Writer_End(tinyDomTree->writer);

  /* TBD there is no way to "free" the old storage */
  node->source_offset = offset;
  return NW_STAT_SUCCESS;
}

NW_Status_t
NW_TinyDom_removeAttrFromListNode(NW_TinyDom_AttrListHandle_t *it, NW_Uint32 length)
{
  NW_Uint16 len = 0;
  NW_Byte* p = NULL;
  NW_Byte *moveTo = NULL;
  NW_Byte *moveFrom = NULL;

  p = (NW_Byte*)((NW_Byte*)it->segment + it->offset);
  moveTo = (NW_Byte*)(p - length);
  moveFrom = p;
  while (*p != 0x01)
  {
    len++;
    p++;
  }
  NW_Mem_memset(moveTo, 0, length);
  NW_Mem_memmove(moveTo, moveFrom, (len+1));
  return NW_STAT_SUCCESS;
}







