//
//  Grid.cpp
//  prog2
//
//  Created by Neil Killeen on 5/9/13.
//  Copyright (c) 2013 Neil Killeen. All rights reserved.
//

#include "Grid.h"

Node::Node() : filled(false) {
    
}

// Private

void Grid::Initialize(unsigned w, unsigned h, unsigned d) {
    Elements.resize(h);
    for (int y = 0; y < h; y++) {
        Elements[y].resize(w);
        for (int x = 0; x < w; x++) {
            Elements[y][x].resize(d);
            for (int z = 0; z < d; z++) {
                Elements[y][x][z].filled = false;
                Elements[y][x][z].color = color4();
            }
        }
    }
}

// Public

Grid::Grid(unsigned w, unsigned h, unsigned d) : width(w), height(h), depth(d), lock(false) {
    Initialize(w, h, d);
    start_pos = vec3((w-1)/2, h-2, (d-1)/2);
    piece_speed = 500;
    
    cube.readVertices("cube.coor");
    cube.readPolygons("cube.poly");
    grid.readVertices("grid.coor");
    grid.readPolygons("grid.poly");
    cube.calcNormals();
    grid.calcNormals();
}

void Grid::Resize(unsigned w, unsigned h, unsigned d) {
    Initialize(w, h, d);
}

void Grid::setNode(point3 position, color4 color, bool filled) {
    int x = round(position.x);
    int y = round(position.y);
    int z = round(position.z);
    if (InBounds(position)) {
        Elements[y][x][z].color = color;
        Elements[y][x][z].filled = filled;
    }
}

bool Grid::InBounds(const point3 &position) const {
    int x = round(position.x);
    int y = round(position.y);
    int z = round(position.z);
    if (x < width && y < height && z < depth && x >= 0 && y >= 0 && z >= 0)
        return true;
    return false;
}

Node* Grid::getElement(unsigned x, unsigned y, unsigned z) {
    if (x < width && y < height && z < depth)
        return &Elements[y][x][z];
    return nullptr;
}

Node* Grid::getElement(const vec3& position) {
    if (InBounds(position))
        return &Elements[round(position.y)][round(position.x)][round(position.z)];
    return nullptr;
}

void Grid::drawAll(GLuint &program, GLuint &vPos, GLuint &vNormal, GLuint &ModelView) {
	if (getLock()) 
		return;
	glUniform4fv( glGetUniformLocation(program, "DiffuseProduct"), 1, color4(0,0,0,0.5) );
    grid.draw(vPos, vNormal, ModelView, 0, false);
    for (int x = 0; x < width; ++x)
        for (int y = 0; y < height; ++y)
            for (int z = 0; z < depth; ++z) {
                if (!Elements[y][x][z].filled)
                    continue;
                Mesh curr_cube = cube;
                glUniform4fv( glGetUniformLocation(program, "DiffuseProduct"), 1, Elements[y][x][z].color );
                curr_cube.mTranslate(x*2, y*2, z*2);
                curr_cube.draw(vPos, vNormal, ModelView, 1);
            }
}

bool Grid::CheckLose() {
	if ( height == Elements.size() )
		return getElement(start_pos)->filled;
	return true;
}

void Grid::CheckLines() {    
    for (int y = 0; y < height; ++y) {
        bool line_complete = true;
        for (int x = 0; x < width && line_complete; ++x)
            for (int z = 0; z < depth && line_complete; ++z)
                line_complete = Elements[y][x][z].filled;
        if (!line_complete)
            continue;
        
        Elements.push_back(vector<vector<Node> >());
        Elements[Elements.size()-1].resize(width);
        for (int x = 0; x < width; x++) {
            Elements[Elements.size()-1][x].resize(depth);
            for (int z = 0; z < depth; z++) {
                Elements[Elements.size()-1][x][z].filled = false;
                Elements[Elements.size()-1][x][z].color = color4();
            }
        }
        
        Elements.erase(Elements.begin() + y);
    }
}

void Grid::Clear() {
    Initialize(10, 20, 10);
}
