/***************************************************************************
 *   Copyright (C) 2007 by tr                                              *
 *   tr_1980@mail.ru                                                       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
 

/*
  Утилиты парсинга
  v. 0.543
*/


#include "parsertools.h"

#include <cstdlib>
//#include <cassert>

SrcReader::SrcReader() {
        errorFlag = false;
        // символы пустого пространства
        strSpacesChars = " \t\x0a\x0d";
        // символы, считающиеся разделителями языка
        strDelimsChars = ";,{}()=";     
        // символы, из которых состоят идентификаторы   
        strWordChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; 
        
}



SrcReader::~SrcReader() {
//      cout << "closing " << iFileName << endl;
        iFile.close();
}



bool SrcReader::Open(const string &aFileName) {
        iFileName = aFileName;
        iFile.open(aFileName.c_str(), ios::in);         
        errorFlag = false;
        iLine = 0;
        lastReadedLine = true;
        if ( !iFile.is_open() ) 
                return false;           
        return true;
}



void SrcReader::AddEndLineComment(const string &aComment) {
        iEndLineComments.push_back(aComment);
}



bool SrcReader::ReadLine(string &aStr) {
  size_t len;   
  lastReadedLine = true;
  if ( errorFlag ) 
          return false;
  do {
    if (iFile.eof()) {
                aStr = "";      
      return false;
    }  
    getline(iFile, aStr);
    iLine++;
    // удаляем комментарии
         for (unsigned int i = 0; i < iEndLineComments.size(); i++) {
                 size_t pos = aStr.find_first_of(iEndLineComments[i]);
                 if ( pos != string::npos )
                        aStr = aStr.substr(0, pos);
         }
    // удаляем начальные и конечные пробелы
         aStr = Trim(aStr);
    len = aStr.length();
  } while ( len == 0 );
  return ( len != 0 );
}


bool SrcReader::ReadWord(string &aStr) {
        bool result = __ReadWord(aStr);
        if ( !result)
                return false;
        bool commentStarted = false;
        // смотрим, не начинается ли слова с признака комментария
        for (size_t i = 0; i < iEndLineComments.size(); i++) {
                 size_t pos = aStr.find_first_of(iEndLineComments[i]);
                 if ( pos == 0 ) {
                        commentStarted = true;
                        break;
                 }
         }
        // если комментарий был, то считываем строку до конца
        if ( !commentStarted ) 
                return true;
        
        // читаем до конца строки
        while ( !iFile.eof() ) {
                char ch;                
                iFile.get(ch);          
                if ( ch == 0x0a ) {
                        iLine++;
                        break;
                }
        }       
        return ReadWord(aStr);
}


bool SrcReader::__ReadWord(string &aStr) {
        aStr = "";
        lastReadedLine = false;
  if ( errorFlag ) 
          return false; 
        bool firstAmpersand = false;
    while ( !iFile.eof() ) {
                char ch;
                
                iFile.get(ch);          
                if ( ch == 0x0a )
                        iLine++;
                if (iFile.eof() ) {
                        iLine--;
                        return (aStr != "");
                }
                // пустое пространство
                if (!firstAmpersand && (strSpacesChars.find(ch) != string::npos) ) {
                        if ( aStr == "" )
                                continue;
                        else
                                return true; 
                // символ "
                } else if ( ch == '"' ) {
                        if ( aStr == "" ) {
                                aStr = '"';
                                firstAmpersand = true;
                                continue;
                        } else if (aStr[aStr.length()-1] != '\\' ) {
                                aStr += '"';
                                return true;
                        } else {
                                aStr[aStr.length()-1] = '"';
                                continue;
                        }               
                // отдельные символы
                } else if ( !firstAmpersand && (strDelimsChars.find(ch) != string::npos) ) {
                        if (aStr == "") 
                                aStr = ch;
                        else
                                iFile.seekg(-1, ios::cur);
                                return true;
                } else {
                        aStr += ch;
                }

    }  
        if (aStr == "")
                return false;
        else
                return true;    
}


string SrcReader::ReadWord() {
        string result = "";
        if ( !ReadWord(result) ) 
                Error("unexpected end of file");                        
        return result;
}



void SrcReader::Error(const string &aMessage) {
        errorFlag = true;       
        int line = lastReadedLine ? (iLine) : (iLine + 1);
        if ( line > 0 )
                
                cout << "ERROR: " << iFileName << ":" << line << " -> " << aMessage << endl;
        else
                cout << "ERROR: " << iFileName << " -> " << aMessage << endl;
}



int SrcReader::GetInt(const string &aStr) {
        string str = Trim(aStr);        
        str = Trim(str);        
        if ( !VerifyInt(str) ) 
                Error("invalid integer: "+str);                 
        return StrToInt(str);
}


// считывает слово, если оно не совпадает с переданным, выдает ошибку и возвражает false
// если aIgnoreCase установлен, игнорирует регистр 

bool SrcReader::VerifyWord(const string &aStr, bool aIgnoreCase) {
        string str = ReadWord();
        bool result = aIgnoreCase ? (StrUpper(str) == StrUpper(aStr)) : (str == aStr);
        if ( !result )
                Error("\"" + aStr + "\" expected");
        return result;
}



// true - если слово прочитано, иначе - конец строки
bool StringReader::ReadWord(string &aStr) {
    aStr = "";
        
    bool firstAmpersand = false;
    while ( pos <  str.length() ) {
                char ch = str[pos++];

                // пустое пространство
                if (!firstAmpersand && (strSpacesChars.find(ch) != string::npos) ) {
                        if ( aStr == "" )
                                continue;
                        else
                                return true; 
                // символ "
                } else if ( ch == '\'' ) {
                        if ( aStr == "" ) {
                                aStr = '\'';
                                firstAmpersand = true;
                                continue;
                        } else if (aStr[aStr.length()-1] != '\\' ) {
                                aStr += '\'';
                                return true;
                        } else {
                                aStr[aStr.length()-1] = '\'';
                                continue;
                        }               
                // отдельные символы
                } else if ( !firstAmpersand && (strDelimsChars.find(ch) != string::npos) ) {
                        if (aStr == "") 
                                aStr = ch;
                        else
                                pos--;
                                return true;
                } else {
                        aStr += ch;
                }

    }  
        if (aStr == "")
                return false;
        else
                return true;    
};



BinReader::BinReader() {
}



BinReader:: ~BinReader() {
        iFile.close();
}



bool BinReader::Open(const string &aFileName) {
        iFile.open(aFileName.c_str(), ios::in | ios::binary);            
   return iFile.is_open();
}



void BinReader::Read(const void *aP, const int aSize) {
        iFile.read((char*)(aP), aSize);
}



unsigned int BinReader::ReadByte() {
        int res = 0;
        iFile.read((char*)(&res), 1);
        return res;
}



unsigned int BinReader::ReadWord() {
        int res = 0;
        iFile.read((char*)(&res), 2);
        return res;
}



unsigned int BinReader::ReadDword() {
        int res = 0;
        iFile.read((char*)(&res), 4);
        return res;
}



string BinReader::ReadString(const int aLen) {
        int len = 0;
        iFile.read((char*)(&len), aLen);
        string res;
        for (int i = 0; i < len; i++)
                res += (char)(ReadByte());
        return res;
}



bool BinReader::eof() {
        return iFile.eof();
}



unsigned int BinReader::size() {
        streampos startPos = iFile.tellg();
        iFile.seekg(0, ios::end);
        unsigned int res = iFile.tellg();
        iFile.seekg(startPos, ios::beg);
        return res;
}



BinWriter::BinWriter() {
}



BinWriter::~BinWriter() {
        iFile.close();  

}



bool BinWriter::Open(const string &aFileName) {
        iFile.open(aFileName.c_str(), ios::out | ios::binary);            
   return iFile.is_open();
}



void BinWriter::Write(const void *aP, const int aSize) {
        iFile.write((char*)(aP), aSize);
}



void BinWriter::WriteByte(const unsigned int aVal) {
        iFile.write((char*)(&aVal), 1);
}


void BinWriter::Write3Byte(const unsigned int aVal) {
        iFile.write((char*)(&aVal), 3);
}




void BinWriter::WriteWord(const unsigned int aVal) {
        iFile.write((char*)(&aVal), 2);
}



void BinWriter::WriteDword(const unsigned int aVal) {
        iFile.write((char*)(&aVal), 4);
}



void BinWriter::WriteString(const string &aStr, const int aLen) {
  size_t len = aStr.length();
  iFile.write((char*)(&len), aLen);
  iFile.write(aStr.c_str(), len);
}



// удаляет начальные и конечные символы пустого пространства в строке
string Trim(const string &aStr) {
  if ( aStr.length() == 0 )
     return aStr;
  size_t beg = aStr.find_first_not_of(" \a\b\f\n\r\t\v");
  size_t end = aStr.find_last_not_of(" \a\b\f\n\r\t\v");
        // если строка содержит одни пробелы
        if ( beg == string::npos )
                return "";
//	return string(aStr, beg, end+1); !!!!!!!!!!!!!!!!!!!
        return string(aStr, beg, end+1-beg);
}



// Возвращает число слов с указанным разделителем в строке
int WordCount(const string &aStr, const string &aWordDelims) {
   int res = 0;
   int i = 0;
   int len = (int)aStr.length();
        while (i < len) {
                while ( (i < len) && (aWordDelims.find(aStr[i]) != string::npos) )
                        i++;      
                if (i < len) 
                        res++;      
                while ( (i < len) && (aWordDelims.find(aStr[i]) == string::npos) )
                        i++;
        };
        return res;
}





int WordPosition(const int aN, const string &aStr, const string &aWordDelims) {
        int count = 0;
        unsigned int i = 0;
        int res = -1;
        while ( (i < aStr.length()) && (count != aN) ) {
                // пропускаем конечные разделители
                while ( (i <= aStr.length()) && (aWordDelims.find(aStr[i]) != string::npos) )
                        i++;
                // если мы конец строки не достигнут, то это начало слова
                if ( i < aStr.length() ) 
                        count++;
                // если не достигли конца строки, то ищем конец текущего слова
                if ( count != aN) 
                        while ( (i < aStr.length()) && (aWordDelims.find(aStr[i]) == string::npos) )
                                i++;
                else
                        res = i;
        };
        return res;
}



// Возвращает указанное слово из строки
string ExtractWord(const int aN, const string &aStr, const string &aWordDelims) {
        int len = 0;
        size_t i = WordPosition(aN, aStr, aWordDelims);
        string res("");
        if (i >= 0)  {
                // поиск конца текущего слова
                while ( (i < aStr.length() ) && (aWordDelims.find(aStr[i]) == string::npos) ) {
                        // добавляем aStr[i] к результату
                        len++;
                        res += aStr[i];
                        i++;
                };
        }
        return res;
}


// удаляет символы кавычек, в которые заключено слово
// возвращает true, если кавычки были найдены и удалены
bool DeleteApostrof(string &aStr) {
        if ( aStr.length() < 2 ) 
                return false;
        if ( (aStr[0] == '"') && (aStr[aStr.length()-1] == '"') ) {
                aStr = aStr.substr(1, aStr.length()-2);
                return true;
        }
        if ( (aStr[0] == '\'') && (aStr[aStr.length()-1] == '\'') ) {
                aStr = aStr.substr(1, aStr.length()-2);
                return true;
        }
        return false;
}






// проверяет, что строка является представлением целого
bool VerifyInt(const string &aStr) {
        if ( aStr.length() == 0)
                return false;
        char *stopstring;
        strtol(aStr.c_str(), &stopstring, 10);
        return ( stopstring[0] == 0 );  
}


// проверяет, что строка является представлением вещественного числа
bool VerifyFloat(const string &aStr) {
        if ( aStr.length() == 0)
                return false;
        char *stopstring;
        strtod(aStr.c_str(), &stopstring);
        return ( stopstring[0] == 0 );  
}


// конвертирует hex представления байта (2 символа!) в его значение
// возвращает -1/-2 в случае ошибки 
int HexToByte(const string &aStr) {
        string loCase = StrLower(aStr);
        unsigned int hi, lo;
        char chHi = loCase[0];
        char chLo = loCase[1];
        if ( (chHi >= '0') && (chHi <= '9') ) 
                hi =  chHi - '0';
        else if ( (chHi >= 'a') && (chHi <= 'f') ) 
                hi = chHi - 'a' + 10;
        else
                return -1;
        if ( (chLo >= '0') && (chLo <= '9') ) 
                lo =  chLo - '0';
        else if ( (chLo >= 'a') && (chLo <= 'f') ) 
                lo = chLo - 'a' + 10;
        else
                return -2;
        return (hi << 4) + lo;
}



// конвертирует байт в hex-представление
string ByteToHex(int aVal) {
        string str = "";
        int hi = (aVal >> 4) & 0x0F;
        int lo = aVal & 0x0F;
        if ( hi < 10 )
                str += (char)(hi+'0');
        else
                str += (char)(hi+'A'-10);
        if ( lo < 10 )
                str += (char)(lo+'0');
        else
                str += (char)(lo+'A'-10);
        return str;
}



// проверяет, что строка является представлением даты в формате "DD.MM.YYYY"
bool VerifyDate(const string &aStr) {
        tm date = StrToDate(aStr);
        return ( date.tm_year > 0 ) && ( date.tm_mon > 0) && (date.tm_mday > 0);
}



// проверяет, что строка является представлением времени в формате "HH:MM:SS"
bool VerifyTime(const string &aStr) {
        tm time = StrToTime(aStr);
        return ( time.tm_hour >= 0 ) && ( time.tm_min >= 0) && (time.tm_sec >= 0);
}



// проверяет, что строка является представлением даты и времени в формате "DD.MM.YYYY HH:MM:SS"
bool VerifyDateTime(const string &aStr) {
        tm datetime = StrToDateTime(aStr);
        return ( datetime.tm_year > 0 ) && ( datetime.tm_mon > 0) && (datetime.tm_mday > 0) && ( datetime.tm_hour >= 0 ) && ( datetime.tm_min >= 0) && (datetime.tm_sec >= 0);
}



// парсит строку даты
tm StrToDate(const string &aStr) {
     tm date;
        string str = Trim(aStr);
        memset(&date, 0, sizeof(tm));
        date.tm_mday = -1;
        date.tm_mon = -1;
        date.tm_year = -1;       

        if ( WordCount(str, ".") != 3)
           return date;
        int y = -1, m = -1, d = -1;
        string s;
        s = ExtractWord(1, str, ".");
        if ( !VerifyInt(s) )            
           return date;
        d = StrToInt(s);

        s = ExtractWord(2, str, ".");
        if ( !VerifyInt(s) )            
           return date;
        m = StrToInt(s);

        s = ExtractWord(3, str, ".");
        if ( !VerifyInt(s) )            
            return date;
        y = StrToInt(s);

        date.tm_mday = d;
        date.tm_mon = m;
        date.tm_year = y;       
        return date;
}



// парсит строку времени
tm StrToTime(const string &aStr) {
        tm time;        
        memset(&time, 0, sizeof(time));
        time.tm_hour = -1;
        time.tm_min = -1;
        time.tm_sec = -1;        

        string str = Trim(aStr);        
        if ( ( WordCount(str, ":") != 3 ) && ( WordCount(str, ":") != 2 ) )
                return time;
        int h, m, s;
        string ss;
        ss = ExtractWord(1, str, ":");          
        if ( !VerifyInt(ss) ) 
                return time;
        h = StrToInt(ss);

        ss = ExtractWord(2, str, ":");  
        if ( !VerifyInt(ss) ) 
                return time;
        m = StrToInt(ss);

        if ( WordCount(str, ":") != 2 ) {
                ss = ExtractWord(3, str, ":");          
                if ( !VerifyInt(ss) ) 
                        return time;
                s = StrToInt(ss);
        } else
                s = 0;

        time.tm_hour = h;
        time.tm_min = m;
        time.tm_sec = s;        
        return time;
}


// парсит строку даты/времени
tm StrToDateTime(const string &aStr) {  
        tm datetime;
        memset(&datetime, 0, sizeof(tm));
        if ( WordCount(aStr, " \t") != 2)
                return datetime;
        string strD = ExtractWord(1, aStr, " \t");
        string strT = ExtractWord(2, aStr, " \t");
        datetime = StrToDate(strD);
        tm tmT = StrToTime(strT);
        datetime.tm_hour = tmT.tm_hour;
        datetime.tm_min = tmT.tm_min;
        datetime.tm_sec = tmT.tm_sec;
        return datetime;
}


