#include <apps.h> // Первым! Приложение!
#include <mem.h>
#include <tadbg.h>
#include <utilities.h>
#include "xml_parser.h"

int IsSpace (char c){
	return	c == ' ' || c == '\n' || c == '\r' || c == '\t';
}

int IsLit (char c){
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

int IsNum (char c){
	return (c >= '0' && c <= '9');
}

int InName (char c){
  return IsLit(c) || IsNum(c) || c == '_' || c == '-' || c == ':';
}


 XMLNode *XMLTree=0;
 XMLAttr *curattr=0;
 int *TreeBranchs;
 int BranchPos=0;
 XMLNode *current = 0;
 XMLNode *tmp=0;
 XMLAttr *attribs=0;
 int isClosed=0;
 char *TagName;					
 char *AttrName;				
 char *AttrValue;				
 char *Text;
 int TagState;
 int MSState;

// Структура, описывающая, что на что менять
typedef struct
{
  char mask[7];
  char replace;
}REPL_ARRAY;

// Сами замены и их количество
const int Repl_chars_count = 5;
REPL_ARRAY Repl_chars[] = {
						   "&apos;\0",		'\'',
                           "&quot;\0",		'"',
                           "&lt;\0\0\0", 	'<',
                           "&gt;\0\0\0", 	'>',
                           "&amp;\0\0", 	'&'
};						   
 
/*
    Получить спецсимвол по его маске
IN: mask_begin - строка символов
    out_ofs - число, к которому прибавится длина обработанной последовательности
OUT: out_ofs - смещение в строке, откуда начинаются необработанные данные
    <return> - спецсимвол
*/
char GetSpecialSym(char *mask_begin, int *out_ofs)
{
  int i=0;
  int replen;
  char rep_ex[10];
  if(*mask_begin!='&')return *(mask_begin);
  for(i=0;i<Repl_chars_count;i++)
  {
    replen = strlen(Repl_chars[i].mask);  // Определяем длину очередной маски
    strncpy(rep_ex, mask_begin, replen);    // Копируем строку такой длины с текущей позиции
    if(!strcmp(rep_ex,Repl_chars[i].mask))// Если совпало с очередной маской
    {
      *out_ofs+=replen-1;                   // Увеличиваем обработанную длину на длину маски
      return Repl_chars[i].replace;       // Возвращаем символ для замены
    }
  }
  return *(mask_begin);       //  Масок не нашлось, возвращаем как есть
}

char * Replace_Special_Syms(char * unrep_str)
{
  int unrep_len=strlen(unrep_str);
  char *rep_buffer = malloc(unrep_len+1);
  char tmp=0;
  int rpl_c=0;
  int j=0;
  for(j=0; j<unrep_len;j++)
  {
    tmp = *(unrep_str+j);
    tmp = GetSpecialSym(unrep_str+j, &j);
    //ShowMSG(1,(int)"fnd");
    rep_buffer[rpl_c++]=tmp;
  }
  //dbgf("AttrValue: %s, len : %d", AttrValue, strlen(AttrValue) );
  //dbgf("rep_buffer: %s, len : %d", AttrValue, strlen(rep_buffer) );
  //rep_buffer = realloc(rep_buffer,rpl_c+1);
  return rep_buffer;
}


void EndTag(void){
  if((TagState == TS_NORMAL)||(TagState == TS_EMPTY)){
    tmp=malloc(sizeof(XMLNode));
    memclr(tmp,sizeof(XMLNode));
    if (!XMLTree){
      XMLTree=tmp;
    }else{
      if(!isClosed)current->next=tmp;
      else current->subnode=tmp;
    }
    tmp->name=malloc(strlen(TagName)+1);
    if(attribs)tmp->attr=attribs;
    attribs=0;
    strcpy(tmp->name,TagName);
    *(TreeBranchs+BranchPos)=(int)tmp;
    BranchPos++;
    current=tmp;
    curattr=0;
    isClosed=1;
  }
  if((TagState == TS_EMPTY)||(TagState == TS_CLOSE)){
    if(BranchPos>0){
      BranchPos--;
      current=(void*)(*(TreeBranchs+BranchPos));
    }
    isClosed=0;
  }
}

void EndAttr(void){
  XMLAttr *tmp2=malloc(sizeof(XMLAttr));
  memclr(tmp2,sizeof(XMLAttr));
  tmp2->name=malloc(strlen(AttrName)+1);
  strcpy(tmp2->name,AttrName);
  if(strlen(AttrValue)>0){
    /*char *newattrval = Replace_Special_Syms(AttrValue);
   tmp2->param=malloc(strlen(newattrval)+1);
   strcpy(tmp2->param, newattrval);
   mfree(newattrval);*
   */
   tmp2->param=malloc(strlen(AttrValue)+1);
   strcpy(tmp2->param, AttrValue);
  }
  if(!curattr){
    attribs=tmp2;
  }else{
    curattr->next=tmp2;
  }
  curattr=tmp2;
}

void DestroyTree(XMLNode *tmpp){
 while(tmpp){
   XMLAttr *ta=tmpp->attr;
   while(ta){
       tmpp->attr=ta->next;
       mfree(ta->name);
       mfree(ta->param);
       mfree(ta);
       ta=tmpp->attr;
   }
   if(tmpp->subnode){
     DestroyTree(tmpp->subnode);
   }
   XMLNode *tmpp2=tmpp->next;
   mfree(tmpp->name);
   mfree(tmpp->value);
   mfree(tmpp);
   tmpp=tmpp2;
 }
 
 XMLTree=0;
 curattr=0;
 BranchPos=0;
 current = 0;
 tmp=0;
 attribs=0;
 isClosed=0; 
}

void Finish(void){
 mfree(TagName);
 mfree(AttrName);
 mfree(AttrValue);
 mfree(Text);
 mfree(TreeBranchs);
}

void *XMLDecode(char *buf, int size){

 if( !buf, !size ) return NULL;
 
 TagName=malloc(256);					
 AttrName=malloc(256);				
 AttrValue=malloc(256);				
 TreeBranchs=malloc(1024);
 Text=malloc(size);
 
 memclr(TagName,256);
 memclr(AttrName,256);
 memclr(AttrValue,256);
 memclr(Text,size);
 MSState = MS_BEGIN;
 TagState = TS_INDEFINITE;
 char sh_str[2]=".\0";
 int i = 0, len = 0;

	while ((i<size)){
            char c = *(buf+i);
            sh_str[0]=c;
            
	    switch (MSState){
		case MS_BEGIN:
			if (c == '<'){
				MSState = MS_BEGINTAG;
			}else if (!IsSpace (c)){
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_BEGINTAG:
                        strcpy(AttrName,"");
			if (IsLit (c)){
				MSState = MS_TAGNAME;
				TagState = TS_NORMAL;
                                strcpy(TagName,sh_str);

			}else if (IsSpace(c)){
				MSState = MS_MIDDLETAG;
				TagState = TS_NORMAL;
			}else if (c == '?'){
				TagState = TS_DECLARATION;
				MSState = MS_MIDDLETAG;
			}else if (c == '/'){
				TagState = TS_CLOSE;
				MSState = MS_MIDDLETAG;
			}else{
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_MIDDLETAG:
			if (IsLit (c)){
				MSState = MS_TAGNAME;
				strcpy(TagName,sh_str);
			}else if (!IsSpace (c)){
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_TAGNAME:
			if (InName (c)){
				strcat(TagName,sh_str);
			}else if (IsSpace(c)){
				MSState = MS_ENDTAGNAME;
			}else if (c == '/'){
				MSState = MS_ENDTAG;
				TagState = TS_EMPTY;
			}else if (c == '>'){
				MSState = MS_TEXT;
				EndTag();
			}else{
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_ENDTAGNAME:
			if (IsLit (c)){
				MSState = MS_ATTRIBNAME;
				strcpy(AttrName,sh_str);
			}else if (c == '/'){
				MSState = MS_ENDTAG;
				TagState = TS_EMPTY;
			}else if (c == '>'){
				MSState = MS_TEXT;
				EndTag();
			}else if (!IsSpace(c) && (c != '?' && TagState != TS_DECLARATION)){
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_ATTRIBNAME:
			if (InName(c)){
				strcat(AttrName,sh_str);
			}else if (c == '='){
				MSState = MS_ENDEQUALLY;
			}else if (IsSpace(c)){
				MSState = MS_ENDATTRIBNAME;
			}else{
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

                case MS_ENDATTRIBNAME:
			if (c == '='){
				MSState = MS_ENDEQUALLY;
			}else if (!IsSpace(c)){
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_ENDEQUALLY:
			if ((c == '\"')||(c == '\'')){
				MSState = MS_ATTRIBVALUE;
				strcpy(AttrValue,"");
				len = 0;
			}else if (!IsSpace(c)){
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_ATTRIBVALUE:
			if ((c == '\"')||(c == '\'')){
				MSState = MS_ENDTAGNAME;
				AttrValue[len] = '\0';
				len = 0;
				EndAttr();
			}else{
				strcat(AttrValue,sh_str);
				len ++;
			}
			break;

		case MS_ENDTAG:
			if (c == '>'){
				MSState = MS_TEXT;
				EndTag();
			}else{
				Finish();
				return XMLTree;//0;
                                //EndParse = 1;
			}
			break;

		case MS_TEXT:

			if (c == '<'){
                          if(strlen(Text)>0){
                            tmp->value=malloc(strlen(Text)+1);
                            strcpy(tmp->value,Text);
                            //tmp->value = Replace_Special_Syms(Text);
                          }
                          strcpy(Text, "");
                          MSState = MS_BEGINTAG;
                          TagState = TS_INDEFINITE;
			}else{
                          if((TagState == TS_NORMAL)) strcat(Text, sh_str);
                        }
			break;
		}
            i++;
	}

	//if (!EndParse) return 1;
        //{
//		Success();
//	}

	Finish();
        return XMLTree;//
}


/*
  Получить значение атрибута по его имени
IN: char* req_attr_name - название атрибута
    XMLAttr* attr_list  - список атрибутов тега
OUT: значение атрибута или NULL
*/
char* XML_Get_Attr_Value(char* req_attr_name, XMLAttr* attr_list)
{
  XMLAttr* attr_Ex = attr_list;
  while(attr_Ex)
  {
    if(attr_Ex->name)
      if(!strcmp(req_attr_name, attr_Ex->name)) return attr_Ex->param;
    attr_Ex = attr_Ex->next;
  }
  return NULL;
}

/*
  Получить дочерний узел из списка дочерних узлов по его имени
IN: XMLNode* node         - родительский узел
    char* req_node_name   - имя требуемого узла
OUT: дочерний узел или NULL
*/
XMLNode* XML_Get_Child_Node_By_Name(XMLNode* node, char* req_node_name)
{
  XMLNode* nodeEx = node->subnode;
  while(nodeEx)
  {
    if(nodeEx->name)
      if(!strcmp(req_node_name, nodeEx->name)) return nodeEx;
    nodeEx = nodeEx->next;
  }
  return NULL;
}

/*
  Получить дочерний узел из списка дочерних узлов по его имени,
  при условии, что существует заданный атрибут с заданным значением
IN: XMLNode* node         - родительский узел
    char* req_node_name   - имя требуемого узла
    char* req_attr_name   - имя атрибута
    char* req_attr_velue  - значение атрибута
OUT: дочерний узел или NULL
*/
XMLNode* XML_Get_Child_Node_By_Name_And_Attr(XMLNode* node, char* req_node_name, char* req_attr_name, char* req_attr_velue)
{
  XMLNode* nodeEx = node->subnode;
  while(nodeEx)
  {
    if(nodeEx->name)
      if(!strcmp(req_node_name, nodeEx->name))    // Если найден нод с нужным именем
      {
        char *attr_val = XML_Get_Attr_Value(req_attr_name, nodeEx->attr);
        if(attr_val)    // Если есть требуемый атрибут
        {
          if(!strcmp(attr_val, req_attr_velue))return nodeEx;          
        }
      }
    nodeEx = nodeEx->next;
  }
  return NULL;
}
//EOL,EOF
