#include "Scene.hpp"
#include "BehaviourScripts/Camera.hpp"
#include <algorithm>
#include "Debug.hpp"
#include "RenderPipeline.hpp"

void TSE::Scene::Render(IRenderer &rnd, const IWindow &wnd)
{
    RenderPipeline* pipeline = IRenderer::pipeline;
    auto camerasBackup = std::vector<Camera*>(IRenderer::camerasToRenderWith);
    
    if(pipeline != nullptr)
    {
        for(int i = 0; i < pipeline->GetRenderStepCount(); i++)
        {
            IRenderer::camerasToRenderWith = std::vector<Camera*>(camerasBackup);
            const RenderStep& step = pipeline->GetRenderStepAt(i);
            if(!step.EditorCamera)
            {
                
                for(int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
                {
                    if(IRenderer::camerasToRenderWith[i]->baseObject->name == ".EditorCamera")
                    IRenderer::camerasToRenderWith.erase(IRenderer::camerasToRenderWith.begin() + i);
                }
            }
            for(int i = 0; i < IRenderer::camerasToRenderWith.size(); i++)
            {
                if(IRenderer::camerasToRenderWith[i]->baseObject->name != ".EditorCamera")
                IRenderer::camerasToRenderWith[i]->SetRenderTarget(step.target);
            }
            
            int counter = 1;

            for(auto l : step.layersToRender)
            {
                if(!l->IsVisual()) continue;
                l->Render(rnd);
                if(counter++ != layers.size())
                {
                    rnd.End(); //OPTIMIZE:
                    //takes up 13,97% of function, but only needed, if there is more then one layer
                    //possible optimizations:
                    // -remove layers
                    // -make layer calculations, in shader
                    // -make an offset, that is calculated on cpu, and then commit everything at once

                    // now it is better because it is only done once per frame if only one shader is used, or textures are full, or more then one layers are used
                    rnd.Flush();
                    rnd.Begin();
                    wnd.ClearDepthBuffer();
                }
            }
        }
    }
    else
    {
        int counter = 1;
        for(auto l : layers)
        {
            IRenderer::camerasToRenderWith.clear();
            if(!l.second->IsVisual()) continue;
            for(auto camera : camerasBackup)
            {
                auto it = std::find(camera->layersNotToRender.begin(), camera->layersNotToRender.end(), l.second->GetID());
                if(it == camera->layersNotToRender.end())
                {
                    IRenderer::camerasToRenderWith.push_back(camera);
                }
            }

            l.second->Render(rnd);
            if(counter++ != layers.size())
            {
                rnd.End(); //OPTIMIZE:
                //takes up 13,97% of function, but only needed, if there is more then one layer
                //possible optimizations:
                // -remove layers
                // -make layer calculations, in shader
                // -make an offset, that is calculated on cpu, and then commit everything at once

                // now it is better because it is only done once per frame if only one shader is used, or textures are full, or more then one layers are used
                rnd.Flush();
                rnd.Begin();
                wnd.ClearDepthBuffer();
            }
        }
    }
}

void TSE::Scene::DoneRender()
{
    IRenderer::camerasToRenderWith.clear();
}

void TSE::Scene::AddLayer(Layer *l)
{
    layers.push_back(std::pair(l->GetName(), l));
}

void TSE::Scene::RemoveLayer(const string &name)
{
    auto it = layers.begin();
    for (int j = 0; j < layers.size(); j++)
    {
        if(it->first == name)
        {
            layers.erase(it);
            break;
        }
        it++;
    }
}

int TSE::Scene::GetLayerCount() const
{
    return layers.size();
}

TSE::Layer *TSE::Scene::GetLayerAt(const int &i) const
{
    auto it = layers.begin();
    for (int j = 0; j < i; j++)
    {
        it++;
        
    }
    
    return (*it).second;
}

void TSE::Scene::SetName(const string &nname)
{
    name = nname;
}

TSE::string TSE::Scene::GetName()
{
    return name;
}

void TSE::Scene::Update()
{
    for(auto l : layers)
    {
        l.second->Update();
    }
}

void TSE::Scene::RenameLayer(const string &oldName, const string &newName)
{
    for (int j = 0; j < layers.size(); j++)
    {
        if(layers[j].first == oldName)
        {
            layers[j].first = newName;
            layers[j].second->SetName(newName);
            break;
        }
    }
}

void TSE::Scene::MoveLayerUp(Layer *l)
{
    auto it = layers.begin();
    for (int j = 0; j < layers.size(); j++)
    {
        if(it->first == l->GetName())
        {
            std::swap(layers[j-1], layers[j]);
            break;
        }
        it++;
    }
}

void TSE::Scene::MoveLayerDown(Layer *l)
{
    auto it = layers.begin();
    for (int j = 0; j < layers.size(); j++)
    {
        if(it->first == l->GetName())
        {
            std::swap(layers[j+1], layers[j]);
            break;
        }
        it++;
    }
}
