/*
 *   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:
 *      Fabian 'x3n' Landau
 *   Co-authors:
 *      ...
 *
 */

#ifndef _Pawn_H__
#define _Pawn_H__

#include "OrxonoxPrereqs.h"

#include <string>
#include <vector>
#include "interfaces/PickupCarrier.h"
#include "interfaces/RadarViewable.h"
#include "worldentities/ControllableEntity.h"
#include "worldentities/ExplosionPart.h"


namespace orxonox // tolua_export
{
    /**
    @brief
        Everything in Orxonox that has a health attribute is a Pawn. After a Pawn is spawned its health is set to
        its initial health. In every call of the Pawns tick function the game checks whether the pawns health is at
        or below zero. If it is, the pawn gets killed.

        Pawns can carry pickups and fire weapons. They can also have shields.

        Notice that every Pawn is a ControllableEntity.
    */

    // tolua_export
    class _OrxonoxExport Pawn // tolua_export
        : public ControllableEntity, public RadarViewable, public PickupCarrier
    { // tolua_export
        friend class WeaponSystem;

        public:
            Pawn(Context* context);
            virtual ~Pawn();

            virtual void XMLPort(Element& xmlelement, XMLPort::Mode mode) override;
            virtual void XMLEventPort(Element& xmlelement, XMLPort::Mode mode) override;
            virtual void tick(float dt) override;

            inline bool isAlive() const
                { return this->bAlive_; }


            void setHealth(float health);
            inline void addHealth(float health)
                { this->setHealth(this->health_ + health); }
            inline void removeHealth(float health)
                { this->setHealth(this->health_ - health); }
            inline float getHealth() const
                { return this->health_; }

            inline void setMaxHealth(float maxhealth)
                { this->maxHealth_ = maxhealth; this->setHealth(this->health_); }
            inline float getMaxHealth() const
                { return this->maxHealth_; }

            inline void setInitialHealth(float initialhealth)
                { this->initialHealth_ = initialhealth; this->setHealth(initialhealth); }
            inline float getInitialHealth() const
                { return this->initialHealth_; }

            void setShieldHealth(float shieldHealth);

            inline float getShieldHealth()
                { return this->shieldHealth_; }

            inline void addShieldHealth(float amount)
                { this->setShieldHealth(this->shieldHealth_ + amount); }

            inline bool hasShield()
                { return (this->getShieldHealth() > 0); }

            void setMaxShieldHealth(float maxshieldhealth);
            inline float getMaxShieldHealth() const
                { return this->maxShieldHealth_; }

            inline void setInitialShieldHealth(float initialshieldhealth)
                { this->initialShieldHealth_ = initialshieldhealth; this->setShieldHealth(initialshieldhealth); }
            inline float getInitialShieldHealth() const
                { return this->initialShieldHealth_; }

            inline void restoreInitialShieldHealth()
                { this->setShieldHealth(this->initialShieldHealth_); }
            inline void restoreMaxShieldHealth()
                { this->setShieldHealth(this->maxShieldHealth_); }

            inline void setShieldAbsorption(float shieldAbsorption)
                { this->shieldAbsorption_ = shieldAbsorption; }
            inline float getShieldAbsorption()
                { return this->shieldAbsorption_; }

            void setShieldRechargeRate(float shieldRechargeRate);
            inline float getShieldRechargeRate() const
                { return this->shieldRechargeRate_; }

            void setShieldRechargeWaitTime(float shieldRechargeWaitTime);
            inline float getShieldRechargeWaitTime() const
                { return this->shieldRechargeWaitTime_; }

            inline void resetShieldRechargeCountdown()
                { this->shieldRechargeWaitCountdown_ = 0; }

            inline void startShieldRechargeCountdown()
                { this->shieldRechargeWaitCountdown_ = this->getShieldRechargeWaitTime(); } // TODO: Implement in Projectile.cc

            void decreaseShieldRechargeCountdownTime(float dt);

            /** @brief Sets the state of the pawns vulnerability. @param bVulnerable */
            inline void setVulnerable(bool bVulnerable)
            {
                if (this->bVulnerable_ != bVulnerable)
                {
                    this->bVulnerable_ = bVulnerable;
                    this->changedVulnerability();
                }
            }
            /** @brief Returns the state of the pawns vulnerability. @return The state of the vulnerability */
            inline const bool& isVulnerable() const { return this->bVulnerable_; }
            /** @brief This function gets called if the vulnerability of the pawn changes. */
            virtual void changedVulnerability();

            inline ControllableEntity* getLastHitOriginator() const
                { return this->lastHitOriginator_; }

            virtual void hit(Pawn* originator, const Vector3& force, const btCollisionShape* cs, float damage, float healthdamage = 0.0f, float shielddamage = 0.0f);
            virtual void hit(Pawn* originator, btManifoldPoint& contactpoint, const btCollisionShape* cs, float damage, float healthdamage = 0.0f, float shielddamage = 0.0f);

            virtual void kill();

            virtual void fired(unsigned int firemode) override;
            virtual void postSpawn();

            void addExplosionPart(ExplosionPart* ePart);
            ExplosionPart * getExplosionPart();

            void addWeaponSlot(WeaponSlot * wSlot);
            WeaponSlot * getWeaponSlot(unsigned int index) const;
            void addWeaponSet(WeaponSet * wSet);
            WeaponSet * getWeaponSet(unsigned int index) const;
            void addWeaponPack(WeaponPack * wPack);
            void addWeaponPackXML(WeaponPack * wPack);
            WeaponPack * getWeaponPack(unsigned int index) const;

            void addMunitionXML(Munition* munition);
            Munition* getMunitionXML() const;
            
            Munition* getMunition(SubclassIdentifier<Munition> * identifier);

            virtual void addedWeaponPack(WeaponPack* wPack) {}

            inline void setSpawnParticleSource(const std::string& source)
                { this->spawnparticlesource_ = source; }
            inline const std::string& getSpawnParticleSource() const
                { return this->spawnparticlesource_; }

            inline void setSpawnParticleDuration(float duration)
                { this->spawnparticleduration_ = duration; }
            inline float getSpawnParticleDuration() const
                { return this->spawnparticleduration_; }

            inline void setExplosionChunks(unsigned int chunks)
                { this->numexplosionchunks_ = chunks; }
            inline unsigned int getExplosionChunks() const
                { return this->numexplosionchunks_; }

            // These are used with the Damage Boost Pickup to use the damage multiplier.
            inline void setDamageMultiplier(float multiplier)
                { this->damageMultiplier_ = multiplier; }
            inline float getDamageMultiplier() const
                { return this->damageMultiplier_; }


            virtual void startLocalHumanControl() override;

            void setAimPosition( Vector3 position )
                { this->aimPosition_ = position; }
            Vector3 getAimPosition()
                { return this->aimPosition_; }

            virtual const Vector3& getCarrierPosition(void) const override
                { return this->getWorldPosition(); };

            virtual void changedVisibility() override;

            void setExplosionSound(const std::string& engineSound);
            const std::string& getExplosionSound();

            inline const WeaponSystem* getWeaponSystem() const
                { return this->weaponSystem_; }

            static void consoleCommand_debugDrawWeapons(bool bDraw);

        protected:
            virtual void preDestroy() override;

            virtual void setPlayer(PlayerInfo* player) override;
            virtual void removePlayer() override;

            virtual void death();
            virtual bool hasSlaves();
            virtual Controller* getSlave();
            virtual void goWithStyle();
            virtual void spawneffect();

            virtual void damage(float damage, float healthdamage = 0.0f, float shielddamage = 0.0f, Pawn* originator = nullptr, const btCollisionShape* cs = nullptr);

            bool bAlive_;
            bool bVulnerable_; ///< If this is false, then the pawn may not take damage

            virtual std::vector<PickupCarrier*>* getCarrierChildren(void) const override
                { return new std::vector<PickupCarrier*>(); }
            virtual PickupCarrier* getCarrierParent(void) const override
                { return nullptr; }


            float health_;
            float maxHealth_;
            float initialHealth_;

            float shieldHealth_;
            float maxShieldHealth_;
            float initialShieldHealth_;
            float shieldAbsorption_; ///< Has to be between 0 and 1
            float shieldRechargeRate_;
            float shieldRechargeWaitTime_;
            float shieldRechargeWaitCountdown_;

            float damageMultiplier_; ///< Used by the Damage Boost Pickup.

            WeakPtr<Pawn> lastHitOriginator_;

            WeaponSystem* weaponSystem_;

            std::string spawnparticlesource_;
            float spawnparticleduration_;
            unsigned int numexplosionchunks_;

            std::vector<ExplosionPart*> explosionPartList_;

        private:
            void registerVariables();
            inline void setWeaponSystem(WeaponSystem* weaponsystem)
                { this->weaponSystem_ = weaponsystem; }
            void drawWeapons(bool bDraw);

            Vector3 aimPosition_;

            WorldSound* explosionSound_; // TODO: Does this really belong here? Maybe move it to BigExplosion?

            std::vector<Model*> debugWeaponSlotModels_;

    }; // tolua_export
} // tolua_export

#endif /* _Pawn_H__ */
