/*
 *  <Short Description>
 *  Copyright (C) 2024  Brett Terpstra
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef BLT_WITH_GRAPHICS_OBJ_LOADER_H
#define BLT_WITH_GRAPHICS_OBJ_LOADER_H

#include "blt/math/vectors.h"
#include "blt/std/hashmap.h"
#include <utility>
#include <vector>
#include <string_view>
#include <string>

namespace blt::parse
{
    
    typedef blt::vec3f vertex_t;
    typedef blt::vec2f uv_t;
    typedef blt::vec3f normal_t;
    typedef blt::vec3f color_t;
    
    class model_data
    {
        public:
        private:
            std::vector<vertex_t> vertices;
            std::vector<uv_t> uvs;
            std::vector<normal_t> normals;
    };
    
    struct face_t
    {
        std::int32_t vertex, uv, normal;
    };
    
    static inline bool operator==(const face_t& f1, const face_t& f2)
    {
        return f1.vertex == f2.vertex && f1.uv == f2.uv && f1.normal == f2.normal;
    }
    
    struct face_hash
    {
        size_t operator()(const face_t& face) const
        {
            std::hash<std::int32_t> hasher;
            return hasher(face.vertex) ^ hasher(face.uv) ^ hasher(face.normal);
        }
    };
    
    struct face_eq
    {
        bool operator()(const face_t& f1, const face_t& f2) const
        {
            return f1 == f2;
        }
    };
    
    struct constructed_vertex_t
    {
        vertex_t vertex;
        uv_t uv;
        normal_t normal;
    };
    
    struct triangle_t
    {
        std::int32_t v[3];
    };
    
    struct quad_t
    {
        std::int32_t v[4];
    };
    
    struct material_t
    {
        std::string material_name;
        color_t ambient;
        color_t diffuse;
        color_t specular;
        float specular_exponent = 0.0f;
        float transparency = 1.0f;
        color_t transmission_filter_color{1, 1, 1};
        std::string texture_ambient;
        std::string texture_diffuse;
        std::string map_spec_color;
        std::string map_spec_highlight;
        std::string map_bump;
        std::string map_displacement;
    };
    
    struct object_data
    {
        std::vector<std::string> object_names;
        std::string material;
        std::vector<triangle_t> indices;
    };
    
    class obj_model_t
    {
        private:
            std::vector<constructed_vertex_t> vertex_data_;
            std::vector<object_data> objects_;
            hashmap_t<std::string, material_t> materials_;
        public:
            obj_model_t(std::vector<constructed_vertex_t>&& vertex_data, std::vector<object_data>&& objects, hashmap_t<std::string, material_t>&& mats):
                    vertex_data_(vertex_data), objects_(objects), materials_(mats)
            {}
            
            inline const auto& vertex_data()
            {
                return vertex_data_;
            };
            
            inline const auto& objects()
            {
                return objects_;
            };
            
            inline const auto& materials()
            {
                return materials_;
            }
    };
    
    class char_tokenizer;
    
    class obj_loader
    {
        private:
            std::vector<vertex_t> vertices;
            std::vector<uv_t> uvs;
            std::vector<normal_t> normals;
            
            // maps between face (constructed vertex) -> vertex indices
            hashmap_t<face_t, std::int32_t, face_hash, face_eq> vertex_map;
            std::vector<constructed_vertex_t> vertex_data;
            object_data current_object;
            std::vector<object_data> data;
            hashmap_t<std::string, material_t> materials;
            
            size_t current_line = 0;
        private:
            bool handle_vertex_and_normals(float x, float y, float z, char type);
            
            void parse_vertex_line(char_tokenizer& tokenizer);
            
            void parse_face(char_tokenizer& tokenizer);
            
            void handle_face_vertex(const std::vector<std::string>& face_list, std::int32_t* arr);
        
        public:
            obj_model_t parseFile(std::string_view file);
    };
    
    obj_model_t quick_load(std::string_view file);
    
}

#endif //BLT_WITH_GRAPHICS_OBJ_LOADER_H