//
// Created by trotfunky on 04/11/2019.
//

#include "Maze.h"


Maze::Maze(const std::string& maze_map, unsigned int scaling_factor, const Texture& floor_texture,
           const Texture& wall_texture, const Texture& outside_texture, Model3D& objective) :
    scaling_factor(scaling_factor),
    floor(floor_texture),
    wall(wall_texture),
    out(outside_texture),
    objective(objective)
{
    map.load_rgb_tga(maze_map);

    corner_pos = map.width * this->scaling_factor;

    for (unsigned int x = 0; x < map.width; x++)
        for (unsigned int y = 0; y < map.height; y++)
        {
            unsigned int data_index = (x*map.height + y)*(map.color_bits/8);

            Vec3i colour_data{
                map.image_data[data_index],
                map.image_data[data_index+1],
                map.image_data[data_index+2]};

            if (colour_data == Tiles::objective)
            {
                objective_positions.emplace_back(x,objective_altitude,y);
            }
            else if (colour_data == Tiles::wall)
            {
                wall_positions.emplace_back(x,wall_height,y);
            }
            else if (colour_data == Tiles::end)
            {
                outside_position = Vec3d{static_cast<double>(x),
                                         wall_height,
                                         static_cast<double>(y)};
            }
        }

    total_objectives = objective_positions.size();
    preapre_display_list();
}

Maze::~Maze()
{
    if (display_list_index != -1)
    {
        glDeleteLists(display_list_index,1);
    }
}

void Maze::draw()
{
    // Draw Maze
    glCallList(display_list_index);

    // Draw objectives
    for (const Vec3d& objective_pos: objective_positions)
    {
        glPushMatrix();
        glTranslatef((objective_pos.x+0.5)*scaling_factor,objective_pos.y,(objective_pos.z+0.5)*scaling_factor);
        objective.draw_model();
        glPopMatrix();
    }
}

bool Maze::remove_objective(const Vec2i& position)
{
    Vec3d objective{static_cast<double>(position.x),
                    objective_altitude,
                    static_cast<double>(position.y)};

    for (auto it = objective_positions.begin();it!=objective_positions.end();++it)
    {
        if (objective == *it)
        {
            objective_positions.erase(it);
            return true;
        }
    }
    return false;
}

void Maze::preapre_display_list()
{
    display_list_index = glGenLists(1);

    glNewList(display_list_index,GL_COMPILE);

    // Draw ground

    glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    glBindTexture(GL_TEXTURE_2D,floor.opengl_id[0]);

    // Deactivate interpolation
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

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

    glBegin(GL_QUADS);
    glTexCoord2f(0,0);
    glVertex3i(0,0,0);

    glTexCoord2d(50,0);
    glVertex3i(0,0,corner_pos);

    glTexCoord2d(50,50);
    glVertex3i(corner_pos,0,corner_pos);

    glTexCoord2d(0,50);
    glVertex3i(corner_pos,0,0);
    glEnd();


    // Draw walls

    glBindTexture(GL_TEXTURE_2D,wall.opengl_id[0]);

    // Deactivate interpolation
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

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

    glBegin(GL_QUADS);
    for (const Vec3d& wall_tile: wall_positions)
    {
        // TODO : Factor with another for
        glTexCoord2d(0,1);
        glVertex3f(wall_tile.x*scaling_factor,0,wall_tile.z*scaling_factor);
        glTexCoord2d(0,0);
        glVertex3f(wall_tile.x*scaling_factor,wall_height,wall_tile.z*scaling_factor);
        glTexCoord2d(1,0);
        glVertex3f((wall_tile.x+1)*scaling_factor,wall_height,wall_tile.z*scaling_factor);
        glTexCoord2d(1,1);
        glVertex3f((wall_tile.x+1)*scaling_factor,0,wall_tile.z*scaling_factor);

        glTexCoord2d(0,1);
        glVertex3f(wall_tile.x*scaling_factor,0,wall_tile.z*scaling_factor);
        glTexCoord2d(0,0);
        glVertex3f(wall_tile.x*scaling_factor,wall_height,wall_tile.z*scaling_factor);
        glTexCoord2d(1,0);
        glVertex3f(wall_tile.x*scaling_factor,wall_height,(wall_tile.z+1)*scaling_factor);
        glTexCoord2d(1,1);
        glVertex3f(wall_tile.x*scaling_factor,0,(wall_tile.z+1)*scaling_factor);

        glTexCoord2d(0,1);
        glVertex3f((wall_tile.x+1)*scaling_factor,0,(wall_tile.z+1)*scaling_factor);
        glTexCoord2d(0,0);
        glVertex3f((wall_tile.x+1)*scaling_factor,wall_height,(wall_tile.z+1)*scaling_factor);
        glTexCoord2d(1,0);
        glVertex3f(wall_tile.x*scaling_factor,wall_height,(wall_tile.z+1)*scaling_factor);
        glTexCoord2d(1,1);
        glVertex3f(wall_tile.x*scaling_factor,0,(wall_tile.z+1)*scaling_factor);

        glTexCoord2d(0,1);
        glVertex3f((wall_tile.x+1)*scaling_factor,0,(wall_tile.z+1)*scaling_factor);
        glTexCoord2d(0,0);
        glVertex3f((wall_tile.x+1)*scaling_factor,wall_height,(wall_tile.z+1)*scaling_factor);
        glTexCoord2d(1,0);
        glVertex3f((wall_tile.x+1)*scaling_factor,wall_height,wall_tile.z*scaling_factor);
        glTexCoord2d(1,1);
        glVertex3f((wall_tile.x+1)*scaling_factor,0,wall_tile.z*scaling_factor);
    }
    glEnd();

    // Draw outside

    glBindTexture(GL_TEXTURE_2D,out.opengl_id[0]);

    // Deactivate interpolation
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

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

    glBegin(GL_QUADS);

    glTexCoord2d(0,1);
    glVertex3f(outside_position.x*scaling_factor,0,outside_position.z*scaling_factor);
    glTexCoord2d(0,0);
    glVertex3f(outside_position.x*scaling_factor,wall_height,outside_position.z*scaling_factor);
    glTexCoord2d(1,0);
    glVertex3f((outside_position.x+1)*scaling_factor,wall_height,outside_position.z*scaling_factor);
    glTexCoord2d(1,1);
    glVertex3f((outside_position.x+1)*scaling_factor,0,outside_position.z*scaling_factor);

    glTexCoord2d(0,1);
    glVertex3f(outside_position.x*scaling_factor,0,outside_position.z*scaling_factor);
    glTexCoord2d(0,0);
    glVertex3f(outside_position.x*scaling_factor,wall_height,outside_position.z*scaling_factor);
    glTexCoord2d(1,0);
    glVertex3f(outside_position.x*scaling_factor,wall_height,(outside_position.z+1)*scaling_factor);
    glTexCoord2d(1,1);
    glVertex3f(outside_position.x*scaling_factor,0,(outside_position.z+1)*scaling_factor);

    glTexCoord2d(0,1);
    glVertex3f((outside_position.x+1)*scaling_factor,0,(outside_position.z+1)*scaling_factor);
    glTexCoord2d(0,0);
    glVertex3f((outside_position.x+1)*scaling_factor,wall_height,(outside_position.z+1)*scaling_factor);
    glTexCoord2d(1,0);
    glVertex3f(outside_position.x*scaling_factor,wall_height,(outside_position.z+1)*scaling_factor);
    glTexCoord2d(1,1);
    glVertex3f(outside_position.x*scaling_factor,0,(outside_position.z+1)*scaling_factor);

    glTexCoord2d(0,1);
    glVertex3f((outside_position.x+1)*scaling_factor,0,(outside_position.z+1)*scaling_factor);
    glTexCoord2d(0,0);
    glVertex3f((outside_position.x+1)*scaling_factor,wall_height,(outside_position.z+1)*scaling_factor);
    glTexCoord2d(1,0);
    glVertex3f((outside_position.x+1)*scaling_factor,wall_height,outside_position.z*scaling_factor);
    glTexCoord2d(1,1);
    glVertex3f((outside_position.x+1)*scaling_factor,0,outside_position.z*scaling_factor);

    glEnd();

    glEndList();
}
