local GameMode = require 'tetris.modes.gamemode'

local PhantomManiaNXGame = GameMode:extend()

local History6RollsRandomizer = require 'tetris.randomizers.history_6rolls_35bag'

PhantomManiaNXGame.name = "Phantom Mania NX"
PhantomManiaNXGame.hash = "PhantomManiaNX"
PhantomManiaNXGame.tagline = "The ultimate invisible challenge! Can you survive the brutal gimmicks?"

function PhantomManiaNXGame:new()
	PhantomManiaNXGame.super:new()

	self.grade = 0
	self.garbage = 0
	self.roll_frames = 0
    self.roll_points = 0
	self.combo = 1
    self.cools = 0
    self.last_section_cool = false
    self.tetrises = 0
    self.section_tetris = {}
	self.randomizer = History6RollsRandomizer()
	
	self.SGnames = {
		"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
		"M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9",
		"GM"
	}
	
	self.lock_drop = true
	self.lock_hard_drop = true
	self.enable_hold = true
	self.next_queue_length = 3

	self.coolregret_message = "COOL!!"
	self.coolregret_timer = 0
end

function PhantomManiaNXGame:getARE()
		if self.level < 300 then return 12
	else return 6 end
end

function PhantomManiaNXGame:getLineARE()
		if self.level < 100 then return 8
	elseif self.level < 200 then return 7
	elseif self.level < 500 then return 6
	elseif self.level < 1300 then return 5
	else return 6 end
end

function PhantomManiaNXGame:getDasLimit()
		if self.level < 100 then return 9
	elseif self.level < 500 then return 7
	else return 5 end
end

function PhantomManiaNXGame:getLineClearDelay()
	if self.level < 1300 then return self:getLineARE() - 2
	else return 6 end
end

function PhantomManiaNXGame:getLockDelay()
        if self.level < 200 then return 18
	elseif self.level < 300 then return 17
	elseif self.level < 500 then return 15
	elseif self.level < 600 then return 13
	elseif self.level < 1100 then return 12
	elseif self.level < 1200 then return 10
	elseif self.level < 1300 then return 8
	else return 15 end
end

function PhantomManiaNXGame:getGravity()
	return 20
end

function PhantomManiaNXGame:getGarbageLimit()
        if self.level < 600 then return 20
	elseif self.level < 700 then return 18
	elseif self.level < 800 then return 10
	elseif self.level < 900 then return 9
	else return 8 end
end

function PhantomManiaNXGame:getSkin()
	return self.level >= 1000 and "bone" or "2tie"
end

function PhantomManiaNXGame:hitTorikan(old_level, new_level)
	if old_level < 300 and new_level >= 300 and self.frames > frameTime(1,30) then
		self.level = 300
		return true
	end
    if old_level < 500 and new_level >= 500 and self.frames > frameTime(2,28) then
		self.level = 500
		return true
	end
    if old_level < 800 and new_level >= 800 and self.frames > frameTime(3,38) then
		self.level = 800
		return true
	end
	if old_level < 1000 and new_level >= 1000 and self.frames > frameTime(4,56) then
		self.level = 1000
		return true
	end
	return false
end

function PhantomManiaNXGame:advanceOneFrame()
	if self.clear then
		self.roll_frames = self.roll_frames + 1
		if self.roll_frames < 0 then
			if self.roll_frames + 1 == 0 then
				switchBGM("credit_roll", "gm3")
				return true
			end
			return false
		elseif self.roll_frames > 3238 then
			switchBGM(nil)
			self.completed = true
		end
	elseif self.ready_frames == 0 then
		self.frames = self.frames + 1
	end
	return true
end

function PhantomManiaNXGame:onPieceEnter()
	if (self.level % 100 ~= 99) and not self.clear and self.frames ~= 0 then
		self:updateSectionTimes(self.level, self.level + 1)
        self.level = self.level + 1
	end
end

local cleared_row_levels = {1, 2, 4, 6}
local roll_points = {4, 8, 12, 26}
local mroll_points = {10, 20, 30, 100}

function PhantomManiaNXGame:qualifiesForGMRoll()
    for i = 0, 12 do
        if not self.section_tetris[i] then
            return false
        end
    end
    return self.cools >= 13 and self.tetrises >= 40
end

function PhantomManiaNXGame:onLineClear(cleared_row_count)
	if not self.clear then
		if cleared_row_count >= 4 then
            self.tetrises = self.tetrises + 1
            self.section_tetris[math.floor(self.level / 100)] = true
        end
        local new_level = self.level + cleared_row_levels[cleared_row_count]
		self:updateSectionTimes(self.level, new_level)
		if new_level >= 1300 or self:hitTorikan(self.level, new_level) then
			self.clear = true
			if new_level >= 1300 then
				self.level = 1300
				self.grid:clear()
				self.roll_frames = -150
			else
				self.game_over = true
			end
		else
			self.level = math.min(new_level, 1300)
		end
		self:advanceBottomRow(-cleared_row_count)
    else
        if self:qualifiesForGMRoll() then
            self.roll_points = self.roll_points + mroll_points[cleared_row_count]
        else
            self.roll_points = self.roll_points + roll_points[cleared_row_count]
        end
	end
end

function PhantomManiaNXGame:onPieceLock(piece, cleared_row_count)
	playSE("lock")
	if cleared_row_count == 0 then self:advanceBottomRow(1) end
end

function PhantomManiaNXGame:updateScore(level, drop_bonus, cleared_lines)
	if not self.clear then
		if cleared_lines > 0 then
			self.combo = self.combo + (cleared_lines - 1) * 2
			self.score = self.score + (
				(math.ceil((level + cleared_lines) / 4) + drop_bonus) *
				cleared_lines * self.combo
			)
		else
			self.combo = 1
		end
		self.drop_bonus = 0
	end
end

local cool_cutoffs = {
    [0] = frameTime(0,32), frameTime(0,32), frameTime(0,29), frameTime(0,25), frameTime(0,25),
          frameTime(0,22), frameTime(0,22), frameTime(0,18), frameTime(0,18), frameTime(0,16),
          frameTime(0,16), frameTime(0,14), frameTime(0,14)
}

function PhantomManiaNXGame:updateSectionTimes(old_level, new_level)
    local section_time = self.frames - self.section_start_time
    if math.floor(old_level / 100) < math.floor(new_level / 100) then
		table.insert(self.section_times, section_time)
		self.section_start_time = self.frames
        if section_time >= frameTime(1,00) then
            self.last_section_cool = false
            self.coolregret_message = "REGRET!!"
			self.coolregret_timer = 300
            self.grade = self.grade - 1
        elseif self.last_section_cool then
			self.cools = self.cools + 1
        end
        self.grade = self.grade + 1
	elseif old_level % 100 < 70 and new_level % 100 >= 70 then
        local old_section = math.floor(old_level / 100)
        table.insert(self.secondary_section_times, section_time)
        if  section_time <= cool_cutoffs[old_section] and (
            section_time < (self.secondary_section_times[old_section] + 120) or
			old_section == 0 )
        then
			self.last_section_cool = true
            self.coolregret_message = "COOL!!"
			self.coolregret_timer = 300
		else
			self.last_section_cool = false
		end
    end
end

function PhantomManiaNXGame:advanceBottomRow(dx)
	if self.level >= 500 and self.level < 1000 then
		self.garbage = math.max(self.garbage + dx, 0)
		if self.garbage >= self:getGarbageLimit() then
			self.grid:copyBottomRow()
			self.garbage = 0
		end
	end
end

function PhantomManiaNXGame:canDrawLCA()
    return (
        self.level < 1000 or (
            self.level >= 1300 and
            not self:qualifiesForGMRoll()
        )
    ) and self.lcd > 0
end

PhantomManiaNXGame.rollOpacityFunction = function(age)
	if age > 4 then return 0
	else return 1 - age / 4 end
end

PhantomManiaNXGame.garbageOpacityFunction = function(age)
	return age > 4 and 0 or 1
end

function PhantomManiaNXGame:drawGrid()
    if not (
        self.game_over or self.completed or
        (self.level >= 1300 and not self:qualifiesForGMRoll())
    ) then
        self.grid:drawInvisible(
            self.rollOpacityFunction,
            self.garbageOpacityFunction,
            self.level < 1000
        )
    else
        self.grid:draw()
    end
end

function PhantomManiaNXGame:getBackground()
	return math.floor(self.level / 100)
end

function PhantomManiaNXGame:getHighscoreData()
	return {
		grade = self:getAggregateGrade(),
        level = self.level,
		frames = self.frames,
	}
end

function PhantomManiaNXGame:getAggregateGrade()
    local grade_cap
    if self:qualifiesForGMRoll() then
        if self.roll_frames > 3238 then
            grade_cap = 42
        else
            grade_cap = 41
        end
    else
        grade_cap = 26
    end
    return math.min(
        self.grade + self.cools + math.floor(self.roll_points / 100) + (
            self:qualifiesForGMRoll() and 1 or 0
        ), grade_cap
    )
end

local master_grades = {"M", "MK", "MV", "MO"}

local function getLetterGrade(grade)
    if grade == 0 then
        return "1"
    elseif grade <= 13 then
        return "S" .. tostring(grade)
    elseif grade <= 26 then
        return "M" .. tostring(grade - 13)
    elseif grade <= 30 then
        return master_grades[grade - 26]
    elseif grade <= 41 then
        return "MM-" .. tostring(grade - 30)
    else
        return "GM"
    end
end

function PhantomManiaNXGame:drawScoringInfo()
	PhantomManiaNXGame.super.drawScoringInfo(self)

	love.graphics.setColor(1, 1, 1, 1)

	if config["side_next"] then
        love.graphics.printf("NEXT", 240, 72, 40, "left")
    else
        love.graphics.printf("NEXT", 64, 40, 40, "left")
    end

    local text_x = config["side_next"] and 316 or 240
    
    love.graphics.setFont(font_3x5_2)
	love.graphics.printf("GRADE", text_x, 120, 40, "left")
	love.graphics.printf("SCORE", text_x, 200, 40, "left")
	love.graphics.printf("LEVEL", text_x, 320, 40, "left")
	local sg = self.grid:checkSecretGrade()
	if sg >= 5 then 
		love.graphics.printf("SECRET GRADE", text_x, 430, 180, "left")
	end
	
	if(self.coolregret_timer > 0) then
		love.graphics.printf(self.coolregret_message, 64, 400, 160, "center")
		self.coolregret_timer = self.coolregret_timer - 1
	end

	local current_section = math.floor(self.level / 100) + 1
	self:drawSectionTimesWithSecondary(current_section)

	love.graphics.setFont(font_3x5_3)
	if self.roll_frames > 3238 then love.graphics.setColor(1, 0.5, 0, 1)
	elseif self.level >= 1300 then love.graphics.setColor(0, 1, 0, 1) end
	love.graphics.printf(getLetterGrade(self:getAggregateGrade()), text_x, 140, 90, "left")
	love.graphics.setColor(1, 1, 1, 1)
	love.graphics.printf(self.score, text_x, 220, 90, "left")
	love.graphics.printf(self.level, text_x, 340, 50, "right")
	if self.clear then
		love.graphics.printf(self.level, text_x, 370, 50, "right")
	else
		love.graphics.printf(math.floor(self.level / 100 + 1) * 100, text_x, 370, 50, "right")
	end
	if sg >= 5 then
		love.graphics.printf(self.SGnames[sg], text_x, 450, 180, "left")
	end
end

return PhantomManiaNXGame
