#include "SM_SysPch.h"
#include <stack>
#include "MVFS.h"
#include "ml.h"
#include "SM_Main.h"


//#define ML_LOGTOCONSOLE

MetaFileToken::MetaFileToken()
{  
}

MetaFileToken::~MetaFileToken()
{
  Shutdown();
}

int MetaFileToken::Init()
{
  return (0);
}

int MetaFileToken::Shutdown()
{
  switch (m_eType)
  {
  case E_TAGBEGIN:
  case E_TAGEND:
  case E_STRING:
    if (m_pszString) delete[] m_pszString;
    m_pszString=0;
    break;
  }

  return (0);
}

MetaFileNode::MetaFileNode()
{
}

MetaFileNode::~MetaFileNode()
{
  Shutdown();
}

int MetaFileNode::Init()
{
  return (0);
}

int MetaFileNode::Shutdown()
{
  unsigned uSize = m_Sons.size();
  for (unsigned i=0 ; i<uSize ; i++)
  {
    delete m_Sons[i];
  }

  m_Token.Shutdown();

  return (0);
}

bool MetaFileNode::IsTag ()
{
  return (m_Token.m_eType==MetaFileToken::E_TAGBEGIN);
}

bool MetaFileNode::IsTag (const char* pcTag)
{
  return (m_Token.m_eType==MetaFileToken::E_TAGBEGIN &&
          strcmp(pcTag, m_Token.m_pszString)==0);
}
  

bool MetaFileNode::IsLeaf()
{
  return (m_Token.m_eType!=MetaFileToken::E_TAGBEGIN);
}

MetaFileNode* MetaFileNode::GetSon(const char* pszString)
{
  for (unsigned i=0 ; i<m_Sons.size() ; i++)
  {
    if (!strcmp(m_Sons[i]->m_Token.m_pszString, pszString))
    {
      return (m_Sons[i]);
    }
  }

  return (0);
}
  

MetaFile::MetaFile()
{
  m_pRoot=0;
}

MetaFile::~MetaFile()
{
  Shutdown();
}

MetaFileNode* MetaFile::Root()
{
  return (m_pRoot);
}


int MetaFile::Load(const char* pszFilename)
{
  MVFSFILE*     f=0;
  char* pszBuffer=0;
  int   iReturn  =0;

  
  f=MVFS::fopen(pszFilename, "rb");
  if (!f)
  {
    return (-1);
  }

  MVFS::fseek(f, 0, SEEK_END);
  int iLength=MVFS::ftell(f);
  MVFS::fseek(f, 0, SEEK_SET);

  pszBuffer=new char[iLength+1];

  if (iLength)
  {
    if (MVFS::fread(pszBuffer, iLength, 1, f)!=1)
    {
      iReturn=-1;
      goto END;
    }
  }

  pszBuffer[iLength]=0;

  iReturn=Parse(pszBuffer);

END:
  if (pszBuffer) delete[] pszBuffer;
  if (f)         MVFS::fclose(f);        

  return (iReturn);
}


int MetaFile::GetToken(const char** ppszBuffer, MetaFileToken* pMetaFileToken)
{
  const char* pc=*ppszBuffer;
  const char* pcMetaFileTokenStart=0;

  enum eParseState
  {
    E_NONE,
    E_TAGBEGIN,
    E_TAGEND,
    E_INT,
    E_FLOAT,
    E_STRING,
    E_FINISHED,
    E_ERROR,
  };

  eParseState ParseState=E_NONE;
  bool bMeaningful=false;
  
  while (1)
  {
    switch (ParseState)
    {      
    case E_NONE:
      switch (*pc)
      {
      case '<':
        pcMetaFileTokenStart=pc+1;
        ParseState=E_TAGBEGIN;
        break;
      case '-':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        pcMetaFileTokenStart=pc;
        ParseState=E_INT;
        break;
      case '"':
        pcMetaFileTokenStart=pc+1;
        ParseState=E_STRING;
        break;
      case '\0':
        goto EOS;
      }
      break;

    case E_TAGBEGIN:
      switch (*pc)
      {
      case '>':
        if (pcMetaFileTokenStart==pc)
        {
          goto HASERROR;
        }

        // Ok, finished MetaFileToken
        pMetaFileToken->m_eType    =MetaFileToken::E_TAGBEGIN;
        pMetaFileToken->m_pszString=new char[pc-pcMetaFileTokenStart+1];
        if (!pMetaFileToken->m_pszString)
        {
          goto HASERROR;
        }
        strncpy(pMetaFileToken->m_pszString, pcMetaFileTokenStart, pc-pcMetaFileTokenStart);
        pMetaFileToken->m_pszString[pc-pcMetaFileTokenStart]=0;

        goto FINISHED;
        
      case '/':
        pcMetaFileTokenStart=pc+1;
        ParseState=E_TAGEND;
        break;
      case '<':
      case '\0':
        goto HASERROR;
      }
      break;
    case E_TAGEND:
      switch (*pc)
      {
      case '>':
        /*
        if (pcMetaFileTokenStart==pc)
        {
          goto HASERROR;
        }
        */

        // Ok, finished MetaFileToken
        pMetaFileToken->m_eType    =MetaFileToken::E_TAGEND;
        pMetaFileToken->m_pszString=new char[pc-pcMetaFileTokenStart+1];
        if (!pMetaFileToken->m_pszString)
        {
          goto HASERROR;
        }
        strncpy(pMetaFileToken->m_pszString, pcMetaFileTokenStart, pc-pcMetaFileTokenStart);
        pMetaFileToken->m_pszString[pc-pcMetaFileTokenStart]=0;

        goto FINISHED;
        
      case '/':
      case '<':
      case '\0':
        goto HASERROR;
      }
      break;
    case E_INT:
      switch (*pc)
      {
      case '-':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        break;
      case '.':
        ParseState=E_FLOAT;
        break;
      default:
        char* pszTemp=new char [pc-pcMetaFileTokenStart+1];
        if (!pszTemp)
        {
          goto HASERROR;
        }

        pMetaFileToken->m_eType=MetaFileToken::E_INT;

        strncpy(pszTemp, pcMetaFileTokenStart, pc-pcMetaFileTokenStart);        
        pszTemp[pc-pcMetaFileTokenStart]=0;
        pMetaFileToken->m_iValue=atoi(pszTemp);
        delete[] pszTemp;
        
        goto FINISHED;
      }
      break;
    case E_FLOAT:
      switch (*pc)
      {      
        case '-':
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          break;
        case '.':
          goto HASERROR;
        default:
          char* pszTemp=new char [pc-pcMetaFileTokenStart+1];
          if (!pszTemp)
          {
            goto HASERROR;
          }
          pMetaFileToken->m_eType=MetaFileToken::E_FLOAT;

          strncpy(pszTemp, pcMetaFileTokenStart, pc-pcMetaFileTokenStart);                
          pszTemp[pc-pcMetaFileTokenStart]=0;
          pMetaFileToken->m_fValue=(float) atof(pszTemp);
          delete[] pszTemp;
        
          goto FINISHED;
      }
      
      break;
    case E_STRING:
      switch (*pc)
      {
        case '"':
          pMetaFileToken->m_eType=MetaFileToken::E_STRING;

          pMetaFileToken->m_pszString=new char[pc-pcMetaFileTokenStart+1];
          if (!pMetaFileToken->m_pszString)
          {
            goto HASERROR;
          }

          strncpy(pMetaFileToken->m_pszString, pcMetaFileTokenStart, pc-pcMetaFileTokenStart);                
          pMetaFileToken->m_pszString[pc-pcMetaFileTokenStart]=0;
          goto FINISHED;

        case '\0':
          goto HASERROR;
        
      }

      break;
    case E_FINISHED:
      break;
    case E_ERROR:
      break;
    }

    pc++;
  }

EOS:
  *ppszBuffer=0; 
  return (0);

FINISHED:
  pc++;
  *ppszBuffer=pc;
   return (1);

HASERROR:
  *ppszBuffer=0;
  
  return (-1);    
}
int MetaFile::Parse(const char* pszBuffer)
{
  MetaFileToken NewToken;
  int iResult;

  const char* pszCurrent=pszBuffer;

  typedef std::stack<MetaFileNode*> NodeStack;

  // Create root MetaFileNode;
  m_pRoot=new MetaFileNode;
  if (!m_pRoot)
  {
    return (-1);    
  }
  m_pRoot->m_Token.m_eType=MetaFileToken::E_ROOT;
  NodeStack Stack;
    
  Stack.push(m_pRoot);
  
  while ((iResult=GetToken(&pszCurrent, &NewToken))>0)
  {
    MetaFileNode* pMetaFileNode=new MetaFileNode;
    
    if (!pMetaFileNode)
    {      
      return (-1);    
    }

    // Copy MetaFileToken to created MetaFileNode
    pMetaFileNode->m_Token=NewToken;

    // @HACK
    NewToken.m_pszString=0;

    switch (NewToken.m_eType)
    {
    case MetaFileToken::E_TAGBEGIN:
      // We add new MetaFileNode to the son list of the top of the stack
      Stack.top()->m_Sons.push_back(pMetaFileNode);
      Stack.push(pMetaFileNode);
      #ifdef ML_LOGTOCONSOLE
      SM_Main::OutputConsole("<%s>\n", NewToken.m_pszString);
      #endif

      break;
    case MetaFileToken::E_TAGEND:      
      if (!Stack.size() || (strcmp(Stack.top()->m_Token.m_pszString, pMetaFileNode->m_Token.m_pszString) &&
                            pMetaFileNode->m_Token.m_pszString[0]))
      {
        goto HASERROR;
      }         
      delete pMetaFileNode;
      Stack.pop();
      #ifdef ML_LOGTOCONSOLE
      SM_Main::OutputConsole("</%s>\n", NewToken.m_pszString);
      #endif

      break;
    case MetaFileToken::E_INT:
      Stack.top()->m_Sons.push_back(pMetaFileNode);
      
      #ifdef ML_LOGTOCONSOLE
      SM_Main::OutputConsole("INT(%i)\n", NewToken.m_iValue);
      #endif
      
      break;
    case MetaFileToken::E_FLOAT:
      Stack.top()->m_Sons.push_back(pMetaFileNode);
      #ifdef ML_LOGTOCONSOLE
      SM_Main::OutputConsole("FLOAT(%f)\n", NewToken.m_fValue);
      #endif

      break;
    case MetaFileToken::E_STRING:
      Stack.top()->m_Sons.push_back(pMetaFileNode);
      #ifdef ML_LOGTOCONSOLE
      SM_Main::OutputConsole("STRING(\"%s\")\n", NewToken.m_pszString);
      #endif

      break;
    }
  }

  // We take out root
  Stack.pop();

  if (Stack.size())
  {
    // Bad formed file
    assert(!"Bad formed file");    
    goto HASERROR;
  }

  return (0); 

HASERROR:
  Shutdown();

  return (-1);
}

void MetaFile::DumpTree(MetaFileNode* pMetaFileNode)
{

}


int MetaFile::Shutdown()
{
  delete m_pRoot;
  m_pRoot=0;
  return (0);
}