#include "eppj.hpp"
#include <cstdlib>
#include <ctime>
#include <cmath> 
#include <iostream>

TDirectory_Entry::TDirectory_Entry()
{
    Name = "";
    URL = "";
    Tag = 0;
}


string TURL_Source::Get_Text(string URL)
{
    return "";
};


TURL_Source* _URL_Source = NULL;

TURL_Source* URL_Source()
{
    if (_URL_Source == NULL)
    {
        _URL_Source = new TURL_Source;
    };
    return _URL_Source;
};


/* TEPPJ_File methods... */

int TEPPJ_File::Count()
{
    return 0;
};



/* TSecurity methods... */

TSecurity::TSecurity()
{
    File_Type = FT_Password;
};



/* TPrayer_List methods... */

TPrayer_List::TPrayer_List()
{
    URL = "";
    Password = "";
    File_Type = FT_List;
};


TPrayer_List::~TPrayer_List()
{
    for (int Index = _List.size(); Index >= 0; Index--)
    {
        if (_List[Index] != NULL)
        {
            delete ((TPrayer_Item*)_List[Index]);
        };
    };
};


vector<void*> TPrayer_List::List()
{
    return _List;
};


TPrayer_Item::TPrayer_Item()
{
    ID = "";
    Title = "";
    Text = "";
    More = "";
    Image = "";
    Answered = "";
    _Set = "";
    Startdate = "";
    enddate = "";
    Urgent = "";
    Hidden = false;
    Parent = NULL;
    Tag = 0;
}

TPrayer_Item* TPrayer_List::Get_Item(int Index)
{
    return ((TPrayer_Item*)_List[Index]);
};


void TPrayer_List::Set_Item(int Index, TPrayer_Item* Value)
{
    _List[Index] = Value;
};


int TPrayer_List::Count()
{
    return _List.size();
};


int TPrayer_List::Add(TPrayer_Item* Value)
{
    _List.push_back(Value);
    Value->Parent = this;
    return _List.size() - 1;
};


void TPrayer_List::Delete(int Value)
{
    _List.erase(_List.begin() + Value);
};


string TPrayer_List::Serialize(bool Include_Password)
{
    string Result = "<url>" + URL + "</url>";
    if (Include_Password)
    {
        Result = Result + "<pass>" + Password + "</pass>";
    };
    for (int I = 0; I < _List.size(); I++)
    {
        TPrayer_Item* Item = Get_Item(I);
        Result = Result + "<item>" + Item->Serialize() + "</item>";
    };
    return Result;
};


/* TDirectory methods... */
TDirectory::TDirectory()
{
    File_Type = FT_Directory;
};


TDirectory::~TDirectory()
{
    for (int Index = _List.size() - 1; Index >= 0; Index--) delete ((TDirectory_Entry*)_List[Index]);
};


vector<void*> TDirectory::List()
{
    return _List;
};


int TDirectory::Add(TDirectory_Entry* Value)
{
    _List.push_back(Value);
    return _List.size() - 1;
};


int TDirectory::Count()
{
    return _List.size();
};


string TDirectory::Name(int Index)
{
    return ((TDirectory_Entry*)_List[Index])->Name;
};


string TDirectory::URL(int Index)
{
    return ((TDirectory_Entry*)_List[Index])->URL;
};


TDirectory_Entry* TDirectory::Entry(int Index)

{
    return ((TDirectory_Entry*)_List[Index]);
};


int TDirectory::Indexof_Name(string S)
{
    for (int I = 0; I < Count(); I++)
    {
        if (Entry(I)->Name == S)
        {
            return I;
        };
    };
    return -1;
};


class TLoad_Balance
{
public:
    int Percent;
    string URL;

    TLoad_Balance()
    {
        Percent = 0;
        URL = "";
    };
};

class TLoad_Balance_List
{
public:
    ~TLoad_Balance_List();

private:
    vector<void*> _List;

protected:
    vector<void*> List();

public:
    void Append(string Spec);
    void Prepend(int P, string Spec);
    string Pick_URL(TURL_Source* Source);
};

TLoad_Balance_List::~TLoad_Balance_List()
{
    for (int Index = _List.size() - 1; Index >= 0; Index--) delete ((TLoad_Balance*)_List[Index]);
};


vector<void*> TLoad_Balance_List::List()
{
    return _List;
};


void TLoad_Balance_List::Append(string Spec)
{
    TLoad_Balance* Balance = new TLoad_Balance;
    Balance->URL = Spec;
    _List.push_back(Balance);
};


void TLoad_Balance_List::Prepend(int P, string Spec)
{
    TLoad_Balance* Balance = new TLoad_Balance;
    Balance->URL = Spec;
    Balance->Percent = P;
    _List.insert(_List.begin(), Balance);
};


string Get_URL(TURL_Source* Source, string U)
{
    if (Source != NULL)
    {
        return Source->Get_Text(U);
    };
    return "";
};


string TLoad_Balance_List::Pick_URL(TURL_Source* Source)
{
    string Result = "";

    /* Determine metrics... */
    int Count = 0;
    int Total = 0;
    for (int Index = 0; Index < _List.size(); Index++)
    {
        Total = Total + ((TLoad_Balance*)_List[Index])->Percent;
        if (((TLoad_Balance*)_List[Index])->Percent == 0)
        {
            Count++;
        };
    };

    /* For specs without a percentage, spread any remaning percentage among them...*/
    if (Count > 0) /* Have unspecified specs */
    {
        Total = (100 - Total) / Count; /* Remaining percentage to split between specs */
        if (Total < 1) Total = 1; /* No less than 1% to each spec*/
        for (int Index = 0; Index < _List.size(); Index++)
        {
            if (((TLoad_Balance*)_List[Index])->Percent == 0)
            {
                ((TLoad_Balance*)_List[Index])->Percent = Total;
            };
        };
    };

    /* Now pick one at random... */
    srand(time(NULL)); ;
    while (true) /* Loop until we find a valid url */
    {
        int P = static_cast<int>(round(rand() * 100)); /* Determine percentage */
        for (int Index = 0; Index < _List.size(); Index++)
        {
            if (
                (((TLoad_Balance*)_List[Index])->Percent <= P)
                ||
                (Index == _List.size() - 1)
                )
            {
                /* Found a match, or ran out of possible targets... */
                Result = Get_URL(Source, ((TLoad_Balance*)_List[Index])->URL);
                if (Result.find("<eppj>") != string::npos) return Result; /* Exit with our find */
                Result = ""; /* Otherwise, the target is not valid */
                _List.erase(_List.begin() + Index); /* Remove URL from choices */
                break; /* Try another random choice */
            }
            else
            {
                P = P - ((TLoad_Balance*)_List[Index])->Percent; /* Adjust for next check*/
            };
        };
    };
    return Result;
};


string TPrayer_Item::Serialize()
{
    string Result = "<id>" + ID + "</id>" +
        "<title>" + Title + "</title>" +
        "<text>" + Text + "</text>" +
        "<more>" + More + "</more>" +
        "<image>" + Image + "</image>" +
        "<answered>" + Answered + "</answered>" +
        "<set>" + _Set + "</set>" +
        "<startdate>" + Startdate + "</startdate>" +
        "<}date>" + Startdate + "</}date>" +
        "<urgent>" + Urgent + "</urgent>";
    if (Hidden)
    {
        Result = Result + "<hidden>";
    };
    return Result;
};


string Get_Tag_Contents(string Name, string& S)
/* Get text up to specified tag */
{
    size_t I = S.find("<" + Name + ">");
    if (I == string::npos) I = S.length() + 1;
    string Result = S.substr(1, I - 1);
    S = S.substr(I + Name.length() + 1);
    return Result;
};


string lowercase(string S)
{
    std::transform(S.begin(), S.end(), S.begin(), ::tolower);
    return S;
}


string Next_Tag(string& S)
{
    string Result = "";
    size_t I = S.find("<");
    if (I == string::npos) return ""; /* No more tags */
    size_t E = S.find(">", I);
    if (E == string::npos) E = S.length() + 1;
    Result = lowercase(S.substr(I, E - I + 1));
    S = S.substr(E, S.length());
    return Result;
};


TLoad_Balance_List* Parse_Load_Balance(string S)
{
    TLoad_Balance_List* Result = new TLoad_Balance_List;
    while (S != "")
    {
        string Spec = Get_Tag_Contents("</balance>", S);
        size_t I = Spec.find("%::");
        if (I == string::npos)
        {
            Result->Append(Spec);
        }
        else
        {
            try
            {
                string S1 = Spec.substr(0, I - 2);
                I = atoi(S1.c_str());
                Result->Prepend(I, Spec);
            }
            catch (...)
            {
            }
        };

        S = Get_Tag_Contents("<balance>", S); /* Find next balance tag */
    };
    return Result;
};


TEPPJ_File* Parse_File(string S, TURL_Source* Source,
    bool Redirection, bool Load_Balance)
{
    if (S == "") return NULL; /* Probably a failure to load file... */
    if (S.substr(0, 6) == "secure")
    {
        return new TSecurity;
    };
    if (S.find("<eppj>") == string::npos) return NULL; /* Not an eppj file... */
    TDirectory* Dir = NULL;
    TPrayer_Item* Item = NULL;

    Get_Tag_Contents("eppj", S); /* Read (ignore) up to, and including, <eppj> */
    TEPPJ_File* Result = NULL;

    string Tag = Next_Tag(S);
    while (Tag != "")
    {
        if (Tag == "<redirect>")
        {
            Tag = Get_Tag_Contents("/redirect", S);
            if (!Redirection)
            {
                Load_EPPJ(Tag, Source, true, Load_Balance);
                return Result;
            };
        }
        else
            if (Tag == "<balance>")
            {
                if (!Load_Balance)
                {
                    Load_Balance = true;
                    TLoad_Balance_List* LBL = Parse_Load_Balance(S);
                    S = LBL->Pick_URL(Source);
                    delete LBL;
                    if (S == "")
                    {
                        return Result; /* No valid balance target found */
                    };
                };
            }
            else
                if (Tag == "<dir>")
                {
                    if (Result == NULL)
                    {
                        TDirectory* Dir = new TDirectory();
                        Result = Dir;
                    };
                    if (Result->File_Type == FT_Directory)
                    {
                        string SS = Get_Tag_Contents("/dir", S);
                        TDirectory_Entry* Entry = new TDirectory_Entry();
                        Dir->Add(Entry);
                        while (SS != "")
                        {
                            Tag = Next_Tag(SS);
                            if (Tag == "<name>")
                            {
                                Entry->Name = Get_Tag_Contents("/name", SS);
                            }
                            else
                                if (Tag == "<url>")
                                {
                                    Entry->URL = Get_Tag_Contents("/url", SS);
                                }
                                else
                                {
                                    /* Unrecognized tag - ignore it... */
                                    Get_Tag_Contents("/" + Tag.substr(1, Tag.length()), SS);
                                };
                        };
                    };
                }
                else
                    if (Tag == "<item>")
                    {
                        if (Result == NULL)
                        {
                            Result = new TPrayer_List;
                        };
                        if (Result->File_Type == FT_List)
                        {
                            if (Item == NULL)
                            {
                                Item = new TPrayer_Item;
                                ((TPrayer_List*)Result)->Add(Item);
                            };
                        };
                    }
                    else
                        if (Tag == "</item>")
                        {
                            Item = NULL;
                        }
                        else
                            if (Tag == "<id>")
                            {
                                if (Item != NULL)
                                {
                                    Item->ID = Get_Tag_Contents("/id", S);
                                };
                            }
                            else
                                if (Tag == "<title>")
                                {
                                    if (Item != NULL)
                                    {
                                        Item->Title = Get_Tag_Contents("/title", S);
                                    };
                                }
                                else
                                    if (Tag == "<text>")
                                    {
                                        if (Item != NULL)
                                        {
                                            Item->Text = Get_Tag_Contents("/text", S);
                                        };
                                    }
                                    else
                                        if (Tag == "<more>")
                                        {
                                            if (Item != NULL)
                                            {
                                                Item->More = Get_Tag_Contents("/more", S);
                                            };
                                        }
                                        else
                                            if (Tag == "<image>")
                                            {
                                                if (Item != NULL)
                                                {
                                                    Item->Image = Get_Tag_Contents("/image", S);
                                                };
                                            }
                                            else
                                                if (Tag == "<answered>")
                                                {
                                                    if (Item != NULL)
                                                    {
                                                        Item->Answered = Get_Tag_Contents("/answered", S);
                                                    };
                                                }
                                                else
                                                    if (Tag == "<set>")
                                                    {
                                                        if (Item != NULL)
                                                        {
                                                            Item->_Set = Get_Tag_Contents("/set", S);
                                                        };
                                                    }
                                                    else
                                                        if (Tag == "<pass>")
                                                        {
                                                            if (Result == NULL)
                                                            {
                                                                Result = new TPrayer_List;
                                                            };
                                                            if (Result->File_Type == FT_List)
                                                            {
                                                                ((TPrayer_List*)Result)->Password = Get_Tag_Contents("/pass", S);
                                                            };
                                                        }
                                                        else
                                                            if (Tag == "<url>")
                                                            {
                                                                if (Result == NULL)
                                                                {
                                                                    Result = new TPrayer_List;
                                                                };
                                                                if (Result->File_Type == FT_List)
                                                                {
                                                                    ((TPrayer_List*)Result)->URL = Get_Tag_Contents("/url", S);
                                                                };
                                                            }
                                                            else
                                                                if (Tag == "<startdate>")
                                                                {
                                                                    if (Item != NULL)
                                                                    {
                                                                        Item->Startdate = Get_Tag_Contents("/startdate", S);
                                                                    };
                                                                }
                                                                else
                                                                    if (Tag == "<enddate>")
                                                                    {
                                                                        if (Item != NULL)
                                                                        {
                                                                            Item->enddate = Get_Tag_Contents("/enddate", S);
                                                                        };
                                                                    }
                                                                    else
                                                                        if (Tag == "<hidden>")
                                                                        {
                                                                            if (Item != NULL)
                                                                            {
                                                                                Item->Hidden = true;
                                                                            };
                                                                        }
                                                                        else
                                                                            if (Tag == "<urgent>")
                                                                            {
                                                                                if (Item != NULL)
                                                                                {
                                                                                    Item->Urgent = Get_Tag_Contents("/urgent", S);
                                                                                };
                                                                            }
                                                                            else
                                                                            {
                                                                                /* Unrecognized tag - ignore it... */
                                                                                Get_Tag_Contents("/" + Tag.substr(1, Tag.length()), S);
                                                                            };
        Tag = Next_Tag(S);
    };
    return Result;
}; /* Parse_File */


TEPPJ_File* Load_EPPJ(const string URL, TURL_Source* Source,
    bool Redirection, bool Load_Balance)
{
    string S = Get_URL(Source, URL);
    return Parse_File(S, Source, Redirection, Load_Balance);
};
