/*
 *   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:
 *      ...
 *
 */

#ifndef _ActionpointController_H__
#define _ActionpointController_H__

#include "controllers/FightingController.h"
#include "tools/Timer.h"
#include "tools/interfaces/Tickable.h"
#include "../modules/pickup/PickupSpawner.h"
#include <map>

namespace orxonox
{
    /**
    @brief
        ActionpointController is a state machine with states:
            1) NONE
            2) FLY: fly towards a point
            3) FIGHT: fight enemies that are in attackRange_ (see FightingController)
            4) PROTECT: follow this->protect_
            5) FIGHTALL: fight all enemies on the map
            6) ATTACK: fight a specific spaceship
        This controller always executes an action that is in the back of the vector being used.
        After current this->action_ is completed, next action becomes the top action (one that will
        be returned by someVector.back()), and current action either will be removed (if not looping),
        or moved to the top (if looping).

        Every second action(), which is once in two seconds, this searches the area for enemies that are in attack range, if finds anyone,
        pushes Action::FIGHT to the stack. That makes spaceship fight enemies inside of a sphere, and when all enemies in range are dead,
        Action::FIGHT is removed from the stack, and spaceship resumes doing whatever action was being executed before.

        In XML one has to attack Actionpoints in order to achieve any complex behaviour, but in Controller all actionpoints are effectively 
        being stored in an array of type Point::Value.
    @note
        ActionpointController will not work, if there is no MasterController in the level!
        All the demos are in a file called AITest.oxw. In the menu look for New AI Testing Level.
    */
    enum class Action
    {  
        NONE, FLY, FIGHT, PROTECT, FIGHTALL, ATTACK
    };
    
    struct Point {
        Action action;
        std::string name;
        Vector3 position;
        bool inLoop;
    };

    class _OrxonoxExport ActionpointController : public FightingController, public Tickable
    {
        public:
           
            ActionpointController(Context* context);
            virtual ~ActionpointController();
            virtual void XMLPort(Element& xmlelement, XMLPort::Mode mode) override;
               
            /**
            @brief
                tick is called every tick by Game (?).
                In tick ship flies and fires.
            */
            virtual void tick(float dt) override;
            /**
            @brief
                XML method, example XML usage:

                @code
                <SpaceShip position="-2000, 1500, -1000" lookat="0,0,0" team=0 name="ss2">
                  <templates>
                    <Template link=spaceshipassff />
                  </templates>
                  <controller>
                    <DivisionController team=0 formationMode="finger4">
                      <actionpoints>
                        <Actionpoint position="0,0,0" action="FLY" /> 
                        <Actionpoint position="-1000,750,-500" action="ATTACK" attack="attack" />
                        <Actionpoint position="-1000,750,-500" action="PROTECt" protectMe=true />
                        <Actionpoint position="-1000,750,-500" action="PROTECt" protect="protect" /> 
                        <Actionpoint position="-1000,750,-500" action="FIGHTALL" />
                       </actionpoints>
                    </DivisionController>
                  </controller>
                </SpaceShip>
                @endcode
                
                Full description:
                Adds an Actionpoint to this->actionpoints_. Actionpoint can take arguments like action="attack" attack="name".
                For documentation on Actionpoint XML arguments, check out Actionpoint.h class
                If any WorldEntity that is not Actionpoint or its child being sent to actionpoints through XML,
                action would be assumed to be Action::FLY and target position to be position of the entity. Also, if not Actionpoint
                is passed, it is assumed to be in a loop. How it works is: in \<actionpoints\> first all Actionpoints between 
                first Actionpoint with loopStart=true and first following Actionpoint with loopEnd=true are included in a single loop.
                If they are adjacent (in the input array) with WorldEntity, then WorldEntity is also in a loop.
                All the Worldentities are assumed to be in loop. 
               
                Loop example:

                @code
                <SpaceShip position="-1500, 1500, -1000" lookat="0,0,0" team=0 name="ss1">
                  <templates>
                    <Template link=spaceshipassff />
                  </templates>
                  <controller>
                    <DivisionController team=0 formationMode="wall">
                      <actionpoints>
                        <Actionpoint position="  0,2000,-600" action="FLY" loopStart=true/>
                        <Actionpoint position="  0,2000,-1000" action="FLY"  />
                        <Actionpoint position="400,2000,-1000" action="FLY" />
                        <Actionpoint position="400,2000,-600" action="FLY" loopEnd=true />
                      </actionpoints>
                    </DivisionController>
                  </controller>
                </SpaceShip>
                @endcode
               
                other loop example:

                @code
                <SpaceShip position="-1500, -1500, -1500" lookat="0,0,0" team=0 name="ss1">
                  <templates>
                    <Template link=spaceshipassff />
                  </templates>
                  <controller>
                    <DivisionController team=0 formationMode="diamond">
                      <actionpoints>
                        <Model mesh="cube.mesh" scale=8 position="  0,2000,-600" />
                        <Model mesh="cube.mesh" scale=8 position="  0,2000,-1000" />
                        <Model mesh="cube.mesh" scale=8 position="400,2000,-1000" />
                        <Model mesh="cube.mesh" scale=8 position="400,2000,-600" />
                      </actionpoints>
                    </DivisionController>
                  </controller>
                </SpaceShip>
                @endcode

            @note
                Don't use several loops, and don't use WorldEntities as input to \<actionpoints\> as I didn't test it well, but you
                can try if feeling lucky.  
            */
            void addActionpoint(WorldEntity* actionpoint); 
            WorldEntity* getActionpoint(unsigned int index) const;    
            void setDefaultFightAll(bool value)
                { this->bDefaultFightAll_ = value; }
            bool getDefaultFightAll ()
                { return this->bDefaultFightAll_; }
            void setDefaultPatrol(bool value)
                { this->bDefaultPatrol_ = value; }
            bool getDefaultPatrol ()
                { return this->bDefaultPatrol_; }
               

            virtual void stayNearProtect();
            virtual void action(); //<! action() is called in regular intervals managing the bot's behaviour. Only gets called by MasterController
            virtual void takeActionpoints (const std::vector<Point>& vector, const std::vector<Point>& loop, bool b);

            virtual Action getAction ();
            virtual std::string getActionName();

            void setAction (Action action);
            void setAction (Action action, ControllableEntity* target);
            void setAction (Action action, const Vector3& target);
            void setAction (Action action, const Vector3& target,  const Quaternion& orient );

            virtual bool setWingman(ActionpointController* wingman)
                { return false; }
            virtual bool hasWingman()
                { return true; }
            virtual bool setFollower(ActionpointController* myFollower)
                { return false; }
            virtual bool hasFollower()
                { return true; }


        protected:
                void startAttackingEnemiesThatAreClose();
                WeakPtr<ActionpointController> myWingman_;
                WeakPtr<ActionpointController> myFollower_;
                WeakPtr<ActionpointController> myDivisionLeader_;
            //----[Actionpoint information]----
                Action action_;
                std::string protectName_;
                std::string targetName_;
                std::vector<WeakPtr<WorldEntity>> actionpoints_;
                float squaredaccuracy_;
                std::vector<Point> parsedActionpoints_; //<! actionpoints as they are stored here after being parsed from XML
                std::vector<Point> loopActionpoints_;   //<! actionpoints that are to be looped
                bool bInLoop_;                          //<! variable for addActionpoint method
                bool bLoop_;                            //<! is state machine looping?
                bool bEndLoop_;                         //<! variable for addActionpoint method
                bool bTakenOver_;                       //<! are actionpoints taken over from the leader when he died? if yes, top actionpoint 
                                                        //<! is to be executed for the state machine to start working
            //----[/Actionpoint information]----
                void setProtect (ControllableEntity* protect);
                ControllableEntity* getProtect (); 
                WeakPtr<ControllableEntity> protect_;   //<! entity that is to be protected if this->action_ == Action::PROTECT
                void fillLoop();                        //<! moves actionpoints that are should be in loop from parsedActionpoints_ to loopActionpoints_
                void fillLoopReversed();
                void moveBackToTop();                   //<! analog of removing back actionpoint for loopActionpoints_: instead of removing it, 
                                                        //<! move it to the top, so that it will be executed later on.
                void setClosestTarget();
                Pawn* closestTarget();
            //----[Actionpoint methods]----
                /**
                @brief
                    Sets this->target_, this->targetPosition_, this->protect_ and this->action_ depending
                    on the current actionpoint in the vector parsedActionpoints_ if not looping or
                    loopActionpoints_ if looping.
                @note
                */
                void executeActionpoint();  
                /**
                @brief
                    If this->bLoop_, move back action to top (back is the current one, top is the last),
                    otherwise remove back actionpoint.
                @note
                    actionpoints_ is only used for XML, real state stacks are parsedActionpoints_ and loopActionpoints_
                */            
                void nextActionpoint();                 
            //----[Actionpoint methods]----          

                bool bDefaultFightAll_;     //<! if true, when no action set, this will fight all

                bool bPatrolling_;        //<! true if current action_ is FIGHT because this found enemies that are close, need this to correctly go back to looping if was looping
                bool bDefaultPatrol_;       //<! if true, this will look out for enemies that are close if this is just flying or doing nothing
                unsigned int ticks_;     //<! local tick counter            
    };
}

#endif /* _ActionpointController_H__ */
