#include "Object3D.h"

#include <fstream>
#include <iostream>
#include <strstream>

Object3D::Object3D() = default;

Object3D::Object3D(const std::string &file_path)
{
    this->loadFromOBJ(file_path);
}

bool Object3D::loadFromOBJ(const std::string& file_path)
{
    std::ifstream f(file_path);
    if (!f.is_open())
        return false;

    std::vector<float> temp_vertices;
    std::vector<unsigned int> temp_indices;
    std::vector<float> temp_normals;

    while (!f.eof())
    {
        char line[128];
        f.getline(line, 128);

        std::string l(line);

        // std::cout << "LINE: " << l << std::endl;

        if (line[0] == 'f')
        {
            l.erase(0, 2); // Erase the "f "
            size_t pos1 = 0;
            std::string token1;

            // Split by space
            while ((pos1 = l.find(' ')) != std::string::npos)
            {
                token1 = l.substr(0, pos1);
                int face_vert;

                if (l.find("//") != std::string::npos)
                {
                    size_t pos2 = 0;
                    std::string token2;
                    bool face = true;

                    while ((pos2 = token1.find("//")) != std::string::npos)
                    {
                        token2 = token1.substr(0, pos2);
                        // std::cout << token1 << std::endl;

                        if (face)
                        {
                            face_vert = std::stoi(token2) - 1;
                            temp_indices.push_back(face_vert);
                            face = false;

                        }

                        token1.erase(0, pos2 + 2);
                    }

                    // The Normal paired with the face is here
                    if (!face) {
                        // std::cout << "thisg" << std::endl;
                        int face_normal = std::stoi(token1) - 1;
                        // std::cout << "f " << face_vert + 1 << "//" << face_normal + 1 << std::endl;

                        bool VBO_el_found = false;

                        // If Vertice AND correct normal are already in VBO buffer just refernce that
                        for (unsigned int i = 0; i < this->VBO_buffer.size(); i += 6)
                        {
                            bool vert_equal = true;
                            bool normal_equal = true;

                            // Since each Vertice & Normal are 3 elements we need to do a little looping
                            for (int a = 0; a < 3; a++)
                            {
                                if (this->VBO_buffer[i + a] != temp_vertices[(face_vert * 3) + a]) {
                                    vert_equal = false;
                                    break;
                                }
                            }

                            for (int a = 3; a < 6; a++)
                            {
                                if (this->VBO_buffer[i + a] != temp_normals[(face_normal * 3) + a - 3]) {
                                    normal_equal = false;
                                    break;
                                }
                            }

                            if (vert_equal && normal_equal) {
                                this->EBO_buffer.push_back(i / 6);
                                VBO_el_found = true;
                            }
                        }

                        // If not we need to add it to the VBO buffer
                        if (!VBO_el_found)
                        {
                            // Again we need to add all three elements for each
                            for (int a = 0; a < 3; a++)
                                this->VBO_buffer.push_back(temp_vertices[face_vert * 3 + a]);


                            for (int a = 0; a < 3; a++)
                                this->VBO_buffer.push_back(temp_normals[face_normal * 3 + a]);

                            this->EBO_buffer.push_back(this->VBO_buffer.size() / 6 - 1);
                        }
                    }

                    // std::cout << l << std::endl;

                    // temp_indices.push_back(std::stoi(token1) - 1);

                }

                l.erase(0, pos1 + 1);
            }

            // std::cout << "thing" << std::endl;
            // std::cout << l << std::endl;

            if (l.find("//") != std::string::npos)
            {
                size_t pos2 = 0;
                std::string token2;
                bool face = true;
                int face_vert;

                while ((pos2 = l.find("//")) != std::string::npos)
                {
                    token2 = l.substr(0, pos2);
                    face_vert = std::stoi(token2) - 1;


                    if (face)
                    {
                        temp_indices.push_back(face_vert);
                        face = false;
                    }
                    // Else case if I want to add normals for the faces

                    l.erase(0, pos2 + 2);
                }

                // The Normal paired with the face is here
                if (!face) {
                    // std::cout << "thisg" << std::endl;
                    int face_normal = std::stoi(l) - 1;
                    // std::cout << "f " << face_vert + 1 << "//" << face_normal + 1 << std::endl;

                    bool VBO_el_found = false;

                    // If Vertice AND correct normal are already in VBO buffer just refernce that
                    for (unsigned int i = 0; i < this->VBO_buffer.size(); i += 6)
                    {
                        bool vert_equal = true;
                        bool normal_equal = true;

                        // Since each Vertice & Normal are 3 elements we need to do a little looping
                        for (int a = 0; a < 3; a++)
                        {
                            if (this->VBO_buffer[i + a] != temp_vertices[(face_vert * 3) + a]) {
                                vert_equal = false;
                                break;
                            }
                        }

                        for (int a = 3; a < 6; a++)
                        {
                            if (this->VBO_buffer[i + a] != temp_normals[(face_normal * 3) + a - 3]) {
                                normal_equal = false;
                                break;
                            }
                        }

                        if (vert_equal && normal_equal) {
                            this->EBO_buffer.push_back(i / 6);
                            VBO_el_found = true;
                        }
                    }

                    // If not we need to add it to the VBO buffer
                    if (!VBO_el_found)
                    {
                        // Again we need to add all three elements for each
                        for (int a = 0; a < 3; a++)
                            this->VBO_buffer.push_back(temp_vertices[face_vert * 3 + a]);


                        for (int a = 0; a < 3; a++)
                            this->VBO_buffer.push_back(temp_normals[face_normal * 3 + a]);

                        this->EBO_buffer.push_back(this->VBO_buffer.size() / 6 - 1);
                    }
                }

                // std::cout << l << std::endl;

                // temp_indices.push_back(std::stoi(token1) - 1);

            }

        }
        else if (line[0] == 'v')
        {


            size_t pos1 = 0;
            std::string token1;
            bool normal = line[1] == 'n';

            if (normal)
            {
                l.erase(0, 3); // Get rid of the "vn "
            }
            else
            {
                l.erase(0, 2); // Get rid of the "v "
            }


            // Split by space
            while ((pos1 = l.find(' ')) != std::string::npos)
            {
                token1 = l.substr(0, pos1);

                if (normal)
                {
                    temp_normals.push_back(std::stof(token1));
                }
                else
                {
                    temp_vertices.push_back(std::stof(token1));
                }


                l.erase(0, pos1 + 1);
            }

            // Last point is still left after the loop
            if (normal)
            {
                temp_normals.push_back(std::stof(l));
            }
            else
            {
                temp_vertices.push_back(std::stof(l));
            }
        }
    }

    this->vertices = temp_vertices;
    this->indices = temp_indices;
    this->normals = temp_normals;

    return true;
}

std::vector<float> Object3D::getVBOBuffer()
{
    std::vector<float> temp_VBO_buffer;

    for (int i = 0; i < this->vertices.size() / 3; i++)
    {
        for (int a = 0; a < 3; a++)
            temp_VBO_buffer.push_back(this->vertices[(i * 3) + a]);
        for (int a = 0; a < 3; a++)
            temp_VBO_buffer.push_back(this->normals[(i * 3) + a]);
    }

    return temp_VBO_buffer;
}

