//
// Created by trotfunky on 30/09/2019.
//

#include "Model3D.h"


Model3D::Model3D() : vertex_count(0), face_count(0), texture_count(0), is_textured(false)
{}

Model3D::Model3D(const std::string& file_name) : Model3D()
{
    load_ascii_off_file(file_name);
}

Model3D::~Model3D()
{
    if (vertex_count > 0)
    {
        delete(vertices);
    }
    if (face_count > 0)
    {
        delete(faces);
    }

    if (is_textured && texture_count > 0)
    {
        delete(texture_coordinates);
        delete(face_textures);
        delete(normals);
    }
}

void Model3D::draw_model()
{
    if (is_textured)
    {
        glEnable(GL_ALPHA_TEST);

        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
        glBindTexture(GL_TEXTURE_2D,texture->opengl_id[0]);

        glColor4f(1.0f, 1.0f, 1.0f,1.00);
    }

    glPushMatrix();

    glScalef(scaling.x,scaling.y,scaling.z);
    glRotatef(rotation_angle,rotation_axis.x,rotation_axis.y,rotation_axis.z);

    glBegin(GL_TRIANGLES);

    for (uint32_t i = 0;i<face_count;i++)
    {
        Vec3i face = faces[i];

        Vec3i* face_texture;
        if (is_textured)
        {
            face_texture = &face_textures[i];
        }

        // Draw each vertex and texture it if needed
        for (int j = 0; j < 3; j++)
        {
            Vec3f& vertex = vertices[face.coordinates[j]];

            if (is_textured)
            {
                Vec2f vertex_texture = texture_coordinates[face_texture->coordinates[j]];
                glTexCoord2f(vertex_texture.x,vertex_texture.y);
            }
            glVertex3f(vertex.x,vertex.y,vertex.z);
        }
    }

    glEnd();
    glPopMatrix();

    if (is_textured)
    {
        glDisable(GL_ALPHA_TEST);
    }
}

bool Model3D::load_ascii_off_file(const std::string& file_name)
{
    std::fstream file_stream(file_name, std::ios_base::in);
    if (file_stream.fail())
    {
        std::cerr << "Error opening " << file_name << std::endl;
        return false;
    }

    std::string temp;
    // Discard the first line (OFF Header)
    std::getline(file_stream,temp);

    // Ignore comments at the beginning of the file
    while (file_stream.peek() == '#')
    {
        if (!std::getline(file_stream,temp))
        {
            std::cerr << "Error while parsing OFF comments in " << file_name << std::endl;
            file_stream.close();
            return false;
        }
    }

    // Parse count information (edge_count is can be safely ignored)
    int edge_count;
    file_stream >> vertex_count >> face_count >> edge_count;

    vertices = new Vec3f[vertex_count];
    faces = new Vec3i[face_count];

    for (uint32_t i = 0;i<vertex_count;i++)
    {
        file_stream >> vertices[i];
    }

    for (uint32_t i = 0;i<face_count;i++)
    {
        // Check the the edge count of each face
        file_stream >> edge_count;
        if (edge_count != 3)
        {
            std::cerr << "Models must only have triangles !" << std::endl;
            file_stream.close();
            return false;
        }
        file_stream >> faces[i];
    }

    if (!file_stream)
    {
        std::cerr << "Unexpected EOF while reading model data in " << file_name << std::endl;
        file_stream.close();
        return false;
    }

    while(std::getline(file_stream,temp) && temp.find("EXT"));

    file_stream >> texture_count;

    if (texture_count > 0)
    {
        is_textured = true;

        texture_coordinates = new Vec2f[texture_count];
        face_textures = new Vec3i[face_count];
        normals = new Vec3f[face_count];

        // Parse U,V coordinates
        for (uint32_t i = 0;i<texture_count;i++)
        {
            file_stream >> texture_coordinates[i];
        }
        // Assign U,V coordinates to vertices
        for (uint32_t i = 0;i<face_count;i++)
        {
            file_stream >> face_textures[i];
        }

        for (uint32_t i=0;i<vertex_count;i++)
        {
            file_stream >> normals[i];
        }

        if (!file_stream)
        {
            std::cerr << "Error while parsing texture data in " << file_name << std::endl;
            file_stream.close();
            return false;
        }
    }

    return true;
}

bool Model3D::assign_texture(Texture& new_texture)
{
    if (is_textured)
    {
        texture = &new_texture;
        return true;
    }
    else
    {
        return false;
    }
}

void Model3D::set_scaling(float x_scale, float y_scale, float z_scale)
{
    scaling.x = x_scale;
    scaling.y = y_scale;
    scaling.z = z_scale;
}

void Model3D::set_rotation(float angle, float x, float y, float z)
{
    rotation_axis.x = x;
    rotation_axis.y = y;
    rotation_axis.z = z;
    rotation_angle = angle;
}
