#include "precompiled.h"

#include "EntityFormation.h"

#include "Entity.h"
#include "FormationCollection.h"
#include "FormationManager.h"
#include "Simulation.h"
#include "ps/Game.h"
#include "ps/Interact.h"
#include "network/NetMessage.h"

CEntityFormation::CEntityFormation( CFormation*& base, size_t index )
{
    if (!base)
        return;

    m_self = m_base = base;
    m_numEntities=0;
    m_speed=0.0f;
    m_orientation = 0.0f;

    m_position.x = m_position.y = -1.0f;
    m_duplication=false;
    m_entities.resize(m_base->m_numSlots);

    m_index = (int)index;
}

CEntityFormation::~CEntityFormation()
{
    for ( int i=0; i<m_base->m_numSlots; ++i )
    {
        if ( m_entities[i] )
        {
            m_entities[i]->m_formation = -1;
            m_entities[i]->m_formationSlot = -1;
            m_entities[i] = NULL;
        }
    }
}

int CEntityFormation::GetSlotCount()
{
    return m_base->m_numSlots;
}

void CEntityFormation::SwitchBase( CFormation*& base )
{
    std::vector<CEntity*> copy;
    copy.resize( m_base->m_numSlots );
    for ( int i=0; i < m_base->m_numSlots; ++i )
    {
        if ( !m_entities[i] )
            continue;
        copy.push_back( m_entities[i] );
        RemoveUnit( m_entities[i] );
    }
    m_self = m_base = base;
    m_entities.resize(m_base->m_numSlots);
    ResetAllEntities();

    for ( std::vector<CEntity*>::iterator it=copy.begin(); it != copy.end(); it++ )
        g_FormationManager.AddUnit(*it, m_index);
}

bool CEntityFormation::AddUnit( CEntity* entity )
{
    debug_assert( entity );
    //Add the unit to the most appropriate slot
    for (int i=0; i<m_base->m_numSlots; ++i)
    {
        if ( IsBetterUnit( i, entity ) )
        {
            if ( m_entities[i] )
            {
                CEntity* prev = m_entities[i];
                RemoveUnit( m_entities[i] );
                m_entities[i] = entity;
                AddUnit( prev );
            }

            m_entities[i] = entity;
            ++m_numEntities;
            entity->m_formation = m_index;
            entity->m_formationSlot = i;

            return true;
        }
    }
    return false;
}

void CEntityFormation::RemoveUnit( CEntity* entity )
{
    if ( !(IsValidOrder(entity->m_formationSlot) && entity) )
        return;

    m_entities[entity->m_formationSlot] = NULL;
    entity->m_formation = -1;
    entity->m_formationSlot = -1;

    --m_numEntities;
    //UpdateFormation();
}

bool CEntityFormation::IsSlotAppropriate( int order, CEntity* entity )
{
    debug_assert( entity );
    if ( !IsValidOrder(order) )
        return false;

    for ( size_t idx=0; idx < m_base->m_slots[order].category.size(); ++idx )
    {
        CStr tmp( m_base->m_slots[order].category[idx] );
        if ( entity->m_classes.IsMember( tmp ) )
            return true;
    }
    return false;
}

bool CEntityFormation::IsBetterUnit( int order, CEntity* entity )
{
    if ( !( IsValidOrder(order) && entity ) )
        return false;
    //Adding to an empty slot, check if we're elligible
    if ( !m_entities[order] )
        return IsSlotAppropriate(order, entity);

    //Go through each category and check both units' classes.  Must be done in order
    //because categories are listed in order of importance
    for ( size_t idx=0; idx < m_base->m_slots[order].category.size(); ++idx )
    {
        CStr cat = m_base->m_slots[order].category[idx];
        bool current=false;
        bool newEnt=false;

        current = m_entities[order]->m_classes.IsMember( cat );
        newEnt = entity->m_classes.IsMember( cat );

        if ( current != newEnt )
            return newEnt;
    }
    return false;
}

void CEntityFormation::UpdateFormation()
{
    //Get the entities in the right order (as in, ordered correctly and in the right order/slot)
    for ( int i=1; i<m_base->m_numSlots; ++i )
    {
        if ( !m_entities[i] )
            continue;

        //Go through slots with higher order
        for ( int j=i-1; j > 0; --j )
        {
            if ( IsBetterUnit( j, m_entities[i] ) )
            {
                CEntity* temp = m_entities[j];
                m_entities[j] = m_entities[i];
                m_entities[i] = temp;

                int tmpSlot = m_entities[i]->m_formationSlot;
                m_entities[i]->m_formationSlot = m_entities[j]->m_formationSlot;
                m_entities[j]->m_formationSlot = tmpSlot;
                --i;
            }
        }
    }
    CEntityList entities = GetEntityList();
    CNetMessage* msg = CNetMessage::CreatePositionMessage( entities, NMT_FormationGoto, m_position );
    g_Game->GetSimulation()->QueueLocalCommand(msg);

}

void CEntityFormation::ResetAllEntities()
{
    for ( int i=0; i<m_base->m_numSlots; ++i )
        m_entities[i] = NULL;
}

void CEntityFormation::ResetAngleDivs()
{
    for ( int i=0; i<m_base->m_anglePenaltyDivs; ++i )
        m_angleDivs[i] = false;
}

void CEntityFormation::SelectAllUnits()
{
    for ( int i=0; i<m_base->m_numSlots; ++i )
    {
        if ( m_entities[i] && !g_Selection.IsSelected(m_entities[i]->me) )
            g_Selection.AddSelection( m_entities[i]->me );
    }
}

CEntityList CEntityFormation::GetEntityList()
{
    CEntityList ret;
    for ( int i=0; i<m_base->m_numSlots; i++ )
    {
        if ( m_entities[i] )
            ret.push_back( m_entities[i]->me);
    }
    return ret;
}

CVector2D CEntityFormation::GetSlotPosition( int order )
{
    if ( IsValidOrder(order) )
        return CVector2D ( m_base->m_slots[order].rankOff, m_base->m_slots[order].fileOff );
    return CVector2D(-1, -1);
}

void CEntityFormation::BaseToMovement()
{
    CFormation* tmp = m_self;
    CFormation* move = g_EntityFormationCollection.GetTemplate( m_base->m_movement );
    if (!move)
        return;
    SwitchBase( move );
    //SwitchBase resets m_self, so reset it so we can return to the correct base later
    m_self = tmp;
}

void CEntityFormation::ResetIndex( size_t index )
{
    m_index = (int)index;

    for ( size_t i=0; i< m_entities.size(); ++i )
    {
        if ( m_entities[i] )
            m_entities[i]->m_formation = m_index;
    }
}

/*
inline void CEntityFormation::SetDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* argv )
{
    m_duplication=ToPrimitive<bool>( argv[0] );
}
inline bool CEntityFormation::IsDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
    return m_duplication;
}*/