/*
 *   ORXONOX - the hottest 3D action shooter ever to exist
 *                    > www.orxonox.net <
 *
 *
 *   License notice:
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version 2
 *   of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 *   Author:
 *      Gani Aliguzhinov
 *   Co-authors:
 *      ...
 *
 */

#include "SectionController.h"

namespace orxonox
{

    RegisterClass(SectionController);

    //Leaders share the fact that they have Wingmans
    SectionController::SectionController(Context* context) : ActionpointController(context)
    {
        RegisterObject(SectionController);
        this->setFormationMode(FormationMode::FINGER4);

        this->myWingman_ = nullptr;
        this->myDivisionLeader_ = nullptr;
        this->bFirstAction_ = true;

    }
   
    SectionController::~SectionController()
    {
        for (WorldEntity* actionpoint : this->actionpoints_)
        {
            if (actionpoint)
                actionpoint->destroy();
        }
        this->parsedActionpoints_.clear();
        this->actionpoints_.clear();
    }

    void SectionController::action()
    {
        if (!this->getControllableEntity() || !this->isActive())
            return;

        //----If no leader, find one----  
        if (!myDivisionLeader_)
        {
            ActionpointController* newDivisionLeader = findNewDivisionLeader();

            this->myDivisionLeader_ = newDivisionLeader;
        }
        //----If have leader----
        else
        {
        }
        if (!myDivisionLeader_)
        {
            ActionpointController::action();
            if (!(this->parsedActionpoints_.empty() && this->loopActionpoints_.empty()))
            {
                if (this->myWingman_)
                {
                    this->myWingman_->takeActionpoints(this->parsedActionpoints_, this->loopActionpoints_, this->bLoop_);
                }    
            }
        }
        else if (myDivisionLeader_)
        {
            if (this->myDivisionLeader_->bKeepFormation_ || !(this->myDivisionLeader_->getAction() == Action::FIGHT 
                || this->myDivisionLeader_->getAction() == Action::FIGHTALL
                || this->myDivisionLeader_->getAction() == Action::ATTACK))
            {
                this->keepFormation();
            }
            else if (!this->myDivisionLeader_->bKeepFormation_)
            {

                if (!this->hasTarget())
                {
                    this->chooseTarget(); 
                }

            }
        }

    }

    
    //PRE: myDivisionLeader_ != 0 && myDivisionLeader_->action_ == Action::FIGHT
    //POST: this->target_ is set unless division leader doesn't have one
    void SectionController::chooseTarget()
    {
        //----If division leader fights, cover him by fighting emenies close to his target----
        Action action = this->myDivisionLeader_->getAction();

        if (action == Action::FIGHT || action == Action::FIGHTALL || action == Action::ATTACK)
        {
            Pawn* target = nullptr;
            //----if he has a target----
            if (this->myDivisionLeader_->hasTarget())
            {
                //----try to find a new target if division leader has wingman (doing fine) and no good target already set----
                if ( this->myDivisionLeader_->hasWingman() && 
                    !( this->hasTarget() && this->getTarget() != this->myDivisionLeader_->getTarget() ) )
                {
                    bool foundTarget = false;
                    //----new target should be close to division's target----
                    Vector3 divisionTargetPosition = this->myDivisionLeader_->getTarget()->getWorldPosition();
                    Gametype* gt = this->getGametype();
                    for (Pawn* pawn : ObjectList<Pawn>())
                    {
                        //----is enemy?----
                        if ( CommonController::sameTeam (this->getControllableEntity(), static_cast<ControllableEntity*>(pawn), gt) )
                            continue;           
                        //----in range?----
                        if ((pawn->getWorldPosition() - divisionTargetPosition).length() < 3000 &&
                            pawn != this->myDivisionLeader_->getTarget())
                        {
                            foundTarget = true;
                            target = pawn;
                            break; 
                        }
                    }
                    //----no target? then attack same target as division leader----
                    if (!foundTarget)
                    {
                        target = orxonox_cast<Pawn*>(this->myDivisionLeader_->getTarget());
                    }
                }
                //----if division leader doesn't have a wingman, support his fire----
                else
                {
                    target = orxonox_cast<Pawn*>(this->myDivisionLeader_->getTarget());
                }
            }
            //----If he fights but doesn't have a target, wait for him to get one----
            else
            {

            }
            this->setTarget (orxonox_cast<ControllableEntity*>(target));
        }
        else
        {
        } 
    }
    Vector3 SectionController::getFormationPosition ()
    {
        this->setFormationMode( this->myDivisionLeader_->getFormationMode() );
        this->spread_ = this->myDivisionLeader_->getSpread();
        switch (this->formationMode_){
            case FormationMode::WALL:
                return Vector3 (-2.0f*this->spread_, 0, 0);

            case FormationMode::FINGER4: 
                return Vector3 (-2.0f*this->spread_, 0, 1.0f*this->spread_);
            
            case FormationMode::DIAMOND: 
                return Vector3 (-2.0f*this->spread_, 0, 1.0f*this->spread_);

            default:
                return Vector3::ZERO;
        }
    }

    void SectionController::keepFormation()
    {
        this->bKeepFormation_ = true;
        ControllableEntity* leaderEntity = this->myDivisionLeader_->getControllableEntity();
        Vector3 targetRelativePosition = this->getFormationPosition();
        if (!leaderEntity)
            return;
        FlyingController::keepFormation(leaderEntity, targetRelativePosition);
    }

    ActionpointController* SectionController::findNewDivisionLeader()
    {

        if (!this->getControllableEntity())
            return nullptr;

        ActionpointController* closestLeader = nullptr;
        float minDistance =  std::numeric_limits<float>::infinity();
        //go through all pawns
        for (ActionpointController* controller : ObjectList<ActionpointController>())
        {
            //0ptr or not DivisionController?
            if (!controller || !(controller->getIdentifier()->getName() == "DivisionController") || !(controller->getControllableEntity()))
                continue;
            //same team?
            if ((this->getControllableEntity()->getTeam() != controller->getControllableEntity()->getTeam()))
                continue;

            //is equal to this?
            if (orxonox_cast<ControllableEntity*>(controller) == this->getControllableEntity())
                continue;

            float distance = CommonController::distance (controller->getControllableEntity(), this->getControllableEntity());
            
            if (distance < minDistance && !(controller->hasFollower()))
            {
                closestLeader = controller;
                minDistance = distance;
            }
          
        }
        if (closestLeader)
        {
            if (closestLeader->setFollower(this))
                return closestLeader;
        }
        return nullptr;
    }

    bool SectionController::setWingman(ActionpointController* newWingman)
    {

        if (!this->myWingman_)
        {
            this->myWingman_ = newWingman;
            newWingman->takeActionpoints (this->parsedActionpoints_, this->loopActionpoints_, this->bLoop_);
            return true;
        }
        else
        {
            return false;
        }
    }
    
    bool SectionController::hasWingman()
    {
        if (this->myWingman_)
            return true;
        else
            return false;
    }
}
