local Object = require 'libs.classic'

local Grid = Object:extend()

local empty = { skin = "", colour = "", oob=false }
local oob = { skin = "", colour = "", oob=true }
local block = { skin = "2tie", colour = "A", oob=false }

function Grid:new(width, height)
    self.grid = {}
    self.grid_age = {}
    self.width = width
    self.height = height
    for y = 1, self.height do
        self.grid[y] = {}
        self.grid_age[y] = {}
        for x = 1, self.width do
            self.grid[y][x] = empty
            self.grid_age[y][x] = 0
        end
    end
end

function Grid:clear()
    for y = 1, self.height do
        for x = 1, self.width do
            self.grid[y][x] = empty
            self.grid_age[y][x] = 0
        end
    end
end

function Grid:getCell(x, y)
    if x < 1 or x > self.width or y > self.height then return oob
    elseif y < 1 then return oob
    else return self.grid[y][x]
    end
end

function Grid:isOccupied(x, y)
    return self:getCell(x+1, y+1) ~= empty
end

function Grid:isRowFull(row)
    for index, square in pairs(self.grid[row]) do
        if square == empty then return false end
    end
    return true
end

function Grid:canPlacePiece(piece)
    local offsets = piece:getBlockOffsets()
    for index, offset in pairs(offsets) do
        local x = piece.position.x + offset.x
        local y = piece.position.y + offset.y
        if self:isOccupied(x, y) then
            return false
        end
    end
    return true
end

function Grid:canPlacePieceInVisibleGrid(piece)
    local offsets = piece:getBlockOffsets()
    for index, offset in pairs(offsets) do
        local x = piece.position.x + offset.x
        local y = piece.position.y + offset.y
        if y < 1 or self:isOccupied(x, y) ~= empty then
            return false
        end
    end
    return true
end

function Grid:getClearedRowCount()
    local count = 0
    local cleared_row_table = {}
    for row = 1, self.height do
        if self:isRowFull(row) then
            count = count + 1
            table.insert(cleared_row_table, row)
        end
    end
    return count, cleared_row_table
end

function Grid:markClearedRows()
    local block_table = {}
    for row = 1, self.height do
        if self:isRowFull(row) then
            block_table[row] = {}
            for x = 1, self.width do
                block_table[row][x] = {
                    skin = self.grid[row][x].skin,
                    colour = self.grid[row][x].colour,
                }
                self.grid[row][x] = {
                    skin = self.grid[row][x].skin,
                    colour = "X"
                }
            end
        end
    end
    return block_table
end

function Grid:clearClearedRows()
    for row = 1, self.height do
        if self:isRowFull(row) then
            for above_row = row, 2, -1 do
                self.grid[above_row] = self.grid[above_row - 1]
                self.grid_age[above_row] = self.grid_age[above_row - 1]
            end
            self.grid[1] = {}
            self.grid_age[1] = {}
            for i = 1, self.width do
                self.grid[1][i] = empty
                self.grid_age[1][i] = 0
            end
        end
    end
    return true
end

function Grid:clearSpecificRow(row)
    for col = 1, self.width do
        self.grid[row][col] = empty
    end
end

function Grid:applyPiece(piece)
    if piece.big then
        self:applyBigPiece(piece)
        return
    end
    offsets = piece:getBlockOffsets()
    for index, offset in pairs(offsets) do
        x = piece.position.x + offset.x
        y = piece.position.y + offset.y
        if y + 1 > 0 and y < self.height then
            self.grid[y+1][x+1] = {
                skin = piece.skin,
                colour = piece.colour,
                flash = 5
            }
        end
    end
end

function Grid:checkStackHeight()
    for i = 0, self.height - 1 do
        for j = 0, self.width - 1 do
            if self:isOccupied(j, i) then return self.height - i end
        end
    end
    return 0
end

function Grid:applyMap(map)
    for y, row in pairs(map) do
        for x, block in pairs(row) do
            self.grid_age[y][x] = 0
            self.grid[y][x] = block
        end
    end
end

function Grid:update()
    for y = 1, self.height do
        for x = 1, self.width do
            if self.grid[y][x] ~= empty then
                self.grid_age[y][x] = self.grid_age[y][x] + 1
            end
        end
    end
end

function Grid:draw(greyscale, timer)
	love.graphics.setColor(0,0,0,1)
	love.graphics.rectangle("fill", 256, 31, 80, 45, 0, 0)
	love.graphics.setColor(0.3, 0.3, 0.3, 1)
	love.graphics.line(256,31,256,31+45)
	love.graphics.line(256,31,256+80,31)
	love.graphics.line(256+80,31,256+80,31+45)
	love.graphics.line(256,31+45,256+80,31+45)
    for y = 1, self.height do
        for x = 1, self.width do
            if BLOCKS[self.grid[y][x].skin] and
            BLOCKS[self.grid[y][x].skin][self.grid[y][x].colour] then
				if self.grid[y][x].flash ~= nil then
					if self.grid[y][x].flash > 0 then
						love.graphics.setColor(0.4+(self.grid[y][x].flash*0.1), 0.4+(self.grid[y][x].flash*0.1), 0.4+(self.grid[y][x].flash*0.1), 1)
						love.graphics.draw(BLOCKS[self.grid[y][x].skin]['W'], 200+x*16, 64+y*16)
						self.grid[y][x].flash = self.grid[y][x].flash - 1
					else
						love.graphics.setColor(1, 1, 1, 1)
						love.graphics.draw(BLOCKS[self.grid[y][x].skin][self.grid[y][x].colour..'_d'], 200+x*16, 64+y*16)
					end
				end
                if greyscale then
                    if timer > 1 then timer = 1 end
                    love.graphics.setColor(0.7, 0.7, 0.7, 0+timer)
                    if self.grid[y][x] ~= empty and self.grid[y][x].colour ~= 'X' then
                        love.graphics.draw(BLOCKS[self.grid[y][x].skin]["A"], 200+x*16, 64+y*16)
                    end
                end
            end
        end
    end
end

return Grid
