local module = {}

local myMath = require("modules.math")
local files  = require("modules.files")
local logging = require("modules.logging")
local json = require("modules.json")
local noteclass = require("modules.types.note")
local characterclass = require("modules.types.character")

local Vector2 = {}
Vector2.__index = Vector2

local Sprite = {}
Sprite.__index = Sprite

local Sprites = {}
local Rects = {}

function Vector2:Lerp(newVector2, position)
    return module.Vector2(myMath.lerp(self.x, newVector2.x, position), myMath.lerp(self.x, newVector2.x, position))
end

function Vector2:Get()
    return self.x, self.y
end

function module.Vector2(x, y)
    return setmetatable({x = x, y = y}, Vector2)
end

function module.Sprite(image, sheet)
    local sheetString = files.read_file(sheet)


    if not sheetString then
        error("Failed to load", sheet)
    end

    local atlas = json.parse(sheetString).TextureAtlas.SubTexture
    
    local animations = {}

    local quads = {}

    local newSprite = setmetatable({
        animations = animations,
        image = love.graphics.newImage(image),
        quads = quads,
        elapsed = 0,
        fps = 10,
        animation = nil,
        frame = 1,
        position = module.Vector2(200, 100), -- changeable
        looping = false,
        extraOffset = module.Vector2(0,0),
        rect = false
    }, Sprite)

    for index, sprite in next, atlas do
        local x = sprite._x
        local y = sprite._y
        local width = sprite._width
        local height = sprite._height
        local frameX = sprite._frameX
        local frameY = sprite._frameY
        local frameW = sprite._frameWidth
        local frameH = sprite._frameHeight

        local name = sprite._name

        local num = tonumber(string.sub(name, name:len() - 3, name:len()))

        if not quads[name:sub(0, name:len() - 4)] then
            quads[name:sub(0, name:len() - 4)] = {}
        end

        quads[name:sub(0, name:len() - 4)][num] = {quad = love.graphics.newQuad(x, y, width, height, newSprite.image), offset = module.Vector2(frameX or 0, frameY or 0), resize = module.Vector2(.8,1)}
    end

    newSprite.quads = quads

    table.insert(Sprites, newSprite)

    return newSprite
end

function Sprite:PlayAnimation(name, fps, loop)
    self.animation = name
    self.fps = fps
    self.looping = loop
    self.frame = 1
    self.ended = false
end

function Sprite:Destroy()
    if self.rect then
        for index, rect in next, Rects do
            if rect == self then
                table.remove(Rects, index)
            end
        end
        self = nil
    else
        for index, sprite in next, Sprites do
            if sprite == self then
                table.remove(Sprites, index)
            end
        end
        self = nil
    end
end

function module.updateSprites(dt)
    for index, sprite in next, Sprites do
        if not sprite.animation then goto continue end

        sprite.elapsed = sprite.elapsed + dt

        if sprite.elapsed > 1 / sprite.fps then
            sprite.frame = sprite.quads[sprite.animation][sprite.frame + 1] and sprite.frame + 1 or sprite.looping and 1 or sprite.frame
            
            if not sprite.quads[sprite.animation][sprite.frame + 1] and not sprite.looping then
                sprite.ended = true
            end

            sprite.elapsed = 0
        end

        -- sprite.quads[sprite.animation][sprite.frame].resize = module.Vector2(love.graphics.getWidth() / 1920, love.graphics.getHeight() / 1080)

        ::continue:: -- At the end. always.
    end
end

module.draws = {}

function module.drawSprites()
    for index, sprite in next, Sprites do
        if not sprite.animation then goto continue end -- For some reason OG LUA doesnt have continue
        -- So im forced to use goto ::continue:: since it goes straight to the last point of the cycle

        local quad = sprite.quads[sprite.animation][sprite.frame] or sprite.quads[sprite.animation][0]

        love.graphics.draw(sprite.image, quad.quad, (sprite.position.x + (sprite.position.x - quad.offset.x - sprite.extraOffset.x)), (sprite.position.y + (sprite.position.y - quad.offset.y - sprite.extraOffset.y)), 0, quad.resize.x, quad.resize.y)
        ::continue:: -- Point itself
    end

    for index, rect in next, Rects do
        if not rect.animation then goto continue end

        local quad = rect.quads[rect.animation][0]

        love.graphics.draw(rect.image, quad.quad, (rect.position.x + (rect.position.x - quad.offset.x - rect.extraOffset.x)), (rect.position.y + (rect.position.y - quad.offset.y - rect.extraOffset.y)), 0, quad.resize.x, quad.resize.y)
        ::continue::
    end
end

function module.Rect(image, sheet)
    local sheetString = files.read_file(sheet)


    if not sheetString then
        error("Failed to load", sheet)
    end

    local atlas = json.parse(sheetString).TextureAtlas.SubTexture
    
    local animations = {}

    local quads = {}

    local newSprite = setmetatable({
        animations = animations,
        image = love.graphics.newImage(image),
        quads = quads,
        elapsed = 0,
        fps = 10,
        animation = nil,
        frame = 1,
        position = module.Vector2(200, 100), -- changeable
        looping = false,
        extraOffset = module.Vector2(0,0),
        rect = true
    }, Sprite)

    for index, sprite in next, atlas do
        local x = sprite._x
        local y = sprite._y
        local width = sprite._width
        local height = sprite._height
        local frameX = sprite._frameX
        local frameY = sprite._frameY
        local frameW = sprite._frameWidth
        local frameH = sprite._frameHeight

        local name = sprite._name

        local num = tonumber(string.sub(name, name:len() - 3, name:len()))

        if not quads[name:sub(0, name:len() - 4)] then
            quads[name:sub(0, name:len() - 4)] = {}
        end

        quads[name:sub(0, name:len() - 4)][num] = {quad = love.graphics.newQuad(x, y, width, height, newSprite.image), offset = module.Vector2(frameX or 0, frameY or 0), resize = module.Vector2(.8,1)}
    end

    newSprite.quads = quads

    table.insert(Rects, newSprite)

    return newSprite
end

function Sprite:Frame(name, frame)
    self.animation = name
    self.frame = frame
end

noteclass.types = module
module.note = noteclass.note

characterclass.myTypes = module
module.character = characterclass.character

return module