/*
 * GDevelop Core
 * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
 * This project is released under the MIT License.
 */
#include "GDCore/Project/Layer.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/CommonTools.h"

namespace gd
{

Camera Layer::badCamera;
Effect Layer::badEffect;

Layer::Layer() :
isVisible(true)
{
}

/**
 * Change cameras count, automatically adding/removing them.
 */
void Layer::SetCameraCount(std::size_t n)
{
    while ( cameras.size() < n)
        cameras.push_back(Camera());

    while ( cameras.size() > n)
        cameras.erase(cameras.begin()+cameras.size()-1);
}

#if defined(GD_IDE_ONLY)
void Layer::SerializeTo(SerializerElement & element) const
{
    element.SetAttribute("name", GetName());
    element.SetAttribute("visibility", GetVisibility());

    SerializerElement & camerasElement = element.AddChild("cameras");
    camerasElement.ConsiderAsArrayOf("camera");
    for (std::size_t c = 0;c<GetCameraCount();++c)
    {
        SerializerElement & cameraElement = camerasElement.AddChild("camera");
        cameraElement.SetAttribute("defaultSize", GetCamera(c).UseDefaultSize());
        cameraElement.SetAttribute("width", GetCamera(c).GetWidth());
        cameraElement.SetAttribute("height", GetCamera(c).GetHeight());

        cameraElement.SetAttribute("defaultViewport", GetCamera(c).UseDefaultViewport());
        cameraElement.SetAttribute("viewportLeft", GetCamera(c).GetViewportX1());
        cameraElement.SetAttribute("viewportTop", GetCamera(c).GetViewportY1());
        cameraElement.SetAttribute("viewportRight", GetCamera(c).GetViewportX2());
        cameraElement.SetAttribute("viewportBottom", GetCamera(c).GetViewportY2());
    }

    SerializerElement & effectsElement = element.AddChild("effects");
    effectsElement.ConsiderAsArrayOf("effect");
    for (std::size_t i = 0;i<GetEffectsCount();++i)
    {
        SerializerElement & effectElement = effectsElement.AddChild("effect");
        GetEffect(i).SerializeTo(effectElement);
    }
}
#endif

/**
 * \brief Unserialize the layer.
 */
void Layer::UnserializeFrom(const SerializerElement & element)
{
    SetName(element.GetStringAttribute("name", "", "Name"));
    SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));

    //Compatibility with GD <= 3.3
    if (element.HasChild("Camera"))
    {
        for (std::size_t i = 0; i < element.GetChildrenCount("Camera"); ++i)
        {
            const SerializerElement & cameraElement = element.GetChild("Camera", i);
            SetCameraCount(GetCameraCount()+1);
            Camera & camera = GetCamera(GetCameraCount()-1);

            camera.SetUseDefaultSize(cameraElement.GetBoolAttribute("DefaultSize", true));
            camera.SetSize(cameraElement.GetDoubleAttribute("Width"), cameraElement.GetDoubleAttribute("Height"));

            camera.SetUseDefaultViewport(cameraElement.GetBoolAttribute("DefaultViewport", true));
            camera.SetViewport(cameraElement.GetDoubleAttribute("ViewportLeft"),
                cameraElement.GetDoubleAttribute("ViewportTop"),
                cameraElement.GetDoubleAttribute("ViewportRight"),
                cameraElement.GetDoubleAttribute("ViewportBottom")
                ); // (sf::Rect used Right and Bottom instead of Width and Height before)
        }
    }
    //End of compatibility code
    else
    {
        SerializerElement & camerasElement = element.GetChild("cameras");
        camerasElement.ConsiderAsArrayOf("camera");
        for (std::size_t i = 0; i < camerasElement.GetChildrenCount(); ++i)
        {
            const SerializerElement & cameraElement = camerasElement.GetChild(i);

            SetCameraCount(GetCameraCount()+1);
            Camera & camera = GetCamera(GetCameraCount()-1);

            camera.SetUseDefaultSize(cameraElement.GetBoolAttribute("defaultSize", true));
            camera.SetSize(cameraElement.GetDoubleAttribute("width"), cameraElement.GetDoubleAttribute("height"));

            camera.SetUseDefaultViewport(cameraElement.GetBoolAttribute("defaultViewport", true));
            camera.SetViewport(cameraElement.GetDoubleAttribute("viewportLeft"),
                cameraElement.GetDoubleAttribute("viewportTop"),
                cameraElement.GetDoubleAttribute("viewportRight"),
                cameraElement.GetDoubleAttribute("viewportBottom")); // (sf::Rect used Right and Bottom instead of Width and Height before)
        }
    }

    effects.clear();
    SerializerElement & effectsElement = element.GetChild("effects");
    effectsElement.ConsiderAsArrayOf("effect");
    for (std::size_t i = 0; i < effectsElement.GetChildrenCount(); ++i)
    {
        const SerializerElement & effectElement = effectsElement.GetChild(i);

        auto effect = std::shared_ptr<gd::Effect>(new Effect);
        effect->UnserializeFrom(effectElement);
        effects.push_back(effect);
    }
}


gd::Effect & Layer::GetEffect(const gd::String & name)
{
    auto effect = find_if(effects.begin(), effects.end(), [&name](std::shared_ptr<gd::Effect> & effect) {
        return effect->GetName() == name;
    });

    if ( effect != effects.end())
        return **effect;

    return badEffect;
}
const gd::Effect & Layer::GetEffect(const gd::String & name) const
{
    auto effect = find_if(effects.begin(), effects.end(), [&name](const std::shared_ptr<gd::Effect> & effect) {
        return effect->GetName() == name;
    });

    if ( effect != effects.end())
        return **effect;

    return badEffect;
}
gd::Effect & Layer::GetEffect(std::size_t index)
{
    return *effects[index];
}
const gd::Effect & Layer::GetEffect (std::size_t index) const
{
    return *effects[index];
}
std::size_t Layer::GetEffectsCount() const
{
    return effects.size();
}

bool Layer::HasEffectNamed(const gd::String & name) const
{
    return ( find_if(effects.begin(), effects.end(), [&name](const std::shared_ptr<gd::Effect> & effect) {
        return effect->GetName() == name;
    }) != effects.end() );
}
std::size_t Layer::GetEffectPosition(const gd::String & name) const
{
    for (std::size_t i = 0;i<effects.size();++i)
    {
        if ( effects[i]->GetName() == name ) return i;
    }
    return gd::String::npos;
}

gd::Effect & Layer::InsertNewEffect(const gd::String & name, std::size_t position)
{
    auto newEffect = std::shared_ptr<gd::Effect>(new Effect);
    newEffect->SetName(name);
    newEffect->SetEffectName(name);
    if (position<effects.size())
        effects.insert(effects.begin()+position, newEffect);
    else
        effects.push_back(newEffect);

    return *newEffect;
}

void Layer::InsertEffect(const gd::Effect & effect, std::size_t position)
{
    auto newEffect = std::shared_ptr<gd::Effect>(new Effect(effect));
    if (position<effects.size())
        effects.insert(effects.begin()+position, newEffect);
    else
        effects.push_back(newEffect);
}

void Layer::RemoveEffect(const gd::String & name)
{
    auto effect = find_if(effects.begin(), effects.end(), [&name](const std::shared_ptr<gd::Effect> & effect) {
        return effect->GetName() == name;
    });
    if ( effect == effects.end() ) return;

    effects.erase(effect);
}

void Layer::SwapEffects(std::size_t firstEffectIndex, std::size_t secondEffectIndex)
{
    if ( firstEffectIndex >= effects.size() || secondEffectIndex >= effects.size() )
        return;

    auto temp = effects[firstEffectIndex];
    effects[firstEffectIndex] = effects[secondEffectIndex];
    effects[secondEffectIndex] = temp;
}

Camera::Camera() :
    defaultSize(true),
    defaultViewport(true),
    x1(0),
    y1(0),
    x2(1),
    y2(1),
    width(0),
    height(0)
{
}

}
