local Object = require 'libs.classic'
local Piece = require 'tetris.components.piece'

local Ruleset = Object:extend()

Ruleset.name = ""
Ruleset.hash = ""

-- Arika-type ruleset defaults
Ruleset.world = false
Ruleset.colourscheme = {
	I = "R",
	L = "O",
	J = "B",
	S = "M",
	Z = "G",
	O = "Y",
	T = "C",
}
Ruleset.softdrop_lock = true
Ruleset.harddrop_lock = false

Ruleset.enable_IRS_wallkicks = false

-- Component functions.

function Ruleset:new()
	if config.gamesettings.piece_colour == 1 then
		blocks["bone"] = (not self.world) and
		{
			R = love.graphics.newImage("res/img/bone.png"),
			O = love.graphics.newImage("res/img/bone.png"),
			Y = love.graphics.newImage("res/img/bone.png"),
			G = love.graphics.newImage("res/img/bone.png"),
			C = love.graphics.newImage("res/img/bone.png"),
			B = love.graphics.newImage("res/img/bone.png"),
			M = love.graphics.newImage("res/img/bone.png"),
			F = love.graphics.newImage("res/img/bone.png"),
			A = love.graphics.newImage("res/img/bone.png"),
			X = love.graphics.newImage("res/img/bone.png"),
		} or {
			R = love.graphics.newImage("res/img/bonew.png"),
			O = love.graphics.newImage("res/img/bonew.png"),
			Y = love.graphics.newImage("res/img/bonew.png"),
			G = love.graphics.newImage("res/img/bonew.png"),
			C = love.graphics.newImage("res/img/bonew.png"),
			B = love.graphics.newImage("res/img/bonew.png"),
			M = love.graphics.newImage("res/img/bonew.png"),
			F = love.graphics.newImage("res/img/bonew.png"),
			A = love.graphics.newImage("res/img/bonew.png"),
			X = love.graphics.newImage("res/img/bonew.png"),
		}
	else
		blocks["bone"] = (config.gamesettings.piece_colour == 2) and
                {
                        R = love.graphics.newImage("res/img/bone.png"),
                        O = love.graphics.newImage("res/img/bone.png"),
                        Y = love.graphics.newImage("res/img/bone.png"),
                        G = love.graphics.newImage("res/img/bone.png"),
                        C = love.graphics.newImage("res/img/bone.png"),
                        B = love.graphics.newImage("res/img/bone.png"),
                        M = love.graphics.newImage("res/img/bone.png"),
                        F = love.graphics.newImage("res/img/bone.png"),
                        A = love.graphics.newImage("res/img/bone.png"),
                        X = love.graphics.newImage("res/img/bone.png"),
                } or {
                        R = love.graphics.newImage("res/img/bonew.png"),
                        O = love.graphics.newImage("res/img/bonew.png"),
                        Y = love.graphics.newImage("res/img/bonew.png"),
                        G = love.graphics.newImage("res/img/bonew.png"),
                        C = love.graphics.newImage("res/img/bonew.png"),
                        B = love.graphics.newImage("res/img/bonew.png"),
                        M = love.graphics.newImage("res/img/bonew.png"),
                        F = love.graphics.newImage("res/img/bonew.png"),
                        A = love.graphics.newImage("res/img/bonew.png"),
                        X = love.graphics.newImage("res/img/bonew.png"),
                }
	end
end

function Ruleset:rotatePiece(inputs, piece, grid, prev_inputs, initial)
	local new_inputs = {}

	for input, value in pairs(inputs) do
		if value and not prev_inputs[input] then
			new_inputs[input] = true
		end
	end

	self:attemptRotate(new_inputs, piece, grid, initial)

	-- prev_inputs becomes the previous inputs
	for input, value in pairs(inputs) do
		prev_inputs[input] = inputs[input]
	end
end

function Ruleset:attemptRotate(new_inputs, piece, grid, initial)
	local rot_dir = 0
	
	if (new_inputs["rotate_left"] or new_inputs["rotate_left2"]) then
		rot_dir = 3
	elseif (new_inputs["rotate_right"] or new_inputs["rotate_right2"]) then
		rot_dir = 1
	elseif (new_inputs["rotate_180"]) then
		rot_dir = self:get180RotationValue()
	end

	if rot_dir == 0 then return end
    if config.gamesettings.world_reverse == 3 or (self.world and config.gamesettings.world_reverse == 2) then
        rot_dir = 4 - rot_dir
    end

	local new_piece = piece:withRelativeRotation(rot_dir)

	if (grid:canPlacePiece(new_piece)) then
		piece:setRelativeRotation(rot_dir)
		self:onPieceRotate(piece, grid)
	else
		if not(initial and self.enable_IRS_wallkicks == false) then
			self:attemptWallkicks(piece, new_piece, rot_dir, grid)
		end
	end
end

function Ruleset:attemptWallkicks(piece, new_piece, rot_dir, grid)
	-- do nothing in default ruleset
end

function Ruleset:movePiece(piece, grid, move)
	local x = piece.position.x
	if move == "left" then
		piece:moveInGrid({x=-1, y=0}, 1, grid)
	elseif move == "speedleft" then
		piece:moveInGrid({x=-1, y=0}, 10, grid)
	elseif move == "right" then
		piece:moveInGrid({x=1, y=0}, 1, grid)
	elseif move == "speedright" then
		piece:moveInGrid({x=1, y=0}, 10, grid)
	end
	if piece.position.x ~= x then
		self:onPieceMove(piece, grid)
	end
end

function Ruleset:dropPiece(
	inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
	hard_drop_enabled, additive_gravity
)
	local y = piece.position.y
	if inputs["down"] == true and drop_locked == false then
		if additive_gravity then
			piece:addGravity(gravity + drop_speed, grid)
		else
			piece:addGravity(math.max(gravity, drop_speed), grid)
		end
	elseif inputs["up"] == true and hard_drop_enabled == true then
		if hard_drop_locked == true or piece:isDropBlocked(grid) then
			piece:addGravity(gravity, grid)
		else
			piece:dropToBottom(grid)
		end
	else
		piece:addGravity(gravity, grid)
	end
	if piece.position.y ~= y then
		self:onPieceDrop(piece, grid)
	end
end

function Ruleset:lockPiece(piece, grid, lock_delay)
	if piece:isDropBlocked(grid) and piece.gravity >= 1 and piece.lock_delay >= lock_delay then
		piece.locked = true
	end
end

function Ruleset:get180RotationValue() return 2 end
function Ruleset:getDefaultOrientation() return 1 end

function Ruleset:initializePiece(
	inputs, data, grid, gravity, prev_inputs,
	move, lock_delay, drop_speed,
	drop_locked, hard_drop_locked, big
)
	local spawn_positions
	if big then
		spawn_positions = self.big_spawn_positions
	else
		spawn_positions = self.spawn_positions
	end
	local colours = ({self.colourscheme, ColourSchemes.Arika, ColourSchemes.TTC})[config.gamesettings.piece_colour]
	
	local piece = Piece(data.shape, data.orientation - 1, {
		x = spawn_positions[data.shape].x,
		y = spawn_positions[data.shape].y
	}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)

	self:onPieceCreate(piece)
	self:rotatePiece(inputs, piece, grid, {}, true)
	self:dropPiece(inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked)
	return piece
end

-- stuff like move count, rotate count, floorkick count go here
function Ruleset:onPieceCreate(piece) end

function Ruleset:processPiece(
	inputs, piece, grid, gravity, prev_inputs,
	move, lock_delay, drop_speed,
	drop_locked, hard_drop_locked,
	hard_drop_enabled, additive_gravity
)
	self:rotatePiece(inputs, piece, grid, prev_inputs, false)
	self:movePiece(piece, grid, move)
	self:dropPiece(
		inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
		hard_drop_enabled, additive_gravity
	)
	self:lockPiece(piece, grid, lock_delay)
end

function Ruleset:onPieceMove(piece) end
function Ruleset:onPieceRotate(piece) end
function Ruleset:onPieceDrop(piece) end

return Ruleset
