#pragma once

#include <cstdint>
#include <vector>

#include "Camera.h"
#include "DataTypes.h"
#include "Maths.h"
#include "Texture.h"

struct SDL_Window;
struct SDL_Surface;


namespace dae {

    enum class ShadeMode{
        ObservedArea,
        Diffuse,
        Specular,
        Combined
    };

    struct Mesh;

    class Timer;

    class Scene;

    class Renderer final {
    public:
        Renderer(SDL_Window *pWindow);

        ~Renderer();

        Renderer(const Renderer &) = delete;

        Renderer(Renderer &&) noexcept = delete;

        Renderer &operator=(const Renderer &) = delete;

        Renderer &operator=(Renderer &&) noexcept = delete;

        void Update(Timer *pTimer);

        void Render();

        bool SaveBufferToImage() const;

        void
        VertexTransformationFunction(const Matrix& WorldViewProjectionMatrix, const Mesh& mesh,  const std::vector<Vertex> &vertices_in, std::vector<Vertex> &vertices_out) const;

        ColorRGB shadePixel(const Sample& sample);

        void SwitchDepthBuffer(){
            m_isDepthBuffer = !m_isDepthBuffer;
        }

        void SetShadeMode(ShadeMode mode) {
            m_ShadeMode = mode;
        }

        void ToggleRotation(){
            m_isRotating = !m_isRotating;
            std::cout << "Rotation: " << m_isRotating << std::endl;
        }

        void ToggleNormals(){
            m_useNormals = !m_useNormals;
            std::cout << "Use Normals: " << m_useNormals << std::endl;
        }

        void CycleRenderingMode(){
            switch (m_ShadeMode) {
                case ShadeMode::ObservedArea:
                    m_ShadeMode = ShadeMode::Diffuse;
                    std::cout << "Diffuse" << std::endl;
                    break;
                case ShadeMode::Diffuse:
                    m_ShadeMode = ShadeMode::Specular;
                    std::cout << "Specular" << std::endl;
                    break;
                case ShadeMode::Specular:
                    m_ShadeMode = ShadeMode::Combined;
                    std::cout << "Combined" << std::endl;
                    break;
                case ShadeMode::Combined:
                    m_ShadeMode = ShadeMode::ObservedArea;
                    std::cout << "Observed Area" << std::endl;
                    break;
            }
        }

    private:
        SDL_Window *m_pWindow{};

        Uint8 r{100};
        Uint8 g{100};
        Uint8 b{100};
        Uint32 m_ClearColor{ Uint32(255 << 24) + Uint32(r << 16) + Uint32(g << 8) + Uint32(b) };

        SDL_Surface* m_pFrontBuffer{ nullptr };
        SDL_Surface* m_pBackBuffer{ nullptr };
        uint32_t* m_pBackBufferPixels{};

        float m_aspectRatio{};

        Texture* m_currentDiffuse{ nullptr };
        Texture* m_currentGloss{ nullptr };
        Texture* m_currentNormal{ nullptr };
        Texture* m_currentSpecular{ nullptr };


        ShadeMode m_ShadeMode{ ShadeMode::Combined };
        bool m_isRotating{ true };
        bool m_useNormals{ true };

        Mesh m_mesh{};

        bool m_isDepthBuffer{ false };


        std::vector<Mesh> m_worldMeshes{};
        std::vector<Vertex> verticiesScreenSpace{};

        float* m_pDepthBufferPixels{};

        Camera m_Camera{};

        int m_Width{};
        int m_Height{};
    };
}
