#include "precompiled.h"
#include "Formation.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "maths/MathUtil.h"

#define LOG_CATEGORY "Formation"

CFormation::CFormation()
{
    m_numSlots = 0;
}
bool CFormation::LoadXml(const CStr& filename)
{
    CXeromyces XeroFile;

    if (XeroFile.Load(filename) != PSRETURN_OK)
        return false;

    #define EL(x) int el_##x = XeroFile.GetElementID(#x)
    #define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
    EL(formation);
    EL(fl);
    EL(rk);
    EL(blank);

    AT(tag);
    AT(bonus);
    AT(bonusbase);
    AT(bonustype);
    AT(bonusval);
    AT(penalty);
    AT(penaltybase);
    AT(penaltytype);
    AT(penaltyval);
    AT(anglepenalty);
    AT(anglepenaltydivs);
    AT(anglepenaltytype);
    AT(anglepenaltyval);

    AT(required);
    AT(next);
    AT(prior);
    AT(movement);
    AT(filespacing);
    AT(rankspacing);
    AT(category);
    AT(order);
    #undef AT
    #undef EL

    XMBElement Root = XeroFile.GetRoot();
    if( Root.GetNodeName() != el_formation )
    {
        LOG( ERROR, LOG_CATEGORY, "CFormation::LoadXml: XML root was not \"Formation\" in file %s. Load failed.", filename.c_str() );
        return( false );
    }

    //Load in single attributes
    XMBAttributeList Attributes = Root.GetAttributes();
    for ( int i=0; i<Attributes.Count; ++i )
    {
        XMBAttribute Attr = Attributes.Item(i);
        if ( Attr.Name == at_tag )
            m_tag = CStr(Attr.Value);
        else if ( Attr.Name == at_bonus )
            m_bonus = CStr(Attr.Value);
        else if ( Attr.Name == at_bonusbase )
            m_bonusBase = CStr(Attr.Value);
        else if ( Attr.Name == at_bonustype )
            m_bonusType = CStr(Attr.Value);
        else if ( Attr.Name == at_bonusval )
            m_bonusVal = CStr(Attr.Value).ToFloat();
        else if ( Attr.Name == at_penalty )
            m_penalty = CStr(Attr.Value);
        else if ( Attr.Name == at_penaltybase )
            m_penaltyBase = CStr(Attr.Value);
        else if ( Attr.Name == at_penaltytype )
            m_penaltyType = CStr(Attr.Value);
        else if ( Attr.Name == at_penaltyval )
            m_penaltyVal = CStr(Attr.Value).ToFloat();

        else if ( Attr.Name == at_anglepenalty )
            m_anglePenalty = CStr(Attr.Value);
        else if ( Attr.Name == at_anglepenaltydivs )
            m_anglePenaltyDivs = CStr(Attr.Value).ToInt();
        else if ( Attr.Name == at_anglepenaltytype )
            m_anglePenaltyType = CStr(Attr.Value);
        else if ( Attr.Name == at_anglepenaltyval )
            m_anglePenaltyVal = CStr(Attr.Value).ToFloat();

        else if ( Attr.Name == at_required)
            m_required = CStr(Attr.Value).ToInt();
        else if ( Attr.Name == at_next )
            m_next = CStr(Attr.Value);
        else if ( Attr.Name == at_prior )
            m_prior = CStr(Attr.Value);
        else if ( Attr.Name == at_movement )
            m_movement = CStr(Attr.Value);
        else if ( Attr.Name == at_rankspacing )
            m_rankSpacing = CStr(Attr.Value).ToFloat();
        else if ( Attr.Name == at_filespacing )
            m_fileSpacing = CStr(Attr.Value).ToFloat();
        else
        {
            const char* invAttr = XeroFile.GetAttributeString(Attr.Name).c_str();
            LOG( ERROR, LOG_CATEGORY, "CFormation::LoadXml: Invalid attribute %s defined in formation file %s. Load failed.", invAttr, filename.c_str() );
            return( false );
        }
    }

    XMBElementList RootChildren = Root.GetChildNodes();
    int file=0;
    int rank=0;
    int maxrank=0;

    //Read in files and ranks
    for (int i = 0; i < RootChildren.Count; ++i)
    {
        XMBElement RootChild = RootChildren.Item(i);
        int ChildName = RootChild.GetNodeName();

        if ( ChildName == el_fl )
        {
             rank = 0;
            XMBAttributeList FileAttribList = RootChild.GetAttributes();
            //Load default category
            CStr FileCatValue = FileAttribList.GetNamedItem(at_category);

            //Specific slots in this file (row)
            XMBElementList RankNodes = RootChild.GetChildNodes();
            for ( int r=0; r<RankNodes.Count; ++r )
            {
                XMBElement Rank = RankNodes.Item(r);
                if ( Rank.GetNodeName() == el_blank )
                {
                    ++rank;
                    continue;
                }
                else if ( Rank.GetNodeName() != el_rk )
                    return false;
                    //error

                XMBAttributeList RankAttribList = Rank.GetAttributes();
                int order = CStr( RankAttribList.GetNamedItem(at_order) ).ToInt();
                CStr category = CStr( RankAttribList.GetNamedItem(at_category) );

                if( order <= 0 )
                {
                    LOG( ERROR, LOG_CATEGORY, "CFormation::LoadXml: Invalid (negative number or 0) order defined in formation file %s. The game will try to continue anyway.", filename.c_str() );
                    continue;
                }
                --order;    //We need this to be in line with arrays, so start at 0
                //if ( category.Length() )
                    //AssignCategory(order, category);
                //else
                    AssignCategory(order, FileCatValue);

                m_slots[order].fileOff = file * m_fileSpacing;
                m_slots[order].rankOff = rank * m_rankSpacing;
                ++m_numSlots;
                ++rank;
            }
            if ( rank > maxrank )
                maxrank = rank;
            ++file;
        }   //if el_fl

        else if ( ChildName == el_blank )
            ++file;
    }

    float centerx = maxrank * m_rankSpacing / 2.0f;
    float centery = file * m_fileSpacing / 2.0f;

    //Here we check to make sure no order was skipped over.  If so, failure, because we rely
    //on a linearly accessible slots in entityformation.cpp.
    for ( int i=0; i<m_numSlots; ++i )
    {
        if ( m_slots.find(i) == m_slots.end() )
        {
            LOG( ERROR, LOG_CATEGORY, "CFormation::LoadXml: Missing orders in %s. Load failed.", filename.c_str() );
            return false;
        }
        else
        {
            m_slots[i].rankOff = m_slots[i].rankOff -centerx;
            m_slots[i].fileOff = m_slots[i].fileOff -centery;
        }

    }
    return true;
}

void CFormation::AssignCategory(int order, CStr category)
{
    category.Remove( CStr(",") );
    category = category + " ";  //So the final word will be pushed as well
    CStr temp;

    //Push categories until last space
    while ( ( temp = category.BeforeFirst(" ") ) != "" )
    {
        m_slots[order].category.push_back(temp);
        //Don't use remove because certain categories could be substrings of others
        size_t off = category.find(temp);
        category.erase( off, temp.length() );
    }
}