/*
 * This file is part of the FortranProject plugin for Code::Blocks IDE
 * and licensed under the GNU General Public License, version 3
 * http://www.gnu.org/licenses/gpl-3.0.html
 */

#include <wx/file.h>
#include "tokenizerf.h"

bool ReadFileToString(wxFile& file,wxString& st)
{
    st.Empty();
    if (!file.IsOpened())
        return false;
    int len = file.Length();
    if(!len)
    {
        file.Close();
        return true;
    }
#if wxUSE_UNICODE
    char* buff = new char[len+1];
    if (!buff)
    {
        file.Close();
        return false;
    }
    file.Read((void*)buff, len);
    file.Close();
    buff[len]='\0';

    st = wxString((const char *)buff, wxConvUTF8);
    if (st.Length() == 0)
    {
        // could not read as utf-8 encoding, try iso8859-1
        st = wxString((const char *)buff, wxConvISO8859_1);
    }
    delete[] buff;

#else
    char* buff = st.GetWriteBuf(len); // GetWriteBuf already handles the extra '\0'.
    file.Read((void*)buff, len);
    file.Close();
    st.UngetWriteBuf();
#endif
    return true;
}

///---------------------------------------

Tokenizerf::Tokenizerf(const wxString& filename, FortranSourceForm sourceForm)
    : m_Filename(filename),
      m_BufferLen(0),
      m_TokenIndex(0),
      m_UndoTokenIndex(0),
      m_PeekedTokenIndex(0),
      m_LineNumber(1),
      m_LineNumberStart(1),
      m_UndoLineNumber(1),
      m_UndoLineNumberStart(1),
      m_PeekedLineNumber(1),
      m_PeekedLineNumberStart(1),
      m_Column(1),
      m_UndoColumn(1),
      m_PeekedColumn(1),
      m_WasNextLine(false),
      m_UndoWasNextLine(false),
      m_PeekedWasNextLine(false),
      m_WasPeeked(false),
      m_IsOK(false),
      m_SourceForm(sourceForm),
      m_PeekedToken(),
      m_DetailedParsing(false)
{
    if (!m_Filename.IsEmpty())
        Init(m_Filename, m_SourceForm);
    m_LineStartIdx.push_back(0);
}

Tokenizerf::~Tokenizerf()
{
}

bool Tokenizerf::Init(const wxString& filename, FortranSourceForm sourceForm)
{
    BaseInit();
    if (filename.IsEmpty())
    {
        if (m_Filename.IsEmpty())
            return false;
    }
    else
        m_Filename = filename;

    if (!wxFileExists(m_Filename))
        return false;

    if (!ReadFile())
        return false;

    if (!m_BufferLen)
        return false;

    m_SourceForm = sourceForm;
    AdjustLineNumber();

    m_IsOK = true;
    return true;
}

bool Tokenizerf::InitFromBuffer(const wxString& buffer, FortranSourceForm sourceForm)
{
    BaseInit();
    m_Buffer = buffer;
    m_BufferLen = buffer.Length();
    m_IsOK = true;
    m_Filename.Clear();
    m_SourceForm = sourceForm;
    AdjustLineNumber();
    return true;
}

void Tokenizerf::BaseInit()
{
    m_TokenIndex = 0;
    m_UndoTokenIndex = 0;
    m_PeekedTokenIndex = 0;
    m_LineNumber = 1;
    m_UndoLineNumber = 1;
    m_PeekedLineNumber = 1;
    m_LineNumberStart = 1;
    m_UndoLineNumberStart = 1;
    m_PeekedLineNumberStart = 1;
    m_Column = 1;
    m_UndoColumn = 1;
    m_PeekedColumn = 1;
    m_BufferLen = 0;
    m_Buffer.Clear();
    m_IsOK = false;
    m_WasNextLine = false;
    m_UndoWasNextLine = false;
    m_PeekedWasNextLine = false;
    m_WasPeeked = false;
    m_LineStartIdx.clear();
    m_LineStartIdx.push_back(0);
}

bool Tokenizerf::ReadFile()
{
    if (!wxFileExists(m_Filename))
        return false;

    // open file
    wxFile file(m_Filename);

    //if (!cbRead(file,m_Buffer))
    if (!ReadFileToString(file,m_Buffer))
    {
        return false;
    }
    m_BufferLen = m_Buffer.Length();

    return true;
}

wxChar Tokenizerf::CurrentChar()
{
    return m_Buffer.GetChar(m_TokenIndex);
}

wxChar Tokenizerf::NextChar()
{
    if ((m_TokenIndex + 1) < 0 || (m_TokenIndex + 1) >= m_BufferLen)
        return 0;
    return m_Buffer.GetChar(m_TokenIndex + 1);
}

wxChar Tokenizerf::PreviousChar()
{
    if ((m_TokenIndex - 1) < 0 || (m_TokenIndex - 1) >= m_BufferLen)
        return 0;
    return m_Buffer.GetChar(m_TokenIndex - 1);
}

void Tokenizerf::AdjustLineNumber()
{
    if (m_WasNextLine)
    {
        ++m_LineNumber;
        m_WasNextLine = false;
        if (m_LineStartIdx.size() < m_LineNumber)
        {
            m_LineStartIdx.push_back(m_TokenIndex);
        }
    }
    if (CurrentChar() == '\n')
    {
        m_WasNextLine = true;
        m_Column = 0;
    }
}

bool Tokenizerf::MoveToNextChar()
{
    ++m_TokenIndex;
    ++m_Column;
    if (!IsEOF())
    {
        AdjustLineNumber();
        return true;
    }
    return false;
}

bool Tokenizerf::SkipWhiteSpace()
{
    // skip spaces, tabs, etc.
    while (!IsEOF() && isspace(CurrentChar()))
        MoveToNextChar();
    if (IsEOF())
        return false;
    return true;
}

bool Tokenizerf::SkipToChar(const wxChar& ch, bool toLineEnd)
{
    // skip everything until we find ch
    while (1)
    {
        while (!IsEOF() && CurrentChar() != ch && (!toLineEnd || (toLineEnd && CurrentChar() != '\n')))
            MoveToNextChar();
        break;
    }
    if (IsEOF())
        return false;
    return true;
}

bool Tokenizerf::CharInString(const char ch, const char* chars)
{
    int len = strlen(chars);
    for (int i = 0; i < len; ++i)
    {
        if (ch == chars[i])
            return true;
    }
    return false;
}


bool Tokenizerf::SkipToOneOfChars(const char* chars, bool toLineEnd)
{
    m_WasPeeked = false;
    // skip everything until we find any one of chars or end of line if toLineEnd
    while (1)
    {
        wxChar cch = CurrentChar();
        if(toLineEnd && cch == '\n')
        {
            break;
        }
        if(cch == '&')
        {
            MoveToNextChar();
            SkipWhiteSpace();
            cch = CurrentChar();
            if (cch == '\n' || cch == '\r' || cch == '!')
            {
                SkipToEOL();
                cch = CurrentChar();
            }
        }
        if(toLineEnd && cch == '!')
        {
            SkipToEOL();
            break;
        }
        else if(cch == '!')
        {
            SkipToEOL();
            cch = CurrentChar();
        }
        if(IsEOF() || CharInString(cch, chars))
        {
            break;
        }
        else
        {
            if (cch == '"' || cch == '\'')
            {
                // this is the case that match is inside a string!
                char ch = CurrentChar();
                MoveToNextChar();
                SkipToChar(ch, true);
            }
            MoveToNextChar();
        }
    }
    if (IsEOF())
        return false;
    return true;
}

bool Tokenizerf::SkipToEOL()
{
    // skip everything until we find EOL
    while (1)
    {
        while (!IsEOF() && CurrentChar() != '\n')
            MoveToNextChar();
        break;
    }
    if (IsEOF())
        return false;
    return true;
}

bool Tokenizerf::SkipBlock(const wxChar& ch, int maxLines)
{
    // skip blocks () [] {} <>
    wxChar match;
    switch (ch)
    {
    case '(':
        match = ')';
        break;
    case '[':
        match = ']';
        break;
    case '{':
        match = '}';
        break;
    case '<':
        match = '>';
        break;
    default :
        return false;
    }

    MoveToNextChar();
    int n_lines = 1;
    int count = 1; // counter for nested blocks (xxx())
    bool wasCont= false;
    while (!IsEOF())
    {
        while (!IsEOF())
        {
            if (CurrentChar() == '"' || CurrentChar() == '\'')
            {
                // this is the case that match is inside a string!
                char chOne = CurrentChar();
                MoveToNextChar();
                SkipToChar(chOne, true);
                MoveToNextChar();
            }
            else
                break;
        }
        if (CurrentChar() == ch)
            ++count;
        else if (CurrentChar() == match)
            --count;
        else if (CurrentChar() == '&' && m_SourceForm == fsfFree && !wasCont)
        {
            SkipToEOL();
            wasCont = true;
        }
        else if (maxLines > 0 && CurrentChar() == '\n')
        {
            n_lines++;
            if (n_lines > maxLines)
                count = 0;
        }
        else if (wasCont && m_SourceForm == fsfFree && !isspace(CurrentChar()))
            wasCont = false;

        MoveToNextChar();
        if (count == 0)
            break;
    }
    if (IsEOF())
        return false;
    return true;
}

bool Tokenizerf::SkipUnwanted()
{
    while ((CurrentChar() == '=' && !m_DetailedParsing) ||
            CurrentChar() == '!' ||
            ((CurrentChar() == 'c' || CurrentChar() == 'C' || CurrentChar() == '*') && m_Column == 1 && m_SourceForm == fsfFixed))
    {
        while ((CurrentChar() == 'c' || CurrentChar() == 'C' ||CurrentChar() == '*') && m_Column == 1 && m_SourceForm == fsfFixed)
        {
            if (IsBindTo())
                return true;
            SkipToEOL();
            if (!SkipWhiteSpace())
                return false;
        }
        while (CurrentChar() == '!')
        {
            if (IsBindTo())
                return true;
            SkipToEOL();
            if (!SkipWhiteSpace())
                return false;
        }

        while (CurrentChar() == '=')
        {
            if (NextChar() != '>')
            {
                MoveToNextChar();
                if (!SkipWhiteSpace())
                    return false;
                // skip assignments
                if (CurrentChar() == '[' || CurrentChar() == '(')
                    break;
                if (!SkipToOneOfChars(";", true))
                    return false;
                if (!SkipWhiteSpace())
                    return false;
            }
            else
            {
                return true;
            }
        }
    }
    return true;
}

wxString Tokenizerf::GetToken()
{
    m_UndoTokenIndex = m_TokenIndex;
    m_UndoLineNumber = m_LineNumber;
    m_UndoLineNumberStart = m_LineNumberStart;
    m_UndoColumn = m_Column;
    m_UndoWasNextLine = m_WasNextLine;
    if (m_WasPeeked)
    {
        m_TokenIndex = m_PeekedTokenIndex;
        m_LineNumber = m_PeekedLineNumber;
        m_LineNumberStart = m_PeekedLineNumberStart;
        m_Column = m_PeekedColumn;
        m_WasNextLine = m_PeekedWasNextLine;
        m_WasPeeked = false;
        return m_PeekedToken;
    }
    else
        return DoGetToken();
}

wxString Tokenizerf::GetTokenSameLine()
{
    unsigned int oldTokenIndex = m_TokenIndex;
    unsigned int oldLineNumber = m_LineNumber;
    unsigned int oldLineNumberStart = m_LineNumberStart;
    unsigned int oldColumn = m_Column;
    bool oldWasNextLine = m_WasNextLine;

    wxString token;

    if (m_WasPeeked)
    {
        m_TokenIndex = m_PeekedTokenIndex;
        m_LineNumber = m_PeekedLineNumber;
        m_LineNumberStart = m_PeekedLineNumberStart;
        m_Column = m_PeekedColumn;
        m_WasNextLine = m_PeekedWasNextLine;
        m_WasPeeked = false;
        token = m_PeekedToken;
    }
    else
        token = DoGetToken();


    if (oldLineNumber != m_LineNumberStart)
    {
        m_TokenIndex = oldTokenIndex;
        m_LineNumber = oldLineNumber;
        m_LineNumberStart = oldLineNumberStart;
        m_Column = oldColumn;
        m_WasNextLine = oldWasNextLine;
        token = wxEmptyString;
    }
    else
    {
        m_UndoTokenIndex = oldTokenIndex;
        m_UndoLineNumber = oldLineNumber;
        m_UndoLineNumberStart = oldLineNumberStart;
        m_UndoColumn = oldColumn;
        m_UndoWasNextLine = oldWasNextLine;
    }

    return token;
}

wxString Tokenizerf::GetTokenSameFortranLine()
{
    wxString token;

    if (m_SourceForm == fsfFree)
    {
        token = GetTokenSameLine();
        while (token.IsSameAs(_T("&")))
        {
            token = GetToken();
        }
    }
    else
    {
        token = PeekToken();
        if (m_LineNumberStart == m_PeekedLineNumberStart)
            token = GetToken();
        else
        {
            if ( (m_PeekedColumn >= 7 && (m_PeekedColumn - token.Length()) >= 7) ||
                    ((m_PeekedColumn - token.Length()) < 6) ||
                    (token.Mid((token.Length() - (m_PeekedColumn - 6)),1).IsSameAs(_("0"))) )
                token = wxEmptyString;
            else
            {
                token = GetToken();
                if (m_Column > 7)
                    token = token.Mid(token.Length() - (m_Column - 7));
                else
                {
                    token = PeekToken();
                    if (m_LineNumberStart == m_PeekedLineNumberStart)
                        token = GetToken();
                    else
                        token = wxEmptyString;
                }
            }
        }
    }
    return token;
}


wxString Tokenizerf::PeekToken()
{
    unsigned int undoTokenIndex = m_TokenIndex;
    unsigned int undoLineNumber = m_LineNumber;
    unsigned int undoLineNumberStart = m_LineNumberStart;
    unsigned int undoColumn = m_Column;
    bool undoWasNextLine = m_WasNextLine;

    m_PeekedToken = DoGetToken();

    m_WasPeeked = true;
    m_PeekedTokenIndex = m_TokenIndex;
    m_PeekedLineNumber = m_LineNumber;
    m_PeekedLineNumberStart = m_LineNumberStart;
    m_PeekedColumn = m_Column;
    m_PeekedWasNextLine = m_WasNextLine;

    m_TokenIndex = undoTokenIndex;
    m_LineNumber = undoLineNumber;
    m_LineNumberStart = undoLineNumberStart;
    m_Column = undoColumn;
    m_WasNextLine = undoWasNextLine;

    return m_PeekedToken;
}

wxString Tokenizerf::PeekTokenSameFortranLine()
{
    unsigned int undoTokenIndex = m_TokenIndex;
    unsigned int undoLineNumber = m_LineNumber;
    unsigned int undoLineNumberStart = m_LineNumberStart;
    unsigned int undoColumn = m_Column;
    bool undoWasNextLine = m_WasNextLine;

    wxString token = GetTokenSameFortranLine();

    m_WasPeeked = false;

    m_TokenIndex = undoTokenIndex;
    m_LineNumber = undoLineNumber;
    m_LineNumberStart = undoLineNumberStart;
    m_Column = undoColumn;
    m_WasNextLine = undoWasNextLine;

    return token;
}

void Tokenizerf::UngetToken()
{
//    m_WasPeeked = true;
//	m_PeekedTokenIndex = m_TokenIndex;
//	m_PeekedLineNumber = m_LineNumber;
//	m_PeekedLineNumberStart = m_LineNumberStart;
//	m_PeekedColumn = m_Column;
//	m_PeekedWasNextLine = m_WasNextLine;

    m_WasPeeked = false;

    m_TokenIndex = m_UndoTokenIndex;
    m_LineNumber = m_UndoLineNumber;
    m_LineNumberStart = m_UndoLineNumberStart;
    m_Column = m_UndoColumn;
    m_WasNextLine = m_UndoWasNextLine;
}

wxString Tokenizerf::DoGetToken()
{
    if (IsEOF())
        return wxEmptyString;

    if (!SkipWhiteSpace())
        return wxEmptyString;

    if (!SkipUnwanted())
        return wxEmptyString;

    m_LineNumberStart = m_LineNumber;

    int start = m_TokenIndex;
    wxString ret_Str;

    if (IsBindTo())
    {
        m_TokenIndex += 8;
        ret_Str = _T("!bindto");
    }
    else if (isalpha(CurrentChar()) || CurrentChar() == '_' || CurrentChar() == '$' || CurrentChar() == '#')
    {
        // keywords, identifiers, etc.
        while (!IsEOF() &&
                (isalnum(CurrentChar()) ||
                 CurrentChar() == '_'    ||
                 CurrentChar() == '$'    ||
                 CurrentChar() == '#'))
            MoveToNextChar();
        if (IsEOF())
            return wxEmptyString;
        ret_Str = m_Buffer.Mid(start, m_TokenIndex - start);
    }
    else if (isdigit(CurrentChar()))
    {
        // numbers
        while (!IsEOF() && CharInString(CurrentChar(), "0123456789.abcdefABCDEFfXxLl"))
            MoveToNextChar();
        if (IsEOF())
            return wxEmptyString;
        ret_Str = m_Buffer.Mid(start, m_TokenIndex - start);
    }
    else if (CurrentChar() == '"' ||
             CurrentChar() == '\'')
    {
        // string, char, etc.
        wxChar match = CurrentChar();
        MoveToNextChar();  // skip starting ' or "
        if (!SkipToChar(match, true))
            return wxEmptyString;
        MoveToNextChar(); // skip ending ' or "
        ret_Str = m_Buffer.Mid(start, m_TokenIndex - start);
    }
    else if (CurrentChar() == ':')
    {
        if (NextChar() == ':')
        {
            MoveToNextChar();
            MoveToNextChar();
            ret_Str = _T("::");
        }
        else
        {
            MoveToNextChar();
            ret_Str = _T(":");
        }
    }
    else if (CurrentChar() == '=' && NextChar() == '>')
    {
        MoveToNextChar();
        MoveToNextChar();
        ret_Str = _T("=>");
    }
    else if (CurrentChar() == '(' || CurrentChar() == '[')
    {
        // skip block ()
        wxChar chBlock = CurrentChar();
        wxString tmp;
        if (m_SourceForm == fsfFree)
        {
            if (!SkipBlock(chBlock,1))
                return wxEmptyString;
            tmp = m_Buffer.Mid(start, m_TokenIndex - start);

            // skip fortran comments
            for (unsigned int i = 0; i < tmp.Length() - 1; ++i)
            {
                if (tmp.GetChar(i) == '!')
                {
                    // replace comment line with spaces
                    tmp.SetChar(i,' ');
                    for(++i; i < tmp.Length() - 1; ++i)
                    {
                        if (tmp.GetChar(i) == '\n')
                        {
                            tmp.SetChar(i,' ');
                            break;
                        }
                        else
                        {
                            tmp.SetChar(i,' ');
                        }
                    }
                }
            }
        }
        else
        {
            // fsfFixed
            if (!SkipBlock(chBlock, 20))
                return wxEmptyString;
            tmp = m_Buffer.Mid(start, m_TokenIndex - start);

            // skip fixed-form fortran comments
            int col = -1;
            for (unsigned int i = 0; i < tmp.Length() - 1; ++i)
            {
                if (col !=  -1)
                {
                    col++;
                    if (col == 6 && tmp.GetChar(i) != ' ' && tmp.GetChar(i) != '0')
                    {
                        //this line is continuation line
                        tmp.SetChar(i,' ');
                    }
                    else if (col == 6)
                    {
                        // something is wrong
                        return wxEmptyString;
                    }
                }
                if ( (tmp.GetChar(i) == '!') ||
                        (col == 1 && (tmp.GetChar(i) == 'c' || tmp.GetChar(i) == 'C' || tmp.GetChar(i) == '*')) )
                {
                    // replace comment line with spaces
                    tmp.SetChar(i,' ');
                    for(++i; i < tmp.Length() - 1; ++i)
                    {
                        if (tmp.GetChar(i) == '\n')
                        {
                            col = 0;
                            tmp.SetChar(i,' ');
                            break;
                        }
                        else
                        {
                            tmp.SetChar(i,' ');
                        }
                    }
                }
                else if (tmp.GetChar(i) == '\n')
                {
                    col = 0;
                }
            }
        }
        tmp.Replace(_T("\t"), _T(" ")); // replace tabs with spaces
        tmp.Replace(_T("\n"), _T(" ")); // replace LF with spaces
        tmp.Replace(_T("\r"), _T(" ")); // replace CR with spaces
        tmp.Replace(_T("&"), _T(" ")); // replace fortran line continuation with spaces
        // fix-up arguments (remove excessive spaces/tabs/newlines)
        for (unsigned int i = 0; i < tmp.Length()-1; ++i)
        {
            if (i < tmp.Length()-1 && tmp.GetChar(i) == ' ' && tmp.GetChar(i + 1) == ' ')
                continue; // skip excessive spaces
            ret_Str << tmp.GetChar(i);
        }
        if (chBlock == '(')
        {
            ret_Str << _T(')'); // add closing parenthesis (see "i < tmp.Length() - 1" in previous "for")
            ret_Str.Replace(_T("  "), _T(" ")); // replace two-spaces with single-space (introduced if it skipped comments or assignments)
            ret_Str.Replace(_T("( "), _T("("));
            ret_Str.Replace(_T(" )"), _T(")"));
        }
        else
        {
            ret_Str << _T(']'); // add closing parenthesis (see "i < tmp.Length() - 1" in previous "for")
            ret_Str.Replace(_T("  "), _T(" ")); // replace two-spaces with single-space (introduced if it skipped comments or assignments)
            ret_Str.Replace(_T("[ "), _T("["));
            ret_Str.Replace(_T(" ]"), _T("]"));
        }
    }
    else
    {
        ret_Str = CurrentChar();
        MoveToNextChar();
    }

    return ret_Str;
}


wxArrayString Tokenizerf::GetTokensToEOL(wxArrayString* arrStrLines)
{
    // get all tokens on line until EOL
    wxArrayString arrStr;
    wxString o_tok;
    wxString tok;
    bool newLineNext = false;
    while (1)
    {
        unsigned int line = m_LineNumber;
        o_tok = tok;
        tok = GetToken();

        if (tok.IsEmpty())
            break;
        unsigned int n_line = m_LineNumber;
        if (m_SourceForm == fsfFree)
        {
            if ( (n_line > line) && !o_tok.IsSameAs(_T("&")) )
            {
                UngetToken();
                break;
            }
            else if (tok.IsSameAs(_T(";")))
            {
                break;
            }
            else if (!tok.IsSameAs(_T("&")) && ((!m_DetailedParsing && !tok.IsSameAs(_T(","))) || m_DetailedParsing) )
            {
                arrStr.Add(tok);
                if (arrStrLines)
                    arrStrLines->Add(GetCurrentLine());
            }
        }
        else
        {
            if ( (((n_line > line) && (m_Column != 0)) || newLineNext) && (m_Column != 7 || tok.Length() > 1) )
            {
                UngetToken();
                break;
            }
            else if (tok.IsSameAs(_T(";")) || (m_Column < 7 && m_Column != 0))
            {
                break;
            }
            else if ((m_Column > 7 || m_Column == 0) && ((!m_DetailedParsing && !tok.IsSameAs(_T(","))) || m_DetailedParsing) )
            {
                arrStr.Add(tok);
                if (arrStrLines)
                    arrStrLines->Add(GetCurrentLine());
            }

            if (m_Column == 0)
                newLineNext = true;
            else
                newLineNext = false;
        }
    }
    return arrStr;
}


wxArrayString Tokenizerf::PeekTokensToEOL()
{
    // peek all tokens on line until EOL
    unsigned int undoTokenIndex = m_TokenIndex;
    unsigned int undoLineNumber = m_LineNumber;
    unsigned int undoLineNumberStart = m_LineNumberStart;
    unsigned int undoColumn = m_Column;
    bool undoWasNextLine = m_WasNextLine;

    wxArrayString arrStr = GetTokensToEOL();

    m_WasPeeked = false;
    m_TokenIndex = undoTokenIndex;
    m_LineNumber = undoLineNumber;
    m_LineNumberStart = undoLineNumberStart;
    m_Column = undoColumn;
    m_WasNextLine = undoWasNextLine;

    return arrStr;
}

wxString Tokenizerf::GetCurrentLine()
{
    int curLineStart  = GetLineStartIndex(m_TokenIndex);
    int curLineEnd  = GetLineEndIndex(m_TokenIndex);
    wxString curLine = m_Buffer.Mid(curLineStart, curLineEnd - curLineStart);
    return curLine;
}

wxString Tokenizerf::GetLineFortran()
{
    // get current statements' line including continuation lines
    int curLineStart  = GetLineStartIndex(m_TokenIndex);
    int curInd = m_TokenIndex - curLineStart;
    int curLineEnd  = GetLineEndIndex(m_TokenIndex);
    wxString curLine = m_Buffer.Mid(curLineStart, curLineEnd - curLineStart);

    int comInd = curLine.Find('!');
    if (comInd != wxNOT_FOUND)
        curLine = curLine.Mid(0,comInd);

    bool startFound = false;
    bool endFound = false;
    int sc_ind = curLine.Find(';');
    if (sc_ind != wxNOT_FOUND)
    {
        if (sc_ind >= curInd)
        {
            curLine = curLine.Mid(0,sc_ind);
            endFound = true;
        }
        else
        {
            curLine = curLine.Mid(sc_ind+1);
            startFound = true;
        }

    }
    curLine = curLine.Trim().Trim(false);

    if (curLineStart != 0 && !startFound)
    {
        int beforeLineStart;
        int beforeLineEnd = curLineStart - 1;
        beforeLineStart = GetLineStartIndex(beforeLineEnd);

        while (beforeLineStart != 0)
        {
            wxString beforeLine = m_Buffer.Mid(beforeLineStart, beforeLineEnd - beforeLineStart);
            comInd = beforeLine.Find('!');
            if (comInd != wxNOT_FOUND)
                beforeLine = beforeLine.Mid(0,comInd);
            beforeLine = beforeLine.Trim().Trim(false);
            if (beforeLine.EndsWith(_T("&")))
            {
                curLine = beforeLine.BeforeLast('&').Trim() + _T(" ") + curLine;
                sc_ind = curLine.Find(';');
                if (sc_ind != wxNOT_FOUND)
                {
                    curLine = curLine.Mid(sc_ind+1).Trim(false);
                    break;
                }
                beforeLineEnd = beforeLineStart - 1;
                beforeLineStart = GetLineStartIndex(beforeLineEnd);
            }
            else
            {
                break;
            }
        }
    }

    if (!endFound && curLine.EndsWith(_T("&")))
    {
        curLine = curLine.BeforeLast('&').Trim();
        unsigned int afterLineStart = curLineEnd + 1;
        unsigned int afterLineEnd = GetLineEndIndex(afterLineStart);
        while (afterLineStart < afterLineEnd)
        {
            wxString afterLine = m_Buffer.Mid(afterLineStart, afterLineEnd - afterLineStart);
            comInd = afterLine.Find('!');
            if (comInd != wxNOT_FOUND)
                afterLine = afterLine.Mid(0,comInd);
            curLine  = curLine + _T(" ") + afterLine.Trim().Trim(false);
            sc_ind = curLine.Find(';');
            if (sc_ind != wxNOT_FOUND)
            {
                curLine = curLine.Mid(0,sc_ind).Trim();
                break;
            }
            if (curLine.EndsWith(_T("&")))
            {
                curLine = curLine.BeforeLast('&');
                afterLineStart = afterLineEnd + 1;
                afterLineEnd = GetLineEndIndex(afterLineStart);
            }
            else
            {
                break;
            }
        }
    }
    return curLine;
}

unsigned int Tokenizerf::GetLineStartIndex(unsigned int indexInLine)
{
    unsigned int startIndex;
    bool foundEnd = false;
    for (int i=indexInLine-1; i>=0; i--)
    {
        if (m_Buffer.GetChar(i) == '\n')
        {
            startIndex = i+1;
            foundEnd = true;
            break;
        }
    }
    if (!foundEnd)
        startIndex = 0;

    return startIndex;
}

unsigned int Tokenizerf::GetLineEndIndex(unsigned int indexInLine)
{
    unsigned int endIndex;
    bool foundEnd = false;
    for (unsigned int i=indexInLine; i<m_Buffer.Len(); i++)
    {
        if (m_Buffer.GetChar(i) == '\n')
        {
            endIndex = i;
            foundEnd = true;
            break;
        }
    }
    if (!foundEnd)
        endIndex = m_Buffer.Len() - 1;

    return endIndex;
}

void Tokenizerf::SetDetailedParsing(bool detPars)
{
    m_DetailedParsing = detPars;
}

void Tokenizerf::SetFilename(const wxString& filename)
{
    m_Filename = filename;
}

wxString Tokenizerf::GetLine(unsigned int nl)
{
    // get line nl. Note: line numbers starts from 1.
    if (nl == 0 || nl > m_LineStartIdx.size())
        return wxEmptyString;

    unsigned int endIndex;
    if (nl >= m_LineStartIdx.size())
        endIndex = m_Buffer.Len() - 1;
    else
        endIndex = m_LineStartIdx[nl];

    wxString linenl = m_Buffer.Mid(m_LineStartIdx[nl-1], endIndex - m_LineStartIdx[nl-1]);
    return linenl;
}

bool Tokenizerf::IsBindTo()
{
    if (CurrentChar() == '!' ||
            ((CurrentChar() == 'c' || CurrentChar() == 'C' || CurrentChar() == '*') && m_Column == 1 && m_SourceForm == fsfFixed))
    {
        if (m_TokenIndex + 7 >= m_BufferLen)
            return false;

        wxString str = m_Buffer.Mid(m_TokenIndex+1,6);
        if (str.IsSameAs(_T("bindto"),false))
        {
            unsigned int idx = m_TokenIndex + 7;
            if (m_Buffer.GetChar(idx) == ' ' || m_Buffer.GetChar(idx) == '\t')
                return true;
        }
    }
    return false;
}
