/*Copyright (C) 2010-2012 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()
{
    
}

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();
}

void Arena::draw()
{
    m_background.draw();
    
    for (unsigned int i = 0; i < m_platformsList.size(); i++)
        m_platformsList[i].draw();
}

void Arena::addPlatform(const Platform &platform, int platformIx)
{
    if (platformIx == -1)
        m_platformsList.push_back(platform);
    
    else if (platformIx >= 0 && platformIx <= static_cast<int>(m_platformsList.size()) - 1)
        m_platformsList[platformIx] = platform;
}

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

void Arena::applyToPhysics(PhysicsEngine &physicsEngine)
{
    //Configure a physics engine's parameters to match with the arena specific ones
    physicsEngine.setPhysicsWorld( Box(0, 0, m_width, m_ground) );
    physicsEngine.setGravityForce(m_gravity);
    physicsEngine.setAirFriction(m_airFriction);
}

void Arena::subscribeObjects(PhysicsEngine &physicsEngine)
{
    //Subscribe arena's platforms in a physics engine
    for (unsigned int i = 0; i < m_platformsList.size(); i++)
        physicsEngine.addObject(&m_platformsList[i]);
}

//Static methods
Arena Arena::loadFromXML(const std::string &name, const std::string &xmlPath, TextureManager &textureManager)
{
    //TinyXML initialization
    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("name");
        if (itemName == name)
        {
            //Arena initialization
            Arena arena;
            
            //Get main informations
            Texture *texture = textureManager.getTexture("data/gfx/arenas/" + itemName + ".png");
            
            float width = 0, height = 0, ground = 0, gravity = 0, airFriction = 0;
            
            xmlElement->QueryFloatAttribute("width", &width);
            xmlElement->QueryFloatAttribute("height", &height);
            xmlElement->QueryFloatAttribute("ground", &ground);
            
            xmlElement->QueryFloatAttribute("gravity", &gravity);
            xmlElement->QueryFloatAttribute("airFriction", &airFriction);
            
                //Update arena
            arena.setWidth(width);
            arena.setHeight(height);
            arena.setGround(ground);
            
            arena.setGravity(gravity);
            arena.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);
                
                arena.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;
                
                platformsElement->QueryIntAttribute("srcX", &srcX);
                platformsElement->QueryIntAttribute("srcY", &srcY);
                platformsElement->QueryIntAttribute("srcWidth", &srcWidth);
                platformsElement->QueryIntAttribute("srcHeight", &srcHeight);
                
                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
                Platform platform(width, height, sprite);
                platform.setXPosition(x);
                platform.setYPosition(y);
                
                arena.addPlatform(platform);
                
                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
                arena.setPlayerSpawn(static_cast<unsigned int>(playerIx), Vector(x, y));
                
                spawnsElement = spawnsElement->NextSiblingElement();
            }
            
            return arena;
        }
        
        nodeIx++;
        xmlElement = xmlElement->NextSiblingElement();
    }
    
    return Arena();
}

//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());
    }
}
