/*
 *   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 "controllers/CommonController.h"

//here starts stuff for sameTeam function copied from FormationController
#include "gametypes/TeamDeathmatch.h"
#include "gametypes/Gametype.h"
#include "controllers/DroneController.h"
#include "gametypes/Dynamicmatch.h"

#include "worldentities/pawns/TeamBaseMatchBase.h"

namespace orxonox
{
    const float CommonController::HARDCODED_PROJECTILE_SPEED = 750;

    RegisterClass(CommonController);
    CommonController::CommonController(Context* context): Controller(context)
    {
        RegisterObject(CommonController);
    }
    CommonController::~CommonController() 
    {
        //no member variables - nothing to destroy
    }
    /**
    @brief
      PRE: a < b.
      returns random float between a and b.
    */
    float CommonController::randomInRange(float a, float b)
    {
        return a + rnd(1.0f) * (b - a);
    }
    /**
    @brief
      returns distance between two entities, if either is zero pointer, returns infinity
    */    
    float CommonController::distance (const ControllableEntity* entity1, const ControllableEntity* entity2)
    {
        if (!entity1 || !entity2)
            return std::numeric_limits<float>::infinity();
        return (entity1->getPosition() - entity2->getPosition()).length();
    }
    /**
    @brief
      bad function from FormationController that returns true if both entities have same team
    */    
    bool CommonController::sameTeam (ControllableEntity* entity1, ControllableEntity* entity2, Gametype* gametype)
    {
        
        if (!entity1 || !entity2)
            return false;
        if (entity1 == entity2)
            return true;

        int team1 = entity1->getTeam();
        int team2 = entity2->getTeam();

        Controller* controller = nullptr;
        if (entity1->getController())
            controller = entity1->getController();
        else
            controller = entity1->getXMLController();
        if (controller)
        {
            if (controller->getIdentifier()->getName() == "MasterController")
                return true;
            CommonController* ac = orxonox_cast<CommonController*>(controller);
            if (ac)
                team1 = ac->getTeam();
        }

        if (entity2->getController())
            controller = entity2->getController();
        else
            controller = entity2->getXMLController();
        if (controller)
        {
            if (controller->getIdentifier()->getName() == "MasterController")
                return true;
            CommonController* ac = orxonox_cast<CommonController*>(controller);
            if (ac)
                team2 = ac->getTeam();
        }

        TeamGametype* tdm = orxonox_cast<TeamGametype*>(gametype);
        if (tdm)
        {
            if (entity1->getPlayer())
                team1 = tdm->getTeam(entity1->getPlayer());

            if (entity2->getPlayer())
                team2 = tdm->getTeam(entity2->getPlayer());
        }

        TeamBaseMatchBase* base = nullptr;
        base = orxonox_cast<TeamBaseMatchBase*>(entity1);
        if (base)
        {
            switch (base->getState())
            {
                case BaseState::ControlTeam1:
                    team1 = 0;
                    break;
                case BaseState::ControlTeam2:
                    team1 = 1;
                    break;
                case BaseState::Uncontrolled:
                default:
                    team1 = -1;
            }
        }
        base = orxonox_cast<TeamBaseMatchBase*>(entity2);
        if (base)
        {
            switch (base->getState())
            {
                case BaseState::ControlTeam1:
                    team2 = 0;
                    break;
                case BaseState::ControlTeam2:
                    team2 = 1;
                    break;
                case BaseState::Uncontrolled:
                default:
                    team2 = -1;
            }
        }

        DroneController* droneController = nullptr;
        droneController = orxonox_cast<DroneController*>(entity1->getController());
        if (droneController && static_cast<ControllableEntity*>(droneController->getOwner()) == entity2)
            return true;
        droneController = orxonox_cast<DroneController*>(entity2->getController());
        if (droneController && static_cast<ControllableEntity*>(droneController->getOwner()) == entity1)
            return true;
        DroneController* droneController1 = orxonox_cast<DroneController*>(entity1->getController());
        DroneController* droneController2 = orxonox_cast<DroneController*>(entity2->getController());
        if (droneController1 && droneController2 && droneController1->getOwner() == droneController2->getOwner())
            return true;

        Dynamicmatch* dynamic = orxonox_cast<Dynamicmatch*>(gametype);
        if (dynamic)
        {
            if (dynamic->notEnoughPigs||dynamic->notEnoughKillers||dynamic->notEnoughChasers) {return false;}

            if (entity1->getPlayer())
                team1 = dynamic->getParty(entity1->getPlayer());

            if (entity2->getPlayer())
                team2 = dynamic->getParty(entity2->getPlayer());

            if (team1 ==-1 ||team2 ==-1) {return false;}
            else if (team1 == dynamic->chaser && team2 != dynamic->chaser) {return false;}
            else if (team1 == dynamic->piggy && team2 == dynamic->chaser) {return false;}
            else if (team1 == dynamic->killer && team2 == dynamic->chaser) {return false;}
            else return true;
        }

        return (team1 == team2 && team1 != -1);
    }
    /**
    @brief
      returns true if entityThatLooks does look at entityBeingLookeAt with a tolerance of angle.
    */    
    bool CommonController::isLooking(const ControllableEntity* entityThatLooks, const ControllableEntity* entityBeingLookedAt, float angle)
    {
        if (!entityThatLooks || !entityBeingLookedAt)
            return false;
        return (getAngle(entityThatLooks ->getPosition() , 
            entityThatLooks->getOrientation()  * WorldEntity::FRONT, 
            entityBeingLookedAt->getWorldPosition()) < angle);
    }
    /**
    @brief
      returns a name of a Pawn entity, if no name set, returns string representing address of the Pawn.
    */    
    std::string CommonController::getName(const Pawn* entity)
    {
        std::string name = entity->getName();
        if (name == "")
        {
            const void * address = static_cast<const void*>(entity);
            std::stringstream ss;
            ss << address;  
            name = ss.str();            
        }
        return name;
    }
}
