/*Copyright (C) 2010-2020 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu 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 3 of the License, or
(at your option) any later version.

Hikou no mizu 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 Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Arena.hpp"

Arena::Arena() : m_width(0), m_height(0), m_ground(0), m_gravity(0), m_airFriction(0)
{
    
}

Arena::Arena(const Sprite &background, float width, float height, float ground, float gravity, float airFriction) :
m_background(background), m_width(width), m_height(height), m_ground(ground), m_gravity(gravity), m_airFriction(airFriction)
{
    resizeBackground();
}

Arena::~Arena()
{
    removePlatforms();
}

void Arena::draw()
{
    m_background.draw();
    
    std::vector< std::pair<Platform*, Box> >::iterator it;
    
    for (it = m_platformsList.begin(); it != m_platformsList.end(); it++)
        it->first->draw();
}

void Arena::addPlatform(const Box &spawnBox, const Box &bodyBox, const Sprite &sprite, bool hitable, bool fixed)
{
    Platform *platform = new Platform();
    platform->setBodyBox(bodyBox);
    platform->setSprite(sprite);
    platform->setGraphicsSize(spawnBox.width, spawnBox.height);
    platform->setHitable(hitable);
    platform->setFixed(fixed);
    
    m_platformsList.push_back( std::make_pair(platform, spawnBox) );
}

void Arena::removePlatform(int platformIx)
{
    if (platformIx >= 0 && platformIx <= static_cast<int>(m_platformsList.size()) - 1)
    {
        delete m_platformsList[platformIx].first;
        m_platformsList.erase(m_platformsList.begin() + platformIx);
    }
}

void Arena::removePlatforms()
{
    std::vector< std::pair<Platform*, Box> >::iterator it;
    
    for (it = m_platformsList.begin(); it != m_platformsList.end(); it++)
        delete it->first;
    
    m_platformsList.clear();
}

void Arena::applyToPhysics(PhysicsWorld &physicsWorld)
{
    //Configure a physics world to match with the arena parameters
    physicsWorld.setWorldBox( Box(0, 0, m_width, m_ground) );
    physicsWorld.setGravityForce(m_gravity);
    physicsWorld.setAirFriction(m_airFriction);
}

void Arena::subscribeObjects(PhysicsWorld &physicsWorld)
{
    //Subscribe arena's platforms to a physics world
    std::vector< std::pair<Platform*, Box> >::iterator it;
    
    for (it = m_platformsList.begin(); it != m_platformsList.end(); it++)
    {
        Platform *platform = it->first;
        const Box &spawnBox = it->second;
        const Box &bodyBox = it->first->getBodyBox();
        
        platform->setPhysicsWorld(physicsWorld);
        platform->setWidth(bodyBox.width);
        platform->setHeight(bodyBox.height);
        
        platform->setPosition(spawnBox.left, spawnBox.top - platform->getBox().height);
        
        
        platform->setFixed(true);
        
        physicsWorld.addObject((*platform));
    }
}

void Arena::loadFromXML(const std::string &name, const std::string &xmlRelPath, TextureManager &textureManager)
{
    //Delete previously loaded data.
    removePlatforms();

    //TinyXML initialization
    std::string xmlPath = BuildValues::data(xmlRelPath); //Locate absolute path to xml data
    
    TiXmlDocument xmlFile;
    xmlFile.LoadFile(xmlPath.c_str());
    
    TiXmlHandle hdl(&xmlFile);
    TiXmlElement *xmlElement = hdl.FirstChildElement().FirstChildElement().ToElement();
    
    int nodeIx = 0;
    while (xmlElement)
    {
        std::string itemName = xmlElement->Attribute("pathname");
        if (itemName == name)
        {
            //Get main informations
            Texture *texture = textureManager.getTexture("gfx/arenas/" + itemName + ".png");
            
            float width = 1024, height = 576, ground = 500, gravity = 1200, airFriction = 1200;
            
            xmlElement->QueryFloatAttribute("width", &width);
            xmlElement->QueryFloatAttribute("height", &height);
            xmlElement->QueryFloatAttribute("ground", &ground);
            
            xmlElement->QueryFloatAttribute("gravity", &gravity);
            xmlElement->QueryFloatAttribute("airFriction", &airFriction);
            
                //Update arena
            setWidth(width);
            setHeight(height);
            setGround(ground);
            
            setGravity(gravity);
            setAirFriction(airFriction);
            
            //Get detailled informations
            TiXmlHandle arenaHdl = hdl.FirstChildElement().Child(nodeIx);
            
                //Background
            TiXmlElement *backgroundElement = arenaHdl.FirstChildElement("background").ToElement();
            
            if (backgroundElement)
            {
                //Get informations
                int srcX = 0, srcY = 0, srcWidth = 0, srcHeight = 0;
                
                backgroundElement->QueryIntAttribute("srcX", &srcX);
                backgroundElement->QueryIntAttribute("srcY", &srcY);
                backgroundElement->QueryIntAttribute("srcWidth", &srcWidth);
                backgroundElement->QueryIntAttribute("srcHeight", &srcHeight);
                
                //Create and add background to the arena
                Box subRect;
                
                subRect.left = (texture->getWidth() > 0) ? static_cast<float>(srcX) / texture->getWidth() : 0;
                subRect.top = (texture->getHeight() > 0) ? static_cast<float>(srcY) / texture->getHeight() : 0;
                
                subRect.width = (texture->getWidth() > 0) ? static_cast<float>(srcWidth) / texture->getWidth() : 1;
                subRect.height = (texture->getHeight() > 0) ? static_cast<float>(srcHeight) / texture->getHeight() : 1;
                
                Sprite background;
                background.setTexture(*texture);
                background.setSubRect(subRect);
                
                setBackground(background);
            }
            
                //Platforms
            TiXmlElement *platformsElement = arenaHdl.FirstChildElement("platforms").FirstChildElement().ToElement();
            
            while (platformsElement)
            {
                //Get informations
                    //Coords
                float x = 0, y = 0, width = 0, height = 0;
                
                platformsElement->QueryFloatAttribute("width", &width);
                platformsElement->QueryFloatAttribute("height", &height);
                platformsElement->QueryFloatAttribute("x", &x);
                platformsElement->QueryFloatAttribute("y", &y);
                
                    //Sprite
                int srcX = 0, srcY = 0, srcWidth = 0, srcHeight = 0;
                float bodyX = 0, bodyY = 0, bodyWidth = 0, bodyHeight = 0;
                
                platformsElement->QueryIntAttribute("srcX", &srcX);
                platformsElement->QueryIntAttribute("srcY", &srcY);
                
                platformsElement->QueryIntAttribute("srcWidth", &srcWidth);
                platformsElement->QueryIntAttribute("srcHeight", &srcHeight);
                
                platformsElement->QueryFloatAttribute("bodyX", &bodyX);
                platformsElement->QueryFloatAttribute("bodyY", &bodyY);
                
                platformsElement->QueryFloatAttribute("bodyWidth", &bodyWidth);
                platformsElement->QueryFloatAttribute("bodyHeight", &bodyHeight);
                
                    //Hit-able ?
                int hitable = 0;
                platformsElement->QueryIntAttribute("hitable", &hitable);
                
                    //Fixed ?
                int fixed = 0;
                platformsElement->QueryIntAttribute("fixed", &fixed);
                
                Box subRect;
                
                subRect.left = (texture->getWidth() > 0) ? static_cast<float>(srcX) / texture->getWidth() : 0;
                subRect.top = (texture->getHeight() > 0) ? static_cast<float>(srcY) / texture->getHeight() : 0;
                
                subRect.width = (texture->getWidth() > 0) ? static_cast<float>(srcWidth) / texture->getWidth() : 1;
                subRect.height = (texture->getHeight() > 0) ? static_cast<float>(srcHeight) / texture->getHeight() : 1;
                
                Sprite sprite;
                sprite.setTexture(*texture);
                sprite.setSubRect(subRect);
                
                //Create and add platform to the arena
                addPlatform( Box(x, y, width, height), Box(bodyX, bodyY, bodyWidth, bodyHeight), sprite, (hitable > 0), (fixed > 0) );
                
                platformsElement = platformsElement->NextSiblingElement();
            }
            
                //Spawns
            TiXmlElement *spawnsElement = arenaHdl.FirstChildElement("spawns").FirstChildElement().ToElement();
            
            while (spawnsElement)
            {
                //Get informations
                int playerIx = 0;
                float x = 0, y = 0;
                
                spawnsElement->QueryIntAttribute("playerIx", &playerIx);
                spawnsElement->QueryFloatAttribute("x", &x);
                spawnsElement->QueryFloatAttribute("y", &y);
                
                //Add them to arena
                setPlayerSpawn(static_cast<unsigned int>(playerIx), Vector(x, y));
                
                spawnsElement = spawnsElement->NextSiblingElement();
            }
        }
        
        nodeIx++;
        xmlElement = xmlElement->NextSiblingElement();
    }
}

//Get and set methods
    //Graphics
void Arena::setBackground(const Sprite &background)
{
    m_background = background;
    resizeBackground();
}

const Sprite &Arena::getBackground() const
{
    return m_background;
}

    //Physics
void Arena::setWidth(float width)
{
    m_width = width;
    resizeBackground();
}

float Arena::getWidth() const
{
    return m_width;
}

void Arena::setHeight(float height)
{
    m_height = height;
    resizeBackground();
}

float Arena::getHeight() const
{
    return m_height;
}

void Arena::setGround(float ground)
{
    m_ground = ground;
}

float Arena::getGround() const
{
    return m_ground;
}

void Arena::setGravity(float gravity)
{
    m_gravity = gravity;
}

float Arena::getGravity() const
{
    return m_gravity;
}

void Arena::setAirFriction(float airFriction)
{
    m_airFriction = airFriction;
}

float Arena::getAirFriction() const
{
    return m_airFriction;
}

    //Others
void Arena::setPlayerSpawn(unsigned int ix, const Vector &playerSpawn)
{
    m_playerSpawns[ix] = playerSpawn;
}

bool Arena::getPlayerSpawn(unsigned int ix, Vector &target) const
{
    std::map<unsigned int, Vector>::const_iterator it = m_playerSpawns.find(ix);
    if (it != m_playerSpawns.end())
    {
        target = it->second;
        return true;
    }
    
    return false;
}

void Arena::resizeBackground()
{
    //Resize background to arena size
    if (m_background.getWidth() > 0 && m_background.getHeight() > 0)
    {
        m_background.setXScale(m_width / m_background.getWidth());
        m_background.setYScale(m_height / m_background.getHeight());
    }
}
