/*

--------
|SKRUZD|
--------

The MIT License

Copyright (c) 2006 Inko Illarramendi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/


#include <crystalspace.h>

#include <physicallayer/entity.h>
#include <physicallayer/propclas.h>
#include <propclass/linmove.h>
#include <propclass/prop.h>
#include <propclass/trigger.h>
#include <propclass/region.h>

#include <propclass/tooltip.h>
#include <propclass/timer.h>
#include <propclass/move.h>
#include <propclass/input.h>
#include <propclass/solid.h>
#include <propclass/colldet.h>

#include "behave.h"
#include "bplayer.h"




BehaviourPlayer::BehaviourPlayer (iCelEntity* entity, BehaviourLayer* bl, iCelPlLayer* pl)
        : BehaviourCommon (entity, bl, pl, app)//we get the variables from the superclass
{
    ///We take the IDs of the messages to use them later in the program.
    id_pccommandinput_forward1 = pl->FetchStringID ("pccommandinput_forward1");
    id_pccommandinput_forward0 = pl->FetchStringID ("pccommandinput_forward0");
    id_pccommandinput_backward1 = pl->FetchStringID ("pccommandinput_backward1");
    id_pccommandinput_backward0 = pl->FetchStringID ("pccommandinput_backward0");
    id_pccommandinput_rotateleft1 = pl->FetchStringID ("pccommandinput_rotateleft1");
    id_pccommandinput_rotateleft0 = pl->FetchStringID ("pccommandinput_rotateleft0");
    id_pccommandinput_rotateright1 = pl->FetchStringID ("pccommandinput_rotateright1");
    id_pccommandinput_rotateright0 = pl->FetchStringID ("pccommandinput_rotateright0");
    id_pccommandinput_cammode1 = pl->FetchStringID ("pccommandinput_cammode1");
    id_pccommandinput_shot1 = pl->FetchStringID ("pccommandinput_shot1");
    id_pccommandinput_shot0 = pl->FetchStringID ("pccommandinput_shot0");
    id_pccommandinput_jump1 = pl->FetchStringID ("pccommandinput_jump1");
    id_pccommandinput_jump0 = pl->FetchStringID ("pccommandinput_jump0");

    id_pctimer_wakeupframe = pl->FetchStringID ("pctimer_wakeupframe");
    id_pctimer_wakeup = pl->FetchStringID ("pctimer_wakeup");

    id_pclinearmovement_collision = pl->FetchStringID ("pclinearmovement_collision");
}


void BehaviourPlayer::GetActorMove ()
{/// Get the iPcActorMove property class of the player entity
    if (!pcactormove)
    {
        pcactormove = CEL_QUERY_PROPCLASS_ENT (entity, iPcActorMove);
    }
}


void BehaviourPlayer::GetMesh ()
{/// Get the iPcMesh property class of the player entity
    if (!pcmesh)
    {
        pcmesh = CEL_QUERY_PROPCLASS_ENT (entity, iPcMesh);
    }
}


void BehaviourPlayer::Weapon ()
{/// We activate the weapon of the player.

    //we find the weapon entity and we get properties from it
    iCelEntity* weapon_entity = pl->FindEntity ("weapon");
    csRef<iPcMesh> pcmesh = CEL_QUERY_PROPCLASS_ENT (
                                weapon_entity, iPcMesh);
    csRef<iPcTimer> pctimer = CEL_QUERY_PROPCLASS_ENT (
                                  weapon_entity, iPcTimer);
    csRef<iPcLinearMovement> pcweaponlinmov = CEL_QUERY_PROPCLASS_ENT (
                weapon_entity, iPcLinearMovement);

    //we get the player and its properties
    iCelEntity* player_entity = pl->FindEntity ("player");
    csRef<iPcMesh> player_pcmesh = CEL_QUERY_PROPCLASS_ENT (
                                       player_entity, iPcMesh);
    csRef<iPcActorMove> player_pcactormove = CEL_QUERY_PROPCLASS_ENT (
                player_entity, iPcActorMove);
    csRef<iPcLinearMovement> pcplayerlinmov = CEL_QUERY_PROPCLASS_ENT (
                player_entity, iPcLinearMovement);

    //we get the position of the player
    csVector3 pos;
    iSector* sector;
    float yrot;
    pcplayerlinmov-> GetLastFullPosition ( pos, yrot ,sector );

    //we set the player at slow speed while it is shooting.
    player_pcactormove->SetMovementSpeed (0.1f);
    player_pcactormove->SetRunningSpeed (0.1f);
    player_pcactormove->SetRotationSpeed (0.1f);
    if (player_pcactormove-> IsMovingForward () )
        player_pcactormove -> Forward (true);
    else if (player_pcactormove-> IsMovingBackward ())
        player_pcactormove -> Backward (true);
    if (player_pcactormove-> IsRotatingLeft ())
        player_pcactormove -> RotateLeft (true);
    else if (player_pcactormove-> IsRotatingRight ())
        player_pcactormove -> RotateRight (true);

    // We calculate the source position for the weapon, in front (z: -0.5) and
    //at the height of the body (y: 1) and we set it.
    pos = player_pcmesh->GetMesh ()->GetMovable ()->GetTransform ()
          .This2Other (csVector3 (0,1,-0.5));
    sector = player_pcmesh->GetMesh ()->GetMovable ()->GetSectors ()->Get (0);
    pcweaponlinmov -> SetFullPosition (pos, yrot, sector);
    //We set the timers for the weapon. The rest will be done in its behaviour
    pctimer-> WakeUp(500, false); //the ending time of the weapon
    pctimer-> WakeUpFrame (0); //to check every frame the enemies in range

    // Show the weapon.
    pcmesh->Show();

}


bool BehaviourPlayer::SendMessage (csStringID msg_id,
                                   iCelPropertyClass* pc,
                                   celData& ret, iCelParameterBlock* params, va_list arg)
{/// We will check the key inputs; also the enemies and shoots in range
    /// of the player to check if there is any hit.
    static bool jumping=false;

    GetActorMove ();
    GetMesh();
    iCelEntity* player_entity = pl->FindEntity ("player");

    if (msg_id == id_pctimer_wakeupframe)
    {
        // We get the center position of the player
        iSector* sector = pcmesh->GetMesh ()
                          ->GetMovable ()->GetSectors ()->Get (0);
        csVector3 pos = pcmesh->GetMesh ()->GetMovable ()->GetTransform ()
                        .This2Other (csVector3 (0, 0.6, 0));
        // We set a box with its center in player's center to check entities on it.
        csBox3 box (-0.25, 0, -0.25, 0.25, 1.2, 0.25);
        box.SetCenter(pos);
        csRef<iCelEntityList> entitylist = pl-> FindNearbyEntities (sector, box);
        //If there is any nearby entity, we will check which entities are,
        size_t listcount=entitylist->GetCount();
        if (listcount!=0)
        {
            iCelEntity* entity;
            const char* name;
            size_t i;
            uint ID;
            for (i=0;i<listcount;i++)
            {// Check the entities one by one untill one enemy or shoot is found
                entity=entitylist-> Get(i);
                name=entity->GetName();
                ID= entity->GetID();
                //printf("\nNAME: %s , ID: %d\n", name, ID);
                if ((!strcmp (name, "bad2")) || (!strcmp (name, "shoot")))
                {//One enemy or shoot has been hit, so the player will loose
                    //one live
                    printf("\nNAME: %s , ID: %d\n", name, ID);

                    //get the iPcTooltip and iPcProperties to set the new live.
                    csRef<iPcTooltip> pctooltip = CEL_QUERY_PROPCLASS_ENT (
                                                      player_entity, iPcTooltip);
                    csRef<iPcProperties> pcproperties = CEL_QUERY_PROPCLASS_ENT (
                                                            player_entity, iPcProperties);
                    //Get the lives, reduce it, store it and print it
                    long int lives = pcproperties-> GetPropertyLong (pcproperties-> GetPropertyIndex ("lives"));
                    lives--;///
                    printf("REDUCING LIVES");
                    pcproperties -> SetProperty ("lives", lives);
                    char livesc[1];
                    sprintf(livesc, "LIVES %d", lives);
                    pctooltip->SetBackgroundColor (-1, -1, -1);
                    pctooltip->SetText(livesc);
                    pctooltip->Show(50,25);

                    //Get the actual live to check if it is below 0.
                    lives = pcproperties-> GetPropertyLong (pcproperties-> GetPropertyIndex ("lives"));
                    if (lives<0)
                    {// it is below 0; it will be checked in "process frame" in app.cpp
                        pctooltip->SetText("YOU ARE DEAD");
                        pctooltip->Show(100,100);
                        return true;
                    }

                    // When hit, a explosion around the player will be created
                    //cleaning the enemies and shoots around.
                    // We get again the players position.
                    iSector* sector = pcmesh->GetMesh ()->GetMovable ()->GetSectors ()->Get (0);
                    csVector3 pos = pcmesh->GetMesh ()->GetMovable ()->GetTransform ()
                                    .This2Other (csVector3 (0, 0, 0));
                    //We get the list of the entities in the explosion radius.
                    csRef<iCelEntityList> entitylist = pl->FindNearbyEntities (sector, pos, 10);
                    size_t listcount=entitylist->GetCount();

                    if (listcount>1)
                    {
                        const char* name;
                        size_t i;
                        uint ID;
                        for (i=0;i<listcount;i++)
                        {//we will get and hide and move away (kill) the enemies in the explosion radius

                            entity=entitylist-> Get(i);
                            name=entity->GetName();
                            ID= entity->GetID();
                            if ((!strcmp (name, "bad2")) || (!strcmp (name, "shoot")) || (!strcmp (name, "acid")))
                            {
                                csRef<iPcMesh> mesh = CEL_QUERY_PROPCLASS_ENT (
                                                          entity, iPcMesh);
                                // move them away from the map
                                pos = mesh->GetMesh ()->GetMovable ()->GetTransform ()
                                      .This2Other (csVector3 (0, -100, 0)); //to send it under the map
                                mesh-> MoveMesh (sector, pos);
                                // Hide the entity not to do anything
                                mesh -> Hide();
                            }
                        }

                        //After hiding the entities around, we will create the explosion
                        csRef<iCelEntity> bigexplosion_entity;
                        bigexplosion_entity = pl->CreateEntity ("bigexplosion",  bl, "shoot_behave",
                                                                "pcmesh",
                                                                "pclinearmovement",
                                                                "pctimer",//TO END THE EXPLOSION
                                                                0);
                        if (!bigexplosion_entity)
                            printf("Error creating acid entity!");

                        // Get the iPcMesh interface
                        csRef<iPcMesh> pcmeshexpl = CEL_QUERY_PROPCLASS_ENT (
                                                        bigexplosion_entity, iPcMesh);
                        pcmeshexpl->SetPath ("/this/model");
                        pcmeshexpl->SetMesh ("partBigExplosion", "bigexplosion");
                        if (!pcmeshexpl->GetMesh ())
                            printf("Error loading model!");
                        //get the player position and set the explosion around
                        pos = pcmesh->GetMesh ()->GetMovable ()->GetTransform ()
                              .This2Other (csVector3 (0, 0, 0));
                        csRef<iPcLinearMovement> pclinmovexpl = CEL_QUERY_PROPCLASS_ENT (
                                                                    bigexplosion_entity, iPcLinearMovement);
                        pclinmovexpl -> SetFullPosition (pos, 0, sector);

                        //set the timer to end the explosion.
                        csRef<iPcTimer> pctimerexpl = CEL_QUERY_PROPCLASS_ENT (
                                                          bigexplosion_entity, iPcTimer);
                        pctimerexpl-> WakeUp(5000, true);
                        //END CREATE EXPLOSION

                    }
                    break;//no need to check more entities, so we break the search*/
                }//if
            }//for
        }//if
    }//if wakeupframe


    //Check the key input:
    if (msg_id == id_pccommandinput_forward1)
        pcactormove->Forward (true);
    else if (msg_id == id_pccommandinput_forward0)
        pcactormove->Forward (false);
    else if (msg_id == id_pccommandinput_backward1)
        pcactormove->Backward (true);
    else if (msg_id == id_pccommandinput_backward0)
        pcactormove->Backward (false);
    else if (msg_id == id_pccommandinput_rotateleft1)
        pcactormove->RotateLeft (true);
    else if (msg_id == id_pccommandinput_rotateleft0)
        pcactormove->RotateLeft (false);
    else if (msg_id == id_pccommandinput_rotateright1)
        pcactormove->RotateRight (true);
    else if (msg_id == id_pccommandinput_rotateright0)
        pcactormove->RotateRight (false);
    else if (msg_id == id_pccommandinput_cammode1)
        pcactormove->ToggleCameraMode ();
    else if (msg_id == id_pccommandinput_shot1)
    {
        //set animation stuff
        csRef<iMeshFactoryWrapper> imeshfact;
        csRef<iMeshWrapper> imesh;
        imesh = pcmesh -> GetMesh ();
        imeshfact = imesh-> GetFactory ();
        csRef<iSpriteCal3DFactoryState> factstate(
            scfQueryInterface<iSpriteCal3DFactoryState> (
                imeshfact->GetMeshObjectFactory()));
        csRef<iSpriteCal3DState> cal3dstate(scfQueryInterface<iSpriteCal3DState> (
                                                imesh->GetMeshObject()));
        //Start "shoot" animation
        cal3dstate->SetAnimAction("shoot", 0.05f, 0.1f);
        Weapon();

    }
    else if (msg_id == id_pccommandinput_jump1)
    {
        if (jumping==false)
        {
            pcactormove-> Jump();
            //set animation stuff
            csRef<iMeshFactoryWrapper> imeshfact;
            csRef<iMeshWrapper> imesh;
            imesh = pcmesh -> GetMesh ();
            imeshfact = imesh-> GetFactory ();
            csRef<iSpriteCal3DFactoryState> factstate(
                scfQueryInterface<iSpriteCal3DFactoryState> (
                    imeshfact->GetMeshObjectFactory()));
            csRef<iSpriteCal3DState> cal3dstate(scfQueryInterface<iSpriteCal3DState> (
                                                    imesh->GetMeshObject()));
            //Load "jump" animation
            cal3dstate->SetAnimAction("jump", 0.0f, 0.0f);

            csRef<iPcTimer> pctimer = CEL_QUERY_PROPCLASS_ENT (
                                          player_entity, iPcTimer);
            pctimer-> WakeUp(1000, false);
            jumping=true;
        }

    }
    else  if (msg_id == id_pctimer_wakeup)
    {//to restore animations after jump animation is finished
        jumping=false;

        csRef<iPcActorMove> player_pcactormove = CEL_QUERY_PROPCLASS_ENT (
                    player_entity, iPcActorMove);
        if (player_pcactormove-> IsMovingForward () )
            player_pcactormove -> Forward (true);
        else if (player_pcactormove-> IsMovingBackward ())
            player_pcactormove -> Backward (true);
        if (player_pcactormove-> IsRotatingLeft ())
            player_pcactormove -> RotateLeft (true);
        else if (player_pcactormove-> IsRotatingRight ())
            player_pcactormove -> RotateRight (true);
    }
    else
        return BehaviourCommon::SendMessage (msg_id, pc, ret, params, arg);

    return true;
}

