local GameMode = require 'tetris.modes.gamemode'

local HistoryRandomizer = require 'tetris.randomizers.history_6rolls_35bag'

local TheTrueHero = GameMode:extend()

bgm.undyne = love.audio.newSource("res/bgm/mus_x_undyne.mp3", "stream")

sounds.undyne = {
    ding = love.audio.newSource("res/se/000029aa.wav", "static"),
    pike = love.audio.newSource("res/se/0000299c.wav", "static"),
    damage = love.audio.newSource("res/se/000029c3.wav", "static")
}

TheTrueHero.name = "The True Hero"
TheTrueHero.hash = "TheTrueHero"
TheTrueHero.tagline = "A tribute to the Puzzle Maker."

function TheTrueHero:new()
    self.super:new()

    self.randomizer = HistoryRandomizer()

    self.attacks = {
        "PIECE GOAL",
        "LINE GOAL",
        "GARBAGE",
        "HIDDEN PREVIEWS",
        "TO THE BEAT",
        "INVISIBLE"
    }

    self.attack_number = 0
    self.current_attack = 0
    self.var = 0
    
    self.section_frames = 0
    self.section_times = {
        385,
        385,
        385,
        386,
        386,
        385,
        381,
        384,
        384,
        384,
        384,
        385,
        385,
        384,
        384,
        288,
        288,
        288,
        288,
        288,
        288,
        288,
        288,
        288,
        288,
        288,
        290,
    }

    self.queue_age = 0
    self.grounded_time = 0

    self.lock_drop = true
    self.lock_hard_drop = true
    self.enable_hold = true
    self.next_queue_length = 5

    self.irs = false
    self.ihs = false
end

function TheTrueHero:getDropSpeed()
	return 20
end

function TheTrueHero:getARR()
	return config.arr
end

function TheTrueHero:getARE()
	return 0
end

function TheTrueHero:getLineARE()
	return 0
end

function TheTrueHero:getDasLimit()
	return config.das
end

function TheTrueHero:getLineClearDelay()
	return 0
end

function TheTrueHero:getLockDelay()
    return 30
end

function TheTrueHero:getGravity()
    return self.current_attack == 5 and 20 or 1.1 ^ (self.attack_number - 1)
end

function TheTrueHero:getDasCutDelay()
    return config.dcd
end

function TheTrueHero:advanceOneFrame(inputs, ruleset)
    if self.ready_frames == 0 then
        if self.frames == 0 then
            switchBGMLoop("undyne")
        end
        if self.section_frames >= self.section_times[Mod1(self.attack_number, #self.section_times)] or self.frames == 0 then
            if (self.current_attack == 1 or self.current_attack == 2) and self.var > 0 then
                playSE("undyne", "damage")
                self.game_over = true
                return false
            end
            self.section_frames = 0
            self.attack_number = self.attack_number + 1
            local prev_attack = self.current_attack
            self.current_attack = -1
            local attack_rolls = 0
            while ((
                prev_attack == self.current_attack or
                self.current_attack == 5
            ) and (attack_rolls < 2)) or attack_rolls == 0 do
                attack_rolls = attack_rolls + 1
                if self.attack_number > 20 then
                    self.current_attack = math.random(#self.attacks)
                else
                    self.current_attack = math.random(4)
                end
            end
            if self.current_attack == 1 then
                self.var = math.floor(3 + math.random(
                    math.floor(self.attack_number / 4),
                    math.floor(self.attack_number / 3)
                ) *
                self.section_times[Mod1(self.attack_number, #self.section_times)] / 342)
            elseif self.current_attack == 2 then
                self.var = math.floor(1 + math.random(
                    math.floor(self.attack_number / 6),
                    math.floor(self.attack_number / 4)
                ) *
                self.section_times[Mod1(self.attack_number, #self.section_times)] / 342)
            elseif self.current_attack == 3 then
                self.var = math.max(10 - math.random(
                    math.floor(self.attack_number / 8),
                    math.floor(self.attack_number / 4)
                ), 3)
            end
        end
        self.frames = self.frames + 1
        self.section_frames = self.section_frames + 1
        if self.current_attack == 5 then
            self.lock_on_soft_drop = false
            self.lock_on_hard_drop = false
            if self.section_frames % 24 == 0 and self.section_frames >= 25 then
                self.piece.locked = true
                playSE("undyne", "pike")
            end
        else
            self.lock_on_soft_drop = ({ruleset.softdrop_lock, self.instant_soft_drop, false, true })[config.gamesettings.manlock]
            self.lock_on_hard_drop = ({ruleset.harddrop_lock, self.instant_hard_drop, true,  false})[config.gamesettings.manlock]
        end
    end
    return true
end

function TheTrueHero:onPieceEnter()
    self.queue_age = 0
    self.grounded_time = 0
end

function TheTrueHero:onHold()
    self.grounded_time = 0
end

function TheTrueHero:whilePieceActive()
    if self.piece:isDropBlocked(self.grid) then
        self.grounded_time = self.grounded_time + 1
        if self.grounded_time >= 120 then
            self.piece.locked = true
        end
    end
    self.queue_age = self.queue_age + 1
end

function TheTrueHero:onPieceLock(piece, cleared_row_count)
    self.super:onPieceLock()
    if self.current_attack == 1 or self.current_attack == 3 then
        self.var = math.max(self.var - 1, 0)
        self:advanceBottomRow(1)
        playSE("undyne", "ding")
    elseif self.current_attack == 2 then
        self.var = math.max(self.var - cleared_row_count, 0)
        if cleared_row_count ~= 0 then
            playSE("undyne", "ding")
        end
    end
end

function TheTrueHero:advanceBottomRow(dx)
	if self.var <= 0 and self.current_attack == 3 then
        self.grid:copyBottomRow()
        self.var = math.max(10 - math.random(
            math.floor(self.attack_number / 8),
            math.floor(self.attack_number / 4)
        ), 3)
        playSE("undyne", "pike")
	end
end

function TheTrueHero:setNextOpacity(i)
	if self.current_attack == 4 then
		local hidden_next_pieces = math.ceil(self.attack_number / 15)
		if i < hidden_next_pieces then
			love.graphics.setColor(1, 1, 1, 0)
		elseif i == hidden_next_pieces then
			love.graphics.setColor(1, 1, 1, 1 - math.min(1, self.queue_age / 15))
		else
			love.graphics.setColor(1, 1, 1, 1)
		end
	else
		love.graphics.setColor(1, 1, 1, 1)
	end
end

local function invisible(game, block, x, y, age)
    return 0.5, 0.5, 0.5, 1 - age / (300 - game.attack_number * 5.5), 0
end

function TheTrueHero:drawGrid()
    if self.current_attack == 6 then
        self.grid:drawCustom(invisible, self)
    else
        self.grid:draw()
    end
    self:drawGhostPiece()
end

function TheTrueHero:drawScoringInfo()
    self.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("ATTACK NUMBER", text_x, 150, 200, "left")
    love.graphics.printf("ATTACK", text_x, 230, 200, "left")

    love.graphics.setFont(font_3x5_3)
    if self.attack_number > 0 then
        love.graphics.printf(self.attack_number .. " - " .. (
            string.sub(formatTime(
                self.section_times[Mod1(self.attack_number, #self.section_times)] - self.section_frames
            ), -4, -1)
        ), text_x, 170, 200, "left")
    end
    love.graphics.printf(
        self.attacks[self.current_attack] or "",
        text_x, 250, 200, "left"
    )

    love.graphics.setFont(font_3x5_4)
    if self.current_attack >= 1 and self.current_attack <= 3 then
        love.graphics.setColor(
            (not self.game_over and self.frames % 4 < 2) and
            (
                self.var == 0 and {0.3, 1, 0.3, 1} or {1, 0.3, 0.3, 1}
            ) or {1, 1, 1, 1}
        )
        love.graphics.printf(self.var, text_x, 280, 200, "left")
        love.graphics.setColor(1, 1, 1, 1)
    end
end

function TheTrueHero:getHighscoreData()
    return {
        frames = self.frames,
    }
end

function TheTrueHero:getBackground()
    return math.max(0, self.attack_number - 1) % 20
end

return TheTrueHero