////////////////////////////////////////
// token.cpp
////////////////////////////////////////
//
//  This file handle all the token strings
//
////////////////////////////////////////

#include "token.h"
#include "main.h"

extern CMainApp theApp;
#define MAX_TOKEN   8192

CToken::CToken()
{
    m_uiTypeID      = 0;
    m_uiResID       = 0;
    m_uiItemID      = 0;
    m_uiFlags       = 0;
    m_uiStatusFlags = 0;
    m_uiLastError   = 0;
    m_strItemName   = "";
    m_strSrcText    = "";
    m_strTgtText    = "";
    
}

int CToken::Parse(CString strSrc, CString strTgt)
{
    CString strSrcTokenID;
    int pos;
    
    if(!strSrc.IsEmpty())
    {
        pos = strSrc.Find('=');
        if(pos==-1)
        {
            theApp.WriteCon(CONERR, "Invalid token [equal sign missing]\t%s\r\n", strSrc);
            theApp.SetReturn(CMainApp::RET_INVALID_TOKEN);
            return CMainApp::ERR_TOKEN_WRONGFORMAT;
        }

        strSrcTokenID = strSrc.Left(pos-3);
        m_strSrcText = strSrc.Right(strSrc.GetLength()-++pos);
        pos = m_strSrcText.Find('\n');
        if(pos!=-1)
            m_strSrcText = m_strSrcText.Left(pos);
    }
    else 
        m_strSrcText = "";

    pos = strTgt.Find('=');
    if(pos==-1)
    {
        theApp.WriteCon(CONERR, "Invalid token  [equal sign missing]\t%s\r\n", strTgt);
        theApp.SetReturn(CMainApp::RET_INVALID_TOKEN);
        return CMainApp::ERR_TOKEN_WRONGFORMAT;
    }
    
    if(pos<=3)
    {
        theApp.WriteCon(CONERR, "Invalid token  [token corruption]\t%s\r\n", strTgt);
        theApp.SetReturn(CMainApp::RET_INVALID_TOKEN);
        return CMainApp::ERR_TOKEN_WRONGFORMAT;
    }

    m_strTokenID = strTgt.Left(pos-3);
    m_strTgtText = strTgt.Right(strTgt.GetLength()-++pos);
    pos = m_strTgtText.Find('\n');
    if(pos!=-1)
        m_strTgtText = m_strTgtText.Left(pos);

    if(5>sscanf(m_strTokenID, TEXT("[[%u|%u|%u|%u|%u|\"%s\"]]"),
        &m_uiTypeID,
        &m_uiResID,
        &m_uiItemID,
        &m_uiFlags,
        &m_uiStatusFlags,
        m_strItemName.GetBuffer(128)))
    {
        theApp.WriteCon(CONERR, "Invalid token [not enough arguments converted]\t%s\"]]\r\n", m_strTokenID);
        theApp.SetReturn(CMainApp::RET_INVALID_TOKEN);
        return CMainApp::ERR_TOKEN_WRONGFORMAT;
    }

	// Special case the Menu Popup
	if(m_uiTypeID==4 && (m_uiFlags & ISPOPUP)==ISPOPUP && (m_uiFlags & OLD_POPUP_ID)==OLD_POPUP_ID)
    {
        m_uiItemID = MAKELONG( 0xFFFF, LOWORD(m_uiItemID) );
    }
	
    m_strItemName.ReleaseBuffer();
    m_strItemName = m_strTokenID.Mid(m_strTokenID.Find("|\"")+2);

    if(!strSrc.IsEmpty())
    {
        // Perform a consistency check on the token files
        unsigned int    uiTypeID = 0;
        unsigned int    uiResID = 0;
        unsigned int    uiItemID = 0;
        unsigned int    uiFlags = 0;
        unsigned int    uiStatusFlags = 0;
        CString         strItemName = "";

        sscanf(strSrcTokenID, TEXT("[[%u|%u|%u|%u|%u|\"%s\"]]"),
            &uiTypeID,
            &uiResID,
            &uiItemID,
            &uiFlags,
            &uiStatusFlags,
            strItemName.GetBuffer(128));

        strItemName.ReleaseBuffer();
        strItemName = strSrcTokenID.Mid(strSrcTokenID.Find("|\"")+2);

        // Special case the Menu Popup
		if(uiTypeID==4 && (uiFlags & ISPOPUP)==ISPOPUP && (m_uiFlags & OLD_POPUP_ID)==OLD_POPUP_ID)
        {
            uiItemID = MAKELONG( 0xFFFF, LOWORD(uiItemID) );
        } 		
		
        // Compare token ID
        if(!((uiTypeID == m_uiTypeID) &&
           (uiResID  == m_uiResID) &&
           (uiItemID == m_uiItemID) &&
           (uiFlags  == m_uiFlags) &&
           (strItemName == m_strItemName)))
        {
            theApp.WriteCon(CONERR, "Token ID mismatch\t%s\"]]\t%s\"]]\r\n", strSrcTokenID.GetBuffer(0), m_strTokenID.GetBuffer(0));
            theApp.SetReturn(CMainApp::RET_TOKEN_MISMATCH);
            return CMainApp::ERR_TOKEN_MISMATCH;
        }
    }

    return 0;
}

BOOL CToken::GetTgtSize(WORD * px, WORD * py,WORD * pcx, WORD * pcy)
{
   return sscanf(m_strTgtText, TEXT("%hu %hu %hu %hu"),
        px,
        py,
        pcx,
        pcy);
}

BOOL CToken::GetSrcSize(WORD * px, WORD * py,WORD * pcx, WORD * pcy)
{
   return sscanf(m_strSrcText, TEXT("%hu %hu %hu %hu"),
        px,
        py,
        pcx,
        pcy);
}


/////////////////////////////////////////////////////////////////////////

CTokenFile::CTokenFile()
{
    m_Tokens.SetSize(0, 10);
    m_iLastPos      = 0;
    m_iUpperBound   = -1;
    m_strSrcFile    = "";
    m_strTgtFile    = "";
}

CTokenFile::~CTokenFile()
{
    for(int at=0; at<=m_iUpperBound; at++)
        delete (m_Tokens.GetAt(at));

    m_Tokens.RemoveAll();
}

int CTokenFile::Open(CString strSrcFile, CString strTgtFile)
{
    int iErr = CMainApp::ERR_NOERROR;
    // Open the files
    CStdioFile SrcFile;
    CStdioFile TgtFile;

    // If we are doing an UPDATE we need both src and tgt files
    // while if we are not it is enough the tgt file.
    // If only the tgt file is given no consistency will be done.
    if(theApp.IsFlag(CMainApp::UPDATE))
        if(!SrcFile.Open(strSrcFile, CFile::modeRead | CFile::shareDenyWrite))
        {
            theApp.WriteCon(CONERR, "Cannot open file: %s\r\n", strSrcFile);
            return CMainApp::ERR_FILE_OPEN;
        }
    
    if(!TgtFile.Open(strTgtFile, CFile::modeRead | CFile::shareDenyWrite))
    {
        theApp.WriteCon(CONERR, "Cannot open file: %s\r\n", strTgtFile);
        return CMainApp::ERR_FILE_OPEN;
    }

    CString strSrc = "";
    CString strTgt = "";
    int at;

    while(TgtFile.ReadString(strTgt.GetBuffer(MAX_TOKEN), MAX_TOKEN))
    {
        if(theApp.IsFlag(CMainApp::UPDATE))
            if(!SrcFile.ReadString(strSrc.GetBuffer(MAX_TOKEN), MAX_TOKEN))
            {
                theApp.WriteCon(CONERR, "The file  %s has more tokens than the file %s!\r\n", strTgtFile, strSrcFile);
                theApp.SetReturn(CMainApp::RET_TOKEN_MISMATCH);
                return CMainApp::ERR_TOKEN_MISMATCH;
            }

        strSrc.ReleaseBuffer();
        strTgt.ReleaseBuffer();

        at = m_Tokens.Add(new CToken());

        if(iErr = ((CToken*)m_Tokens.GetAt(at))->Parse(strSrc,strTgt)) 
        {
            goto close;
        }

    }
    
    m_iUpperBound = m_Tokens.GetUpperBound();

close:
    if(theApp.IsFlag(CMainApp::UPDATE))
        SrcFile.Close();

    TgtFile.Close();

    return iErr;
}

const CToken * CTokenFile::GetTokenSize(CToken * pToken, WORD * px, WORD * py,
                      WORD * pcx, WORD * pcy)
{
    
    if((pToken!=NULL) && (pToken->m_uiTypeID!=5))
        return NULL;
    
    CToken * pTokenSize = (CToken *)m_Tokens.GetAt(m_iLastPos++);
    
    if(pTokenSize==NULL)
        return NULL;

    WORD x, y, cx, cy;
    pTokenSize->GetSrcSize(&x, &y, &cx, &cy);
    
    // check if the size changed
    if(!theApp.IsFlag(CMainApp::UPDATE) ||
       (x==*px &&
       y==*py &&
       cx==*pcx &&
       cy==*pcy) )
    {
        pTokenSize->GetTgtSize(px, py, pcx, pcy);
    }
    else
    {
        theApp.WriteCon(CONWRN, "Item Resized\t%s\"]]\r\n", pTokenSize->m_strTokenID);
        theApp.AddResized();
    }

    return pTokenSize;
}

const CToken * CTokenFile::GetTokenSize(unsigned int TypeID,
                      unsigned int ResID,
                      unsigned int ItemID,
                      CString strItemName,
                      WORD * px, WORD * py,
                      WORD * pcx, WORD * pcy)
{
    if(TypeID!=5)
        return NULL;

    BOOL bMatch = FALSE;
        
    ASSERT(m_iUpperBound!=-1);
    if(m_iLastPos>m_iUpperBound)
        m_iLastPos = 0;

    CToken * pToken = NULL;
    int iLastPos = m_iLastPos;

    while(!bMatch)
    {
        pToken = (CToken*)m_Tokens.GetAt(m_iLastPos++);

        if(pToken==NULL)
            return NULL;

        while(pToken->m_uiTypeID!=TypeID && !bMatch) {
            if(m_iLastPos>m_iUpperBound) {
                m_iLastPos = 0;
                bMatch = TRUE;
            }
            pToken = (CToken*)m_Tokens.GetAt(m_iLastPos++);
            if(pToken==NULL)
                return NULL;
        }

        // Let's see if we have at least some parameter to find the token
        if(pToken->m_uiTypeID==TypeID &&
           pToken->m_uiResID==ResID &&
           pToken->m_uiItemID==ItemID && 
           pToken->m_strItemName==strItemName &&
           (pToken->m_uiFlags & ISCOR))      // to be compatible with rlman token ids
        {
            WORD x, y, cx, cy;
            pToken->GetSrcSize(&x, &y, &cx, &cy);
            
            // check if the size changed
            if(!theApp.IsFlag(CMainApp::UPDATE) ||
               (x==*px &&
               y==*py &&
               cx==*pcx &&
               cy==*pcy) )
            {
                pToken->GetTgtSize(px, py, pcx, pcy);
            }
            else
            {
                theApp.WriteCon(CONWRN, "Item Resized\t%s\"]]\r\n", pToken->m_strTokenID);
                theApp.AddResized();
            }

            return pToken;
        }
        else if(pToken->m_uiTypeID!=TypeID) 
        {
            m_iLastPos = iLastPos;
            return NULL;        
        }

        if(m_iLastPos>m_iUpperBound)
           return NULL;
    }
    
    return NULL;
}

const CToken * CTokenFile::GetToken(unsigned int TypeID,
                      unsigned int ResID,
                      unsigned int ItemID,
                      CString strText,
                      CString strItemName)
{
    if(strText.IsEmpty())
        return NULL;

    BOOL bMatch = FALSE;

    ASSERT(m_iUpperBound!=-1);
    if(m_iLastPos>m_iUpperBound)
        m_iLastPos = 0;

    CToken * pToken = NULL;
    int iLastPos = m_iLastPos;

    while(!bMatch)
    {
        pToken = (CToken*)m_Tokens.GetAt(m_iLastPos++);

        if(pToken==NULL)
            return NULL;

        while(pToken->m_uiTypeID!=TypeID && !bMatch) {
            if(m_iLastPos>m_iUpperBound) {
                m_iLastPos = 0;
                bMatch = TRUE;
            }
            pToken = (CToken*)m_Tokens.GetAt(m_iLastPos++);
            if(pToken==NULL)
                return NULL;
        }

        // Let's see if we have at least some parameter to find the token
        if(pToken->m_uiTypeID==TypeID &&
           pToken->m_uiResID==ResID &&
           pToken->m_uiItemID==ItemID && 
           pToken->m_strItemName==strItemName &&
           !(pToken->m_uiFlags & ISCOR))    // to be compatible with rlman token ids        
        {
            if(!theApp.IsFlag(CMainApp::FONTS) && (pToken->m_uiFlags & ISDLGFONTNAME) || (pToken->m_uiFlags & ISDLGFONTSIZE))
                return pToken;
            else if(!theApp.IsFlag(CMainApp::UPDATE) || pToken->m_strSrcText==strText)
                return pToken;
            else
            {
                theApp.WriteCon(CONWRN, "Context changed\t%s\"]]\r\n", pToken->m_strTokenID);
                theApp.AddChanged();
                pToken->m_uiLastError = 1;
                return pToken;
            }
        }
        else if(pToken->m_uiTypeID!=TypeID) {
                m_iLastPos = iLastPos;
                return NULL;        
        }

        if(m_iLastPos>m_iUpperBound)
           return NULL;
    }
    
    return NULL;
}

const CToken * CTokenFile::GetNoCaptionToken(unsigned int TypeID,
                      unsigned int ResID,
                      unsigned int ItemID,
                      CString strItemName)
{
    BOOL bMatch = FALSE;

    ASSERT(m_iUpperBound!=-1);
    if(m_iLastPos>m_iUpperBound)
        m_iLastPos = 0;

    CToken * pToken = NULL;
    int iLastPos = m_iLastPos;

    while(!bMatch)
    {
        pToken = (CToken*)m_Tokens.GetAt(m_iLastPos++);

        if(pToken==NULL)
            return NULL;

        while(pToken->m_uiTypeID!=TypeID && !bMatch) {
            if(m_iLastPos>m_iUpperBound) {
                m_iLastPos = 0;
                bMatch = TRUE;
            }
            pToken = (CToken*)m_Tokens.GetAt(m_iLastPos++);
            if(pToken==NULL)
                return NULL;
        }

        // Let's see if we have at least some parameter to find the token
        if(pToken->m_uiTypeID==TypeID &&
           pToken->m_uiResID==ResID &&
           pToken->m_uiItemID==ItemID && 
           pToken->m_strItemName==strItemName)  // to be compatible with rlman token ids        
        {
           return pToken;
        }
        else if(pToken->m_uiTypeID!=TypeID) {
                m_iLastPos = iLastPos;
                return NULL;        
        }

        if(m_iLastPos>m_iUpperBound)
           return NULL;
    }
    
    return NULL;
}