//
// parses xml tree format into internal objects
//


#include "plugin.h"

void Patch_XMLParse(patchMesh_t *pPatch, xmlNodePtr surface)
{
  char *str, *content;
  int i, j;

  for(xmlNodePtr current = surface->children; current != NULL; current = current->next)
  {
    if(current->type != XML_ELEMENT_NODE) continue;
    if(!strcmp((char *)current->name, "matrix"))
    {
      str = (char *)xmlGetProp(current, (xmlChar *)"width");
      pPatch->width = atoi(str);
      xmlFree(str);
      str = (char *)xmlGetProp(current, (xmlChar *)"height");
      pPatch->height = atoi(str);
      xmlFree(str);

      content = Q_StrDup((char *)current->children->content);

      str = strtok(content, " \n\r\t\v\0");
      for(i=0; i<pPatch->width; i++)
      {
        for(j=0; j<pPatch->height; j++)
        {
          pPatch->ctrl[i][j].xyz[0] = atof(str);
          str = strtok(NULL, " \n\r\t\v\0");
          pPatch->ctrl[i][j].xyz[1] = atof(str);
          str = strtok(NULL, " \n\r\t\v\0");
          pPatch->ctrl[i][j].xyz[2] = atof(str);
          str = strtok(NULL, " \n\r\t\v\0");
          pPatch->ctrl[i][j].st[0] = atof(str);
          str = strtok(NULL, " \n\r\t\v\0");
          pPatch->ctrl[i][j].st[1] = atof(str);
          str = strtok(NULL, " \n\r\t\v\0");
        }
      }

      delete [] content;
    }
    else if(!strcmp((char *)current->name, "shader")) {
      pPatch->pShader = QERApp_Shader_ForName((char*)current->children->content);
      pPatch->d_texture = pPatch->pShader->getTexture();
    }
  }
}

void Face_XMLParse (face_t *face, xmlNodePtr surface)
{
  char *str, *content;
  int i, j;

  for(xmlNodePtr current = surface->children; current != NULL; current = current->next)
  {
    if(current->type != XML_ELEMENT_NODE) continue;
    if(!strcmp((char *)current->name, "planepts"))
    {
      content = Q_StrDup((char *)current->children->content);

      str = strtok(content, " \n\r\t\v\0");
      for (i=0 ; i<3 ; i++)
	    {
		    for (j=0 ; j<3 ; j++)
		    {
			    face->planepts[i][j] = atof(str);
          str = strtok(NULL, " \n\r\t\v\0");
		    }
      }

      delete [] content;
    }
    else if(!strcmp((char *)current->name, "texdef"))
    {
      content = Q_StrDup((char *)current->children->content);
  
      str = strtok(content, " \n\r\t\v\0");
      face->texdef.shift[0] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->texdef.shift[1] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->texdef.rotate = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->texdef.scale[0] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->texdef.scale[1] = atof(str);

      delete [] content;
    }
    else if(!strcmp((char *)current->name, "bpmatrix"))
    {
      content = Q_StrDup((char *)current->children->content);
  
      str = strtok(content, " \n\r\t\v\0");
      face->brushprimit_texdef.coords[0][0] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->brushprimit_texdef.coords[0][1] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->brushprimit_texdef.coords[0][2] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->brushprimit_texdef.coords[1][0] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->brushprimit_texdef.coords[1][1] = atof(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->brushprimit_texdef.coords[1][2] = atof(str);

      delete [] content;
    }
    else if(!strcmp((char *)current->name, "flags"))
    {
      content = Q_StrDup((char *)current->children->content);

      str = strtok(content, " \n\r\t\v\0");
      face->texdef.contents = atoi(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->texdef.flags = atoi(str);
      str = strtok(NULL, " \n\r\t\v\0");
      face->texdef.value = atoi(str);

      delete [] content;
    }
    else if(!strcmp((char *)current->name, "shader"))
    {
      face->texdef.SetName((char *)current->children->content);
    }
  }
}

void Brush_XMLParse (brush_t *pBrush, xmlNodePtr primitive)
{
  face_t    *f;

  for(xmlNodePtr current = primitive->children; current != NULL; current = current->next)
  {
    if(current->type != XML_ELEMENT_NODE) continue;
    f = pBrush->brush_faces;
    pBrush->brush_faces = Face_Alloc();
    Face_XMLParse(pBrush->brush_faces, current);
    pBrush->brush_faces->next = f;
  }
}

void Entity_XMLParse(entity_t *pEntity, xmlNodePtr entity)
{
  brush_t *pBrush;

  for(xmlNodePtr current = entity->children; current != NULL; current = current->next)
  {
    if(current->type != XML_ELEMENT_NODE) continue;
    if(!strcmp((char *)current->name, "epair"))
    {
      char *key = (char *)xmlGetProp(current, (xmlChar *)"key");
      char *value = (char *)xmlGetProp(current, (xmlChar *)"value");
      SetKeyValue(pEntity, key, value);
      xmlFree(key);
      xmlFree(value);
    }
    else if(strcmp((char *)current->name, "brush") == 0)
    {
      pBrush = Brush_Alloc();
      Brush_XMLParse(pBrush, current);
      ((CPtrArray*)pEntity->pData)->Add(pBrush);
    }
    else if(strcmp((char *)current->name, "patch") == 0)
    {
      pBrush = Brush_Alloc();
      pBrush->patchBrush = true;
      pBrush->pPatch = Patch_Alloc();
      pBrush->pPatch->pSymbiot = pBrush;
      Patch_XMLParse(pBrush->pPatch, current);
      ((CPtrArray*)pEntity->pData)->Add(pBrush);
    }
  }
}

void Map_XMLRead(CPtrArray *map, xmlNodePtr map_node)
{
  entity_t *pEntity;
  xmlNodePtr current;

  for(current = map_node->children; current != NULL; current = current->next)
  {
    if(current->type != XML_ELEMENT_NODE) continue;
    pEntity = Entity_Alloc();
    pEntity->pData = new CPtrArray;
    Entity_XMLParse(pEntity, current);
    map->Add(pEntity);
  }
}

// SPoG
// temporarily copied from qe3.cpp
// duplicate code starts here (note: g_strAppPath swapped for g_FuncTable.m_pfnGetQERPath())

void HandleXMLError( void* ctxt, const char* text, ... )
{
  va_list argptr;
  static char buf[32768];

  va_start (argptr,text);
  vsprintf (buf, text, argptr);
  Sys_FPrintf (SYS_ERR, "XML %s\n", buf);
  va_end (argptr);
}

#define DTD_BUFFER_LENGTH 1024
xmlDocPtr ParseXMLStream(IDataStream *stream, bool validate = false)
{
  xmlDocPtr doc = NULL;
  bool wellFormed = false, valid = false;
  int res, size = 1024;
  char chars[1024];
  xmlParserCtxtPtr ctxt;

  // SPoG
  // HACK: use AppPath to resolve DTD location
  // do a buffer-safe string copy and concatenate
  int i;
  char* w;
  const char* r;
  char buf[DTD_BUFFER_LENGTH];

  w = buf;
  i = 0;
  // copy
  //assert(g_FuncTable.m_pfnGetQERPath() != NULL);
  for(r = g_FuncTable.m_pfnGetQERPath(); i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;
  // concatenate
  for(r = "dtds/"; i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;
  // terminate
  w[i] = '\0';

  if(i == DTD_BUFFER_LENGTH)
  {
    HandleXMLError(NULL, "ERROR: buffer overflow: DTD path length too large\n");
    return NULL;
  }

  //if(validate)
  //  xmlDoValidityCheckingDefaultValue = 1;
  //else
    xmlDoValidityCheckingDefaultValue = 0;

  xmlSetGenericErrorFunc(NULL, HandleXMLError);

  res = stream->Read(chars, 4);
  if (res > 0)
  {
    ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, buf);

    while ((res = stream->Read(chars, size)) > 0)
    {
      xmlParseChunk(ctxt, chars, res, 0);
    }
    xmlParseChunk(ctxt, chars, 0, 1);
    doc = ctxt->myDoc;

    wellFormed = (ctxt->wellFormed == 1);
    valid = (ctxt->valid == 1);

    xmlFreeParserCtxt(ctxt);
  }

  if(wellFormed && (!validate || (validate && valid)))
    return doc;

  if(doc != NULL)
    xmlFreeDoc(doc);

  return NULL;
}

// duplicate code ends here

void Map_Read (IDataStream *in, CPtrArray *map)
{
  xmlDocPtr doc;

  doc = ParseXMLStream(in, false ); // quick hack while dtd validation is broken

  if(doc != NULL)
  {
    xmlNodePtr node=doc->children;
    while(node != NULL && node->type != XML_ELEMENT_NODE) node=node->next;
    if(node != NULL)
      Map_XMLRead(map, node);
  }
 
  xmlFreeDoc(doc);
}
