/******************************************************************************
*	File		-	component_guis.cpp
*	Author		-	Joey Pollack
*	Date		-	2022/06/01 (y/m/d)
*	Mod Date	-	2022/06/01 (y/m/d)
*	Description	-	Functions to render component editing GUIs
******************************************************************************/

#include "component_guis.h"
#include <editor/editor.h>
#include <utils/logger.h>
#include <gui/imgui_ext.h>
#include <dearimgui/imgui.h>
#include <dearimgui/imgui_internal.h>

#include <editor/contents/content_manager.h>
#include <editor/contents/script.h>
#include <editor/contents/image.h>

#include <renderer/texture.h>

#include <vector>

namespace lunarium { namespace editor
{
    /////////////////////////////////////////////////////////////////////
    //				COMMON SECTIONS
    /////////////////////////////////////////////////////////////////////
    static bool DrawTitle(std::string title)
    {
        ImVec2 top_left(ImGui::GetWindowPos().x, ImGui::GetCursorScreenPos().y - ImGui::GetStyle().FramePadding.y);
        ImVec2 bot_right(top_left.x + ImGui::GetWindowSize().x, ImGui::GetCursorScreenPos().y + ImGui::GetFrameHeight());
        //ImU32 color = ImGui::ColorConvertFloat4ToU32(ImVec4(0.1f, 0.3f, 0.8f, 1.0f));
        ImU32 color = ImGui::ColorConvertFloat4ToU32(ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
        ImGui::GetWindowDrawList()->AddRectFilled(top_left, bot_right, color);
        ImGuiExt::TextCentered(title.c_str());
        ImGui::SameLine();
        std::string label = "X##";
        label += title;
        float x = ImGui::GetCursorPosX();
        x += ImGui::GetContentRegionAvail().x;
        ImGui::SetCursorPosX(x - 25);
        return ImGui::Button(label.c_str());
    }

    bool CompGui::RenderTagComp(TagComponent& comp)
    {
        bool remove = false;
        remove = DrawTitle("Tag Component");

        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(3.0f, ImGui::GetStyle().ItemSpacing.y));
        const int BUFFER_SIZE = 256;
        static char buffer[BUFFER_SIZE] = "";
        ImGui::Text("Tag Info");
        ImGui::SameLine();
        strcpy(buffer, comp.Info.c_str());
        ImGui::InputText("##Tag", buffer, BUFFER_SIZE);
        comp.Info = buffer;
        ImGui::PopStyleVar();
        
        return remove;
    }

    /// This code taken from the Hazel engine
    /// https://github.com/TheCherno/Hazel/blob/master/Hazelnut/src/Panels/SceneHierarchyPanel.cpp
    bool CompGui::RenderTransformComp(TransformComponent& comp)
    {        
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Transform Component");
        ImGui::PopStyleVar();
        
        ImGuiExt::Vec2Control("Translation", comp.Position, 0.0f, 85.0f);
        glm::vec3 rotation = glm::degrees(comp.Rotation);
        //ImGuiExt::Vec3Control("Rotation", rotation, 0.0f, 85.0f);
        ImGuiExt::FloatControl("Rotation", rotation.z, 0.0f, 85.0f);
        comp.Rotation = glm::radians(rotation);
        ImGuiExt::Vec2Control("Scale", comp.Scale, 1.0f, 85.0f);

        return remove;
    }

    
    bool CompGui::RenderVelocityComp(VelocityComponent& comp)
    {
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Velocity Component");
        ImGui::PopStyleVar();

        ImGuiExt::Vec2Control("Velocity", comp.Velocity, 0.0f, 85.0f);

        return remove;
    }

    
    bool CompGui::RenderCameraComp(CameraComponent& comp)
    {
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Camera Component");
        ImGui::PopStyleVar();

        glm::vec3 pos = comp.Camera.GetPosition();
        if (ImGuiExt::Vec2Control("Position", pos))
        {
            comp.Camera.SetPosition(pos);
        }

        float rot = comp.Camera.GetRotation();      
        if (ImGuiExt::FloatControl("Rotation", rot, 0.0f, 85.0f))
        {
            comp.Camera.SetRotation(rot);
        }

        return remove;
    }

    
    bool CompGui::RenderBlockOutComp(BlockOutComponent& comp)
    {
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Block Out Component");
        ImGui::PopStyleVar();

        ImGui::Text("Block Color: ");
        ImGui::SameLine();
        ImGui::ColorEdit4("##BlockOut Color", &comp.Color.x, ImGuiColorEditFlags_DisplayRGB);
        
        glm::vec3 _size = {comp.Size.x, comp.Size.y, 0.0f };
        if (ImGuiExt::Vec2Control("Size", _size, 1.0f, 85.0f))
        {
            comp.Size.x = _size.x;
            comp.Size.y = _size.y;
        }

        ImGui::Text("Render Layer: ");
        ImGui::SameLine();
        ImGui::InputInt("##Render Layer", &comp.RenderLayer, 1, 10);

        
        return remove;
    }

    
    bool CompGui::RenderSpriteRendererComp(SpriteRendererComponent& comp)
    {
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Sprite Renderer Component");
        ImGui::PopStyleVar();

        ImGui::Text("Preview");
        Image* pImage = (editor::Image*) ContentManager::GetInstance().GetAsset(comp.ImageID);
        if (pImage)
        {
            lunarium::Texture* pTex = pImage->GetImage();
            float x1 = comp.FramePos.x / pTex->GetWidth();
            float y1 = comp.FramePos.y / pTex->GetHeight();
            float x2 = (comp.FramePos.x + comp.FrameSize.x) /  pTex->GetWidth();
            float y2 = (comp.FramePos.y + comp.FrameSize.y) /  pTex->GetHeight();
            ImGui::Image((ImTextureID)pTex->GetGLID64(), ImVec2(comp.FrameSize.x, comp.FrameSize.y), ImVec2(x1, y1), 
                            ImVec2(x2, y2), 
                            ImVec4(comp.TintColor.R, comp.TintColor.G, comp.TintColor.B, comp.TintColor.A));
        }
        else
        {
            ImGui::Text("NOT SET");
        }
        
        ImGui::Separator();

        // The image asset
        std::string preview = (pImage ? pImage->GetFileLocation().filename().string().c_str() : "NOT SET");
        if (ImGui::BeginCombo("Image", preview.c_str()))
        {
            char search_buf[256] = "";
            ImGui::InputText("Search ##IMAGE", search_buf, 256);
            std::string ssearch = search_buf;

            std::vector<EditorAsset*> assets;
            ContentManager::GetInstance().GetAllAssetsByType(assets, AssetType::EATYPE_IMAGE);

            for (auto iter = assets.begin(); iter != assets.end(); iter++)
            {
                editor::Image* pImg = (editor::Image*)(*iter);

                if (ssearch.size() > 0)
                {
                    // Filter items based on search term
                    if (pImg->GetFileLocation().string().find(ssearch) == std::string::npos)
                    {
                        continue;
                    }
                }

                bool selected = false;
                if (ImGui::Selectable(pImg->GetFileLocation().string().c_str(), &selected))
                {
                    comp.ImageID = pImg->GetID();
                }
            }

            ImGui::EndCombo();
        }

        // Render Layer
        ImGui::Text("Render Layer: ");
        ImGui::SameLine();
        ImGui::InputInt("##Render Layer Image", &comp.RenderLayer, 1, 10);

        // Frame Size
        glm::vec3 _size = {comp.FrameSize.x, comp.FrameSize.y, 0.0f };
        if (ImGuiExt::Vec2Control("Frame Size", _size, 1.0f, 85.0f))
        {
            comp.FrameSize.x = _size.x;
            comp.FrameSize.y = _size.y;
        }

        // Frame Position
        glm::vec3 _size2 = {comp.FramePos.x, comp.FramePos.y, 0.0f };
        if (ImGuiExt::Vec2Control("Frame Position", _size2, 1.0f, 85.0f))
        {
            comp.FramePos.x = _size2.x;
            comp.FramePos.y = _size2.y;
        }

        // Tint Color
        ImGui::Text("Tint Color: ");
        ImGui::SameLine();
        ImGui::ColorEdit4("##Tint Color", comp.TintColor.arr, ImGuiColorEditFlags_DisplayRGB);

        return remove;
    }
 
    bool CompGui::RenderScriptComp(ScriptComponent& comp)
    {
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Script Component");
        ImGui::PopStyleVar();

        ImGui::TextUnformatted("Script: ");
        ImGui::SameLine();

        // TODO: Finish Script GUI
        //       Drag and Drop script asset
        //       Click to Open Script (editor event?)
        //       ComboBox to set script
        lunarium::editor::Script* pScript = (lunarium::editor::Script*)ContentManager::GetInstance().GetAsset(comp.ScriptID);
        std::string preview = (pScript ? pScript->GetScriptFile().filename().string().c_str() : "NOT SET");
        if (ImGui::BeginCombo("Script", preview.c_str()))
        {
            char search_buf[256] = "";
            ImGui::InputText("Search ##SCRIPT", search_buf, 256);
            std::string ssearch = search_buf;

            std::vector<EditorAsset*> assets;
            ContentManager::GetInstance().GetAllAssetsByType(assets, AssetType::EATYPE_SCRIPT);

            for (auto iter = assets.begin(); iter != assets.end(); iter++)
            {
                editor::Script* pScript = (editor::Script*)(*iter);

                if (ssearch.size() > 0)
                {
                    // Filter items based on search term
                    if (pScript->GetScriptFile().string().find(ssearch) == std::string::npos)
                    {
                        continue;
                    }
                }

                bool selected = false;
                if (ImGui::Selectable(pScript->GetScriptFile().string().c_str(), &selected))
                {
                    comp.ScriptID = pScript->GetID();
                }
            }

            ImGui::EndCombo();
        }
        
        return remove;
    }

    bool CompGui::RenderRigidBodyComp(RigidBodyComponent& comp)
    {
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Rigid Body Component");
        ImGui::PopStyleVar();

        bool is_static = comp.IsStatic;
        if (ImGuiExt::CheckBoxLeft("Is Static:", &is_static))
        {
            comp.IsStatic = is_static;
        }

        float mass = comp.Mass;
        if (ImGuiExt::FloatControl("Mass:", mass, 1.0f, 85.0f))
        {
            comp.Mass = mass;
        }

        bool freeze_rotation = comp.Defintion.fixedRotation;
        if (ImGuiExt::CheckBoxLeft("Freeze Rotation: ", &freeze_rotation))
        {
            comp.Defintion.fixedRotation = freeze_rotation;
        }

        bool is_high_speed = comp.Defintion.bullet;
        if (ImGuiExt::CheckBoxLeft("Is High Speed: ", &is_high_speed))
        {
            comp.Defintion.bullet = is_high_speed;
        }

        if (ImGui::IsItemHovered())
        {
            ImGui::SetTooltip("Is this a fast moving body that should be prevented from tunneling through other moving bodies?\n\
                            Note that all bodies are prevented from tunneling through kinematic and static bodies.\n\
                            This setting is only considered on dynamic bodies. \n\
                            warning: You should use this flag sparingly since it increases processing time.");
        }

        // TODO: Expose other body parameters (damping, gravity scale, sleeping, etc)
        //          Maybe as "advanced" parameters hidden in a collapsable area


        return remove;
    }

    bool CompGui::RenderCollisionComp(CollisionComponent& comp)
    {
        bool remove = false;
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f));
        remove = DrawTitle("Collision Component");
        ImGui::PopStyleVar();

        // Shape Type
        std::string preview = "NOT SET";
        switch (comp.Type)
        {
            case CollisionComponent::ShapeType::Box: preview = "Box"; break;
            case CollisionComponent::ShapeType::Circle: preview = "Circle"; break;
        }

        ImGui::SetNextItemWidth(85);
        if (ImGui::BeginCombo("Shape Type", preview.c_str()))
        {
            if (ImGui::Selectable("Box"))
            {
                comp.Type = CollisionComponent::ShapeType::Box;
            }

            if (ImGui::Selectable("Circle"))
            {
                comp.Type = CollisionComponent::ShapeType::Circle;
            }
            
            ImGui::EndCombo();
        }

        // Is Sensor
        bool Is_sensor = comp.IsSensor;  
        if (ImGuiExt::CheckBoxLeft("Is Sensor: ", &Is_sensor))
        {
            comp.IsSensor = Is_sensor;
        }

        if (ImGui::IsItemHovered())
        {
            ImGui::SetTooltip("A sensor does not generate collision responses (aka a trigger)");
        }
        
        if (comp.Type == CollisionComponent::ShapeType::Circle)
        {
            ImGuiExt::FloatControl("Radius:", comp.Radius, 1.0f, 85.0f);
        }

        if (comp.Type == CollisionComponent::ShapeType::Box)
        {
            ImGuiExt::FloatControl("Width:", comp.Width, 1.0f, 85.0f);
            ImGuiExt::FloatControl("Height:", comp.Height, 1.0f, 85.0f);
        }

        // Collision Group
        ImGui::Text("Collision Group: ");
        ImGui::SameLine();
        ImGui::SetNextItemWidth(80);
        ImGui::InputInt("##Collision Group", &comp.CollisionGroup, 1, 10);
        if (ImGui::IsItemHovered())
        {
            ImGui::SetTooltip("Only objects in the same Collision Group can collide");
        }

        // Physics properties
        ImGuiExt::FloatControl("Density:", comp.Density, 1.0f, 85.0f);
        ImGuiExt::FloatControl("Friction:", comp.Friction, 0.25f, 85.0f);
        ImGuiExt::FloatControl("Restitution:", comp.Restitution, 0.25f, 85.0f);

        
        return remove;
    }
}}