/*
WebPageParser.cpp
Written by Matthew Fisher
*/

#include "Main.h"

void WebPageParser::ParseLine(const WebPageParameters &PageParameters, const String &Line, ofstream &OutputFile)
{
    if(Line.Length() > 0 && Line[0] == '@')
    {
        //@Include("Header.txt", HomeActive=@MenuActiveString)
        PersistentAssert(Line.Length() > 1, "Invalid command");
        
        if(Line[1] == '@')
        {
            //
            // Assignment
            //
            String VariableName, Value;
            Line.PartitionAboutIndex(Line.FindFirstIndex('='), VariableName, Value);
            VariableName.PopFront();
            VariableName.PopFront();
            HandleAssignment(VariableName, Value);
        }
        else
        {
            String Command, ParameterString;
            Line.PartitionAboutIndex(Line.FindFirstIndex('('), Command, ParameterString);
            PersistentAssert(ParameterString.Last() == ')', "Invalid command");
            ParameterString.PopEnd();

            if(Command == "@Include")
            {
                HandleInclude(PageParameters, ParameterString, OutputFile);
            }
            else if(Command == "@Code")
            {
                HandleCode(PageParameters, ParameterString, OutputFile);
            }
            else
            {
                SignalError("Invalid command");
            }
        }
    }
    else
    {
        String Result;
        SubstituteVariables(Line, Result);
        OutputFile << Result << endl;
    }
}

WebPageVariable* WebPageParser::GetVariable(const String &Name)
{
    for(UINT VariableIndex = 0; VariableIndex < _Variables.Length(); VariableIndex++)
    {
        if(_Variables[VariableIndex].Name == Name)
        {
            return &_Variables[VariableIndex];
        }
    }
    return NULL;
}

void WebPageParser::HandleInclude(const WebPageParameters &PageParameters, const String &Filename, ofstream &OutputFile)
{
    Vector<String> Lines;
    Utility::GetFileLines(PageParameters.SourceDirectory + String("\\") + Filename, Lines);
    for(UINT LineIndex = 0; LineIndex < Lines.Length(); LineIndex++)
    {
        ParseLine(PageParameters, Lines[LineIndex], OutputFile);
    }
}

bool WebPageParser::IsCodeFile(const String &Filename)
{
    return (Filename.EndsWith(".cpp") || Filename.EndsWith(".h") || Filename.EndsWith(".inl") ||
            Filename.EndsWith(".cs") || Filename.EndsWith(".vsh") || Filename.EndsWith(".psh") ||
            Filename.EndsWith(".vs") || Filename.EndsWith(".ps"));
}

void WebPageParser::HandleCode(const WebPageParameters &PageParameters, const String &ParameterString, ofstream &OutputFile)
{
    String CodeDirectory, OutputDirectory, FullOutputDirectory;
    ParameterString.PartitionAboutIndex(ParameterString.FindFirstIndex(','), CodeDirectory, OutputDirectory);

    String TempString, CodeFolderName;
    OutputDirectory.PartitionAboutIndex(OutputDirectory.FindLastIndex('/'), TempString, CodeFolderName);

    OutputFile << "<h4>" << CodeFolderName << " Code Listing</h4><br/>" << endl;

    CodeDirectory = CodeDirectory + String("/");
    OutputDirectory = OutputDirectory + String("/");
    FullOutputDirectory = PageParameters.TargetDirectory + String("/") + OutputDirectory;

    Vector<String> AllLines;
    UINT TotalLineCount = 0;
    Directory BaseCodeDirectory(CodeDirectory);
    for(UINT FileIndex = 0; FileIndex < BaseCodeDirectory.Files().Length(); FileIndex++)
    {
        const String &CurFilename = BaseCodeDirectory.Files()[FileIndex];
        if(IsCodeFile(CurFilename))
        {
            Utility::GetFileLines(CodeDirectory + CurFilename, AllLines);
            TotalLineCount += AllLines.Length();
            HandleSingleCodeFile(PageParameters, CodeDirectory, CurFilename, FullOutputDirectory + CurFilename, OutputDirectory, 0, OutputFile);
        }
    }
    for(UINT DirectoryIndex = 0; DirectoryIndex < BaseCodeDirectory.Directories().Length(); DirectoryIndex++)
    {
        const String &SubDirectoryName = BaseCodeDirectory.Directories()[DirectoryIndex];
        if(SubDirectoryName != "Debug" && SubDirectoryName != "Release" && SubDirectoryName != "DebugDLL" && SubDirectoryName != "ReleaseDLL" &&
           SubDirectoryName != "DebugApp" && SubDirectoryName != "ReleaseApp" && SubDirectoryName != "ipch" && 
           SubDirectoryName != "bin" && SubDirectoryName != "obj" && SubDirectoryName != "Properties" && SubDirectoryName != "Icons")
        {
            Directory SubDirectory(CodeDirectory + SubDirectoryName + String("/"));
            OutputFile << "<img height=\"18\" alt=\"x\" src=\"Images/Folder.png\" width=\"20\" border=\"0\"></img>";
            OutputFile << "<b>" << SubDirectoryName << "</b><br/>" << endl;
            for(UINT FileIndex = 0; FileIndex < SubDirectory.Files().Length(); FileIndex++)
            {
                const String &CurFilename = SubDirectory.Files()[FileIndex];
                if(IsCodeFile(CurFilename))
                {
                    Utility::GetFileLines(SubDirectory.DirectoryPath() + CurFilename, AllLines);
                    TotalLineCount += AllLines.Length();
                    HandleSingleCodeFile(PageParameters, SubDirectory.DirectoryPath(), CurFilename, FullOutputDirectory + CurFilename, OutputDirectory, 1, OutputFile);
                }
            }
        }
    }

    OutputFile << "<p>Total lines of code: <b>" << TotalLineCount << " </b><br/>" << endl;
}

void WebPageParser::HandleSingleCodeFile(const WebPageParameters &PageParameters, const String &CodeDirectory, const String &CodeFilename,
                                         const String &FormattedFile, const String &OutputDirectory, UINT IndentCount, ofstream &OutputFile)
{
    const String &RootDirectory = PageParameters.SourceToHTMLConverterDirectory;
    if(CopyAllCode && !FormattedFile.Contains("?"))
    {
        Utility::CopyFile(CodeDirectory + CodeFilename, RootDirectory + String("Temp.txt"));
        Utility::CopyFile(CodeDirectory + CodeFilename, FormattedFile);
        Console::WriteLine("Converting " + CodeFilename);
        Utility::RunCommand(RootDirectory + String("CPPtoHTML.exe"), "Temp.txt", true);
        Utility::CopyFile(RootDirectory + String("Temp.txt.html"), FormattedFile + String(".html"));

        Vector<String> FileLines;
        String FormattedHTMLFilename = FormattedFile + String(".html");
        Utility::GetFileLines(FormattedHTMLFilename, FileLines);
        ofstream FormattedHTMLFile(FormattedHTMLFilename.CString());
        for(UINT LineIndex = 0; LineIndex < FileLines.Length(); LineIndex++)
        {
            String CurLine = FileLines[LineIndex];
            FormattedHTMLFile << CurLine.FindAndReplace("<title>Temp.txt</title>", String("<title>") + CodeFilename + String("</title>")) << endl;
        }
    }

    //<img height="18" alt="x" src="Images/Header.png" width="20" border="0"> <a href="MainControl.h">MainControl.h</a>, <a href="MainControl.h.html">Web Version</a>
    //<img height=\"18\" alt=\"x\" src=\"Images/Header.png\" width=\"20\" border=\"0\"> <a href=\"MainControl.h\">MainControl.h</a>, <a href=\"MainControl.h.html\">Web Version</a>
    String ImageName = "Unknown";
    if(CodeFilename.EndsWith(".cpp"))
    {
        ImageName = "Source";
    }
    else if(CodeFilename.EndsWith(".inl"))
    {
        ImageName = "Source";
    }
    else if(CodeFilename.EndsWith(".vsh") || CodeFilename.EndsWith(".vs"))
    {
        ImageName = "Txt";
    }
    else if(CodeFilename.EndsWith(".psh") || CodeFilename.EndsWith(".ps"))
    {
        ImageName = "Txt";
    }
    else if(CodeFilename.EndsWith(".h"))
    {
        ImageName = "Header";
    }
    else if(CodeFilename.EndsWith(".cs"))
    {
        ImageName = "CSharp";
    }
    for(UINT IndentIndex = 0; IndentIndex < IndentCount; IndentIndex++)
    {
        OutputFile << "<img height=\"18\" alt=\"x\" src=\"Images/Placeholder.png\" width=\"20\" border=\"0\"></img>";
    }
    OutputFile << "<img height=\"18\" alt=\"x\" src=\"Images/" << ImageName << ".png\" width=\"20\" border=\"0\"></img> <a href=\"";
    OutputFile << OutputDirectory + CodeFilename << "\">" << CodeFilename << "</a>, <a href=\"" << OutputDirectory + CodeFilename << ".html\">Web Version</a><br/>" << endl;
}

void WebPageParser::HandleAssignment(const String &VariableName, const String &Value)
{
    String DereferencedValue;
    SubstituteVariables(Value, DereferencedValue);

    WebPageVariable* Variable = GetVariable(VariableName);
    if(Variable == NULL)
    {
        _Variables.PushEnd(WebPageVariable(VariableName, DereferencedValue));
    }
    else
    {
        Variable->Value = DereferencedValue;
    }
}

void WebPageParser::SubstituteVariables(const String &Line, String &Result)
{
    //
    // all @'s must be preceeded by a ' '; if @ is the first symbol in a line it is a command
    //

    Result.FreeMemory();
    for(UINT CharIndex = 0; CharIndex < Line.Length(); CharIndex++)
    {
        char PrevC = ' ';
        if(CharIndex != 0)
        {
            PrevC = Line[CharIndex - 1];
        }
        char C = Line[CharIndex];
        if(PrevC == ' ' && C == '@')
        {
            String VariableName;
            bool Done = false;
            for(CharIndex = CharIndex + 1; CharIndex < Line.Length() && !Done; CharIndex++)
            {
                C = Line[CharIndex];
                if(isalpha(C))
                {
                    VariableName.PushEnd(C);
                }
                else
                {
                    CharIndex--;
                    Done = true;
                }
            }
            CharIndex--;
            WebPageVariable* Variable = GetVariable(VariableName);
            PersistentAssert(Variable != NULL, String("Variable ") + VariableName + String(" not found"));
            Result += Variable->Value;
        }
        else
        {
            Result.PushEnd(C);
        }
    }
}