#include "scene/scenetree.h"
#include "resourcemanager.h"
#include <SparrowRenderer/pipeline.h>
#include <SparrowRenderer/deferredpipeline.h>
#include <algorithm>
#include <SFML/Window.hpp>
// Scene
#include <SparrowRenderer/mesh.h>
#include <iostream>
#include "scene/scenenode.h"
#include "scene/cameranode.h"

SceneTree::SceneTree(const Engine &engine) :
    Scene(),
    m_engine(engine),
    m_shaderRefreshRequired(false),
    m_camera(nullptr)
{
    DeferredPipeline *pipeline = new DeferredPipeline();
    m_pipeline = pipeline;
    pipeline->setRenderTarget(FrameBuffer::screen);
    m_root.setSceneTree(this);
}

SceneTree::~SceneTree()
{

}

void SceneTree::setMainCamera(CameraNode *camNode)
{
    m_camera = camNode->getCamera();
    ((DeferredPipeline*)m_pipeline)->setCamera(m_camera);
    sf::Vector2u size = getEngine().getWindow()->getSize();
    m_camera->resize(size.x, size.y);
}

SceneIterator<Light*>* SceneTree::getLights()
{
    return new ArrayIterator<Light*>(m_lights);
}

SceneIterator<GeometryNode*>* SceneTree::getGeometry()
{
    return new ArrayIterator<GeometryNode*>(m_geometries);
}

void SceneTree::getMeshTypes(std::vector<unsigned int> &meshTypes)
{
    for(unsigned int type : m_meshTypes)
        meshTypes.push_back(type);
}

void SceneTree::getLightTypes(std::vector<unsigned int> &lightTypes)
{
    for(unsigned int type : m_lightTypes)
        lightTypes.push_back(type);
}

void SceneTree::update()
{
    m_root.update();
}

void SceneTree::registerMeshType(unsigned int meshType)
{
    auto ret = m_meshTypes.emplace(meshType);
    if(ret.second)
        m_shaderRefreshRequired = true;
}

void SceneTree::registerLightType(unsigned int lightType)
{
    auto ret = m_lightTypes.emplace(lightType);
    if(ret.second)
        m_shaderRefreshRequired = true;
}

void SceneTree::addToIndex(SceneNode* node){
    Light *light = node->getLight();
    GeometryNode *geometrynode = node->getGeometryNode();
    //TODO : Check for doublon in m_lights et m_geometries => not necessary if correctly removed ?
    if (light != nullptr) m_lights.push_back(light);
    if (geometrynode != nullptr) m_geometries.push_back(geometrynode);
}

void SceneTree::removeFromIndex(SceneNode *node){
    Light *light = node->getLight();
    GeometryNode *geometrynode = node->getGeometryNode();

    if(light != nullptr)
    {
        auto it_l = std::find(m_lights.begin(),m_lights.end(),node->getLight());
        if (it_l != m_lights.end()){
            std::iter_swap(it_l,m_lights.end()-1);
            m_lights.pop_back();
        }
    }
    if(geometrynode != nullptr)
    {
        auto it_g = std::find(m_geometries.begin(),m_geometries.end(),node->getGeometryNode());
        if (it_g != m_geometries.end()){
            std::iter_swap(it_g,m_geometries.end()-1);
            m_geometries.pop_back();
        }
    }
}

void SceneTree::updateShaders()
{
    if(m_shaderRefreshRequired)
    {
        ((DeferredPipeline*) m_pipeline)->refreshScene(this);
        m_shaderRefreshRequired = false;
    }
}
