/*
 *   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:
 *      Vedat Aydin
 *   Co-authors:
 *      ...
 *
 */



#include "ExplosionPart.h"
#include "core/XMLPort.h"


namespace orxonox
{

    RegisterClass(ExplosionPart);

    ExplosionPart::ExplosionPart(Context* context) : MovableEntity(context)
    {
        RegisterObject(ExplosionPart);
        this->bStop_ = false;
        this->LOD_ = LODParticle::Normal;
        this->mesh_ = "";
        this->effect1_ = "";
        this->effect2_ = "";
        this->model_= new Model(this->getContext());
        this->effect1Particle_= nullptr;
        this->effect2Particle_= nullptr;
        this->explosionEntity_ = new MovableEntity(this->getContext());
        this->posOffset_ = Vector3::ZERO;



    }


    ExplosionPart::~ExplosionPart()
    {
        if (this->isInitialized())
        {
            if (this->effect1Particle_)
            {
                this->model_->detachOgreObject(this->effect1Particle_->getParticleSystem());
                delete this->effect1Particle_;
            }
            if (this->effect2Particle_)
            {
                this->model_->detachOgreObject(this->effect2Particle_->getParticleSystem());
                delete this->effect2Particle_;
            }
        }
    }


    void ExplosionPart::XMLPort(Element& xmlelement, XMLPort::Mode mode)
    {
        SUPER(ExplosionPart, XMLPort, xmlelement, mode);

        XMLPortParam(ExplosionPart, "mesh", setMesh, getMesh, xmlelement, mode).defaultValues(""); //Define mesh file, that is going to fly out
        XMLPortParam(ExplosionPart, "minspeed", setMinSpeed, getMinSpeed, xmlelement, mode).defaultValues(50); //Minimum velocity at which parts fly out
        XMLPortParam(ExplosionPart, "maxspeed", setMaxSpeed, getMaxSpeed, xmlelement, mode).defaultValues(100); //Maximum velocity at which parts fly out, set both minspeed and maxspeed to 0 to have stationary effects
        XMLPortParam(ExplosionPart, "effect1", setEffect1, getEffect1, xmlelement, mode).defaultValues(""); //particle effect 1
        XMLPortParam(ExplosionPart, "effect2", setEffect2, getEffect2, xmlelement, mode).defaultValues(""); //particle effect 2
        XMLPortParam(ExplosionPart, "offset", setOffset, getOffset, xmlelement, mode).defaultValues(Vector3::ZERO); //Offset of the position if you need to have an explosion off-center
        XMLPortParam(ExplosionPart, "direction", setDirection, getDirection, xmlelement, mode).defaultValues(Vector3(1,1,1)); //general direction the parts fly in
        XMLPortParam(ExplosionPart, "angle", setAngle, getAngle, xmlelement, mode).defaultValues(180); //defines a cone shape with direction "direction" and angle "angle" inside which the parts fly out of
        XMLPortParam(ExplosionPart, "delay", setDelay, getDelay, xmlelement, mode).defaultValues(0); //delay to the explosion in seconds
    }


    void ExplosionPart::Explode()
    {
        this->destroyTimer_.setTimer(delay_, false, createExecutor(createFunctor(&ExplosionPart::ActuallyExplode, this)));
    }

    void ExplosionPart::stop()
    {
        if (this->effect1Particle_)
            this->effect1Particle_->setEnabled(false);
        if (this->effect2Particle_)
            this->effect2Particle_->setEnabled(false);
        if (this->model_)
            this->model_->setVisible(false);

        if (GameMode::isMaster())
        {
            this->bStop_ = true;
            this->destroyTimer_.setTimer(1.0f, false, createExecutor(createFunctor(&ExplosionPart::destroy, this)));
        }
    }

    void ExplosionPart::ActuallyExplode()
    {
        this->model_->setVisible(true);

        //this->explosionEntity_->setSyncMode(0);

        //this->model_->setSyncMode(0);


        if(effect1_ != "")
        {
            this->effect1Particle_ = new ParticleInterface(this->getScene()->getSceneManager(), effect1_, this->LOD_);
            this->effect1Particle_->setDimensions(this->getScale());
            this->model_->attachOgreObject(this->effect1Particle_->getParticleSystem());
        }

        if(effect2_ != "")
        {
            this->effect2Particle_ = new ParticleInterface(this->getScene()->getSceneManager(), effect2_, this->LOD_);
            this->effect2Particle_->setDimensions(this->getScale());
            this->model_->attachOgreObject(this->effect2Particle_->getParticleSystem());
        }




        Vector3 velocityOffset = direction_.perpendicular();
        velocityOffset.normalise();
        Degree offsetDirection = Degree(rnd(0,360));
        velocityOffset = Quaternion(offsetDirection, direction_.normalisedCopy()) * velocityOffset;
        velocityOffset.normalise();
        direction_.normalise();

        Vector3 finalDirection = direction_ + sin((rnd(0, angle_))*math::pi/180)*velocityOffset;

        this->explosionEntity_->setVelocity(finalDirection*rnd(minSpeed_,maxSpeed_));
        this->explosionEntity_->setAngularVelocity(Vector3(rnd(-1, 1), rnd(-1, 1), rnd(-1, 1)).normalisedCopy() * Degree(400).valueRadians());
        this->explosionEntity_->setScale(this->getScale() * 4);


        this->explosionEntity_->attach(model_);


        this->attach(explosionEntity_);

        if (GameMode::isMaster())
        {
            this->destroyTimer_.setTimer(rnd(2, 4), false, createExecutor(createFunctor(&ExplosionPart::stop, this)));
        }
    }




    void ExplosionPart::setMesh(const std::string& newString)
    {
        if(newString != "")
        {
            this->mesh_ = newString;
            this->model_->setMeshSource(mesh_);
            this->model_->setVisible(false);
        }
    }

    void ExplosionPart::setEffect1(const std::string& newString)
    {
        this->effect1_ = newString;
    }

    void ExplosionPart::setEffect2(const std::string& newString)
    {
        this->effect2_ = newString;
    }

    void ExplosionPart::setMinSpeed(float speed)
    {
        this->minSpeed_ = speed;
    }

    void ExplosionPart::setMaxSpeed(float speed)
    {
        this->maxSpeed_ = speed;
    }

    void ExplosionPart::setOffset(Vector3 newVector)
    {
        this->posOffset_ = newVector;
        this->explosionEntity_->setPosition(this->getPosition() + this->posOffset_ / this->getScale());
    }

    void ExplosionPart::setDirection(Vector3 newDirection)
    {
        this->direction_ = newDirection;
    }

    void ExplosionPart::setAngle(float newAngle)
    {
        this->angle_ = newAngle;
    }

    void ExplosionPart::setDelay(float newDelay)
    {
        this->delay_ = newDelay;
    }

    std::string& ExplosionPart::getMesh()
    { return this->mesh_; }

    std::string& ExplosionPart::getEffect1()
    { return this->effect1_; }

    std::string& ExplosionPart::getEffect2()
    { return this->effect2_; }

    float ExplosionPart::getMinSpeed()
    {
        return this->minSpeed_;
    }

    float ExplosionPart::getMaxSpeed()
    {
        return this->maxSpeed_;
    }

    Vector3 ExplosionPart::getOffset()
    {
        return this->posOffset_;
    }

    Vector3 ExplosionPart::getDirection()
    {
        return direction_;
    }

    float ExplosionPart::getAngle()
    {
        return angle_;
    }

    float ExplosionPart::getDelay()
    {
        return delay_;
    }



}
