/*
 * GDevelop C++ Platform
 * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
 * This project is released under the MIT License.
 */
#include "RuntimeSceneCameraTools.h"
#include "GDCpp/Runtime/RuntimeScene.h"
#include "GDCpp/Runtime/RuntimeObject.h"
#include "GDCpp/Runtime/RuntimeLayer.h"

float GD_API GetCameraX(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    const sf::View & view = scene.GetRuntimeLayer(layer).GetCamera(camera).GetSFMLView();
    return view.getCenter().x;
}

float GD_API GetCameraY(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;
    const sf::View & view = scene.GetRuntimeLayer(layer).GetCamera(camera).GetSFMLView();
    return view.getCenter().y;
}

void GD_API SetCameraX(RuntimeScene & scene, float x, const gd::String & layer, std::size_t cameraId)
{
    if(cameraId >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    RuntimeCamera & camera = scene.GetRuntimeLayer(layer).GetCamera(cameraId);
    camera.SetViewCenter(sf::Vector2f(x, camera.GetViewCenter().y));
}

void GD_API SetCameraY(RuntimeScene & scene, float y, const gd::String & layer, std::size_t cameraId)
{
    if(cameraId >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    RuntimeCamera & camera = scene.GetRuntimeLayer(layer).GetCamera(cameraId);
    camera.SetViewCenter(sf::Vector2f(camera.GetViewCenter().x, y));
}

double GD_API GetCameraAngle(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    return scene.GetRuntimeLayer(layer).GetCamera(camera).GetRotation();
}

void GD_API SetCameraAngle(RuntimeScene & scene, float newValue, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    return scene.GetRuntimeLayer(layer).GetCamera(camera).SetRotation(newValue);
}

void GD_API SetCameraZoom(RuntimeScene & scene, float newZoom, const gd::String & layer, std::size_t cameraNb)
{
    if(cameraNb >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    scene.GetRuntimeLayer(layer).GetCamera(cameraNb).SetZoom(newZoom);
}

double GD_API GetCameraWidth(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    return scene.GetRuntimeLayer(layer).GetCamera(camera).GetWidth();
}

double GD_API GetCameraHeight(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    return scene.GetRuntimeLayer(layer).GetCamera(camera).GetHeight();
}

double GD_API GetCameraViewportLeft(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    return scene.GetRuntimeLayer(layer).GetCamera(camera).GetSFMLView().getViewport().left;
}

double GD_API GetCameraViewportTop(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    return scene.GetRuntimeLayer(layer).GetCamera(camera).GetSFMLView().getViewport().top;
}

double GD_API GetCameraViewportRight(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    const sf::FloatRect & sfmlViewport = scene.GetRuntimeLayer(layer).GetCamera(camera).GetSFMLView().getViewport();

    return sfmlViewport.left+sfmlViewport.width;
}

double GD_API GetCameraViewportBottom(RuntimeScene & scene, const gd::String & layer, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return 0.f;

    const sf::FloatRect & sfmlViewport = scene.GetRuntimeLayer(layer).GetCamera(camera).GetSFMLView().getViewport();

    return sfmlViewport.top+sfmlViewport.height;
}

/**
 * Change the size of a camera and reset the zoom factor.
 */
void GD_API SetCameraSize( RuntimeScene & scene, const gd::String & layer, std::size_t cameraNb, float width, float height)
{
    if(cameraNb >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    scene.GetRuntimeLayer(layer).GetCamera(cameraNb).SetZoom(1);
    scene.GetRuntimeLayer(layer).GetCamera(cameraNb).SetSize(width, height);
}

void GD_API CenterCameraOnObjectWithLimits(RuntimeScene & scene, RuntimeObject * object, float left, float top, float right, float bottom, bool anticipateObjectMove, const gd::String & layer, std::size_t camera)
{
    if ( object == NULL ) return;

    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    float xOffset = 0;
    float yOffset = 0;
    double elapsedTime = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0;
    if (anticipateObjectMove)
    {
        xOffset = object->TotalForceX() * elapsedTime;
        yOffset = object->TotalForceY() * elapsedTime;
    }

    RuntimeCamera & cam = scene.GetRuntimeLayer(layer).GetCamera(camera);

    double newX = std::min(std::max(object->GetDrawableX() + object->GetCenterX() + xOffset, left+cam.GetWidth()/2), right-cam.GetWidth()/2);
    double newY = std::min(std::max(object->GetDrawableY() + object->GetCenterY() + yOffset, top+cam.GetHeight()/2), bottom-cam.GetHeight()/2);

    cam.SetViewCenter(sf::Vector2f(newX, newY));

    return;
}

void GD_API CenterCameraOnObject(RuntimeScene & scene, RuntimeObject * object,  bool anticipateObjectMove, const gd::String & layer, std::size_t camera)
{
    if ( object == NULL ) return;

    if(camera >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    float xOffset = 0;
    float yOffset = 0;
    double elapsedTime = static_cast<double>(scene.GetTimeManager().GetElapsedTime()) / 1000000.0;
    if (anticipateObjectMove)
    {
        xOffset = object->TotalForceX() * elapsedTime;
        yOffset = object->TotalForceY() * elapsedTime;
    }

    scene.GetRuntimeLayer(layer).GetCamera(camera).SetViewCenter(sf::Vector2f(object->GetDrawableX() + object->GetCenterX() + xOffset,
                                                                              object->GetDrawableY() + object->GetCenterY() + yOffset));

    return;
}

/**
 * Delete a camera of a layer
 */
void GD_API ActDeleteCamera(RuntimeScene & scene, const gd::String & layerName, std::size_t camera)
{
    if(camera >= scene.GetRuntimeLayer(layerName).GetCameraCount())
        return;

    scene.GetRuntimeLayer(layerName).DeleteCamera(camera);
}

/**
 * Add a camera to a layer
 */
void GD_API AddCamera( RuntimeScene & scene, const gd::String & layerName, float width, float height, float viewportLeft, float viewportTop, float viewportRight, float viewportBottom )
{
    //Create the new view
    const sf::RenderWindow * window = scene.renderWindow;
    sf::View view = window ? window->getDefaultView() : sf::View();

    //Setup the viewport and the view
    if ( viewportBottom != 0 && viewportLeft != 0 && viewportRight != 0 && viewportTop != 0) {
        sf::FloatRect newViewport(viewportLeft, viewportTop, viewportRight - viewportLeft, viewportBottom - viewportTop);
        view.setViewport(newViewport);
    }
    if ( width != 0 || height != 0) {
        view.setSize(sf::Vector2f(width, height));
    }

    //Add the runtime camera to the layer
    scene.GetRuntimeLayer(layerName).AddCamera(RuntimeCamera(view));
    return;
}

void GD_API SetCameraViewport( RuntimeScene & scene,  const gd::String & layer, std::size_t cameraNb, float viewportLeft, float viewportTop, float viewportRight, float viewportBottom )
{
    if(cameraNb >= scene.GetRuntimeLayer(layer).GetCameraCount())
        return;

    RuntimeCamera & camera = scene.GetRuntimeLayer(layer).GetCamera(cameraNb);
    camera.SetViewport(viewportLeft, viewportTop, viewportRight, viewportBottom);
}
