local game = {}

local IMG_PATH = "games/dog-sitting/imgs/"

local _loadImage = loadImage
local function loadImage(file)
    return _loadImage(IMG_PATH .. file)
end

local function loadImages(prefix, from, to)
    if not to then
        to = from
        from = 1
    end
    local images = {}
    for i = from, to do
        table.insert(images, loadImage(("%s-%04d.png"):format(prefix, i)))
    end
    return images
end

local function newPDA(initially)
    local pda = {state = {initially}}
    function pda:push(state)
        table.insert(self.state, state)
    end
    function pda:pop()
        table.remove(self.state)
    end
    function pda:become(state)
        pda:pop()
        pda:push(state)
    end
    function pda:currently()
        return self.state[#self.state]
    end
    function pda:is(state)
        return self:currently() == state
    end
    function pda:any(...)
        for _, state in ipairs{...} do
            if self:is(state) then
                return true
            end
        end
        return false
    end
    return pda
end

function game:setup()
    love.audio.stop()
    size(512, 480)
    self.sprites = 
        { broc = loadImages("broc", 22)
        , brocPiss = loadImages("broc-piss", 10)
        , brocWater = loadImages("broc-water", 10)
        , gren = loadImages("gren", 22)
        , grenPiss = loadImages("gren-piss", 10)
        , grenWater = loadImages("gren-water", 10)
        , doggo = loadImages("doggo", 20)
        , doggoTail = loadImages("doggo-tail", 20)
        , dresser = loadImages("dresser", 2)
        , recording = loadImages("recording", 2)
        , ending = loadImages("ending", 4)
        }

    self.animations = 
        { gren = 
            { idleA = { 1, 2 }
            , idleB = { 3, 4 }
            , idleC = { 5, 6 }
            , idleD = { 7, 8 }
            , undressA = { 9, 10 }
            , undressB = { 11, 12 }
            , undressC = { 13, 14 }
            , pet = { 15, 16, 17, 18, 17, 16, 15 }
            , drinkA = { 19, 20 }
            , drinkB = { 21, 22 }
            }

        , broc =
            { idleA = { 1, 2 }
            , idleB = { 3, 4 }
            , idleC = { 5, 6 }
            , idleD = { 7, 8 }
            , undressA = { 9, 10 }
            , undressB = { 11, 12 }
            , undressC = { 13, 14 }
            , pet = { 15, 16, 17, 18, 17, 16, 15 }
            , drinkA = { 19, 20 }
            , drinkB = { 21, 22 }
            }

        , brocPiss =
            { faceForwards = { 1, 2 }
            , faceBroc = { 3, 4 }
            , faceGren = { 5, 6 }
            , drinkGren = { 7, 8 }
            , drinkBroc = { 9, 10 }
            }

        , grenPiss =
            { faceForwards = { 1, 2 }
            , faceBroc = { 3, 4 }
            , faceGren = { 5, 6 }
            , drinkGren = { 7, 8 }
            , drinkBroc = { 9, 10 }
            }

        , doggo = 
            { idle = { 1, 2 }
            , faceForwards = { 3, 4 }
            , faceBroc = { 5, 6 }
            , faceGren = { 7, 8 }
            , drinkGren = { 9, 10 }
            , drinkBroc = { 11, 12 }
            , brocPet = { 13, 14, 15, 16, 15, 14, 13 }
            , grenPet = { 17, 18, 19, 20, 19, 18, 17 }
            }

        , doggoTail = 
            { faceForwards = { 1, 2, 3, 4, 3, 2 }
            , faceBroc = { 5, 6, 7, 8, 7, 6 }
            , faceGren = { 9, 10, 11, 12, 11, 10 }
            , drinkGren = { 13, 14, 15, 16, 15, 14 }
            , drinkBroc = { 17, 18, 19, 20, 19, 18 }
            , grenPet = { 1, 2, 3, 4, 3, 2 }
            , brocPet = { 1, 2, 3, 4, 3, 2 }
            }

        , grenWater = 
            { { 9, 10 }
            , { 7, 8 }
            , { 5, 6 }
            , { 3, 4 }
            , { 1, 2 }
            }

        , brocWater =
            { { 9, 10 }
            , { 7, 8 }
            , { 5, 6 }
            , { 3, 4 }
            , { 1, 2 }
            }
        , ending = { 1, 2, 3, 4, 3, 2 }
    }
    self.sfx = 
        { panting = love.audio.newSource("sfx/panting.wav", "static")
        , pissing = love.audio.newSource("sfx/pissing.wav", "static")
        , petting = love.audio.newSource("sfx/petting.wav", "static")
        , zipper = love.audio.newSource("sfx/zipper.wav", "static")
        , showering = love.audio.newSource("sfx/showering.wav", "static")
        , liftDress = love.audio.newSource("sfx/lift-dress.wav", "static")
        , drink = love.audio.newSource("sfx/drink.wav", "static")
        }
    self.sfx.panting:setLooping(true)
    self.sfx.showering:setLooping(true)
    self.sfx.petting:setVolume(0.5)
    self.sfx.petting:setLooping(true)
    self.hornyProgress = 
        { "idleA", "idleB", "idleC", "idleD" }

    self.broc = 
        { state = newPDA("idleA")
        , hornyLevel = 1
        , timer = 0
        , frame = 0
        , hornyTimer = 0
        , pissing = false
        , petting = false
        , petTimer = 3
        , pissTimer = 5
        , drinkTimer = 3
        }

    self.brocWater = 
        { state = newPDA(5)
        , timer = 0
        , frame = 0
        }
    
    self.brocPiss = 
        { delegateTo = "doggo"
        , timer = 0
        , frame = 0
        }

    self.gren =
        { state = newPDA("idleA")
        , hornyLevel = 1
        , timer = 0
        , frame = 0
        , hornyTimer = 0
        , pissing = false
        , petting = false
        , petTimer = 3
        , pissTimer = 5
        , drinkTimer = 3
        }
    
    self.grenWater = 
        { state = newPDA(5)
        , timer = 0
        , frame = 0
        }

    self.grenPiss =
        { delegateTo = "doggo"
        , timer = 0
        , frame = 0
        }
    
    self.doggo =
        { state = newPDA("idle")
        , frame = 0
        , timer = 0
        }
    
    self.doggoTail = 
        { delegateTo = "doggo"
        , frame = 0
        , timer = 0
        }

    self.decor = 
        { timer = 0
        , frame = 0
        }
    self.state = "playing"
    self.endingTimer = 3
    self.endingFrame = 0
end

function game:getState(forWho)
    if self[forWho].state then
        return self[forWho].state:currently()
    else 
        return self[self[forWho].delegateTo].state:currently()
    end
end

function game:lookUpFrame(forWho)
    local state = self:getState(forWho)
    return self.sprites[forWho][self.animations[forWho][state][1 + self[forWho].frame]]
end

function game:frameCount(forWho, state)
    return #self.animations[forWho][state]
end

function game:nextFrame(forWho)
    local state = self:getState(forWho) 
    return (self[forWho].frame + 1) % self:frameCount(forWho, state)
end

function game:tickAnimation(forWho)
    local who = self[forWho]
    who.timer = who.timer + deltaTime / 1000
    if who.timer > 1 / 6 then
        who.timer = 0
        who.frame = self:nextFrame(forWho)
    end
end

function game:hoverOnGren()
    return 401 <= mouseX
    and not self.gren.pissing
    and not self.gren.petting
    and not ( 
        self.broc.petting and (
            self.gren.state:any("idleA", "idleB", "idleC")
            or self.gren.state:is "undressC"
        )
    )
    and not ( 
        self.broc.pissing 
        and self.gren.state:any("idleA", "idleB", "idleC")
    )
    and not (self.grenWater.state:currently() == 1 and self.gren.pissTimer == 0)
    and not (self.gren.state:is("undressC") and self.gren.pissTimer == 0)
    and not self.gren.state:any("drinkA", "drinkB")
end

function game:hoverOnBroc()
    return mouseX <= 88
    and not self.broc.pissing
    and not self.broc.petting
    and not (
        self.gren.petting and (
            self.broc.state:any("idleA", "idleB", "idleC")
            or self.broc.state:is("undressC")
        )
    )
    and not (
        self.gren.pissing 
        and self.broc.state:any("idleA", "idleB", "idleC")
    )
    and not (self.brocWater.state:currently() == 1 and self.broc.pissTimer == 0)
    and not (self.broc.state:is("undressC") and self.broc.pissTimer == 0)
    and not self.broc.state:any("drinkA", "drinkB")
end

function game:hoverOnGrensWater()
    return 338 <= mouseX 
    and mouseX <= 338 + 58 
    and 26 <= mouseY 
    and mouseY <= 26 + 158 
    and self.grenWater.state:currently() > 1
    and self.gren.pissTimer == 0
    and not self.gren.pissing
    and not self.gren.petting
    and not self.gren.state:is("drinkB")
end

function game:hoverOnBrocsWater()
    return 115 <= mouseX 
    and mouseX <= 115 + 58 
    and 26 <= mouseY 
    and mouseY <= 26 + 158 
    and self.brocWater.state:currently() > 1
    and self.broc.pissTimer == 0
    and not self.broc.pissing
    and not self.broc.petting
    and not self.broc.state:is("drinkB")
end

function game:hovingOnObject()
    if self:hoverOnGren() or self:hoverOnBroc() or self:hoverOnGrensWater() or self:hoverOnBrocsWater() then
        love.mouse.setCursor(love.mouse.getSystemCursor("hand"))
    else
        love.mouse.setCursor(love.mouse.getSystemCursor("arrow"))
    end
end

function game:tickDecor()
    self.decor.timer = self.decor.timer + deltaTime / 1000
    if self.decor.timer > 1 / 6 then
        self.decor.timer = 0
        self.decor.frame = (self.decor.frame + 1) % 2
    end
end


function game:drawDresser()
    image(self.sprites.dresser[1 + self.decor.frame], 0, 0)
end


function game:drawGrensWater()
    if not self.gren.state:is "drinkB" then
        self:tickAnimation("grenWater")
        image(self:lookUpFrame("grenWater"), 0, 0)
    end
end

function game:drawBrocsWater()
    if not self.broc.state:is "drinkB" then
        self:tickAnimation("brocWater")
        image(self:lookUpFrame("brocWater"), 0, 0)
    end
end

function game:updateGren()
    if self.gren.petting then
        self.gren.state:push("pet")
    end
    self:tickAnimation("gren")
    image(self:lookUpFrame("gren"), 0, 0)
    if self.gren.state:is("pet") then
        -- drain the pet timer then exit the petting state
        self.gren.petTimer = self.gren.petTimer - deltaTime / 1000
        if self.gren.petTimer <= 0 then
            self.gren.petTimer = 2
            self.gren.petting = false
            self.gren.frame = 0
            self.doggo.state:become("idle")
            self.sfx.panting:stop()
            self.sfx.petting:stop()
            self.doggo.frame = 0
        end
        self.gren.state:pop()
    elseif self.gren.pissing then
        self.gren.pissTimer = self.gren.pissTimer - deltaTime / 1000
        if self.gren.pissTimer <= 0 then
            self.gren.pissing = false
            self.gren.pissTimer = 0
        end
    elseif self.gren.state:is "drinkB" then
        self.gren.drinkTimer = self.gren.drinkTimer - deltaTime / 1000
        if self.gren.drinkTimer <= 0 then
            self.gren.drinkTimer = 3
            self.gren.pissTimer = 5
            self.grenWater.state:become(self.grenWater.state:currently() - 1)
            self.gren.state:pop()
        end
    end
    
    if self.gren.state:any("idleA", "idleB", "idleC") then
        -- based on what actions are currently happening, increase the rate 
        -- the horny timer ticks. Default rate is 5s to full, gren petting 
        -- is 3s to full, gren petting is 2s to full, and gren pissing tics
        -- every second.
        local tickFactor = 1 / 10
        
            if self.broc.petting then tickFactor = 1 / 5
        elseif self.gren.petting then tickFactor = 1 / 2
        elseif self.broc.pissing then tickFactor = 1 
        end

        self.gren.hornyTimer = self.gren.hornyTimer + deltaTime / 1000 * tickFactor
        if self.gren.hornyTimer > 1 then
            self.gren.hornyTimer = 0
            self.gren.hornyLevel = self.gren.hornyLevel + 1
            self.gren.state:become(self.hornyProgress[self.gren.hornyLevel])
        end
    end
end

function game:drawGren()
    game:updateGren()
end

function game:updateBroc()
    local returnState = self.broc.state
    if self.broc.petting then
        self.broc.state:push("pet")
    end
    self:tickAnimation("broc")
    image(self:lookUpFrame("broc"), 0, 0)
    if self.broc.state:is("pet") then
        -- drain the pet timer then exit the petting state
        self.broc.petTimer = self.broc.petTimer - deltaTime / 1000
        if self.broc.petTimer <= 0 then
            self.broc.petTimer = 2
            self.broc.petting = false
            self.broc.frame = 0
            self.sfx.panting:stop()
            self.sfx.petting:stop()
            self.doggo.state:become("idle")
            self.doggo.frame = 0
        end
        self.broc.state:pop()
    elseif self.broc.pissing then
        self.broc.pissTimer = self.broc.pissTimer - deltaTime / 1000
        if self.broc.pissTimer <= 0 then
            self.broc.pissing = false
            self.broc.pissTimer = 0
        end
    elseif self.broc.state:is "drinkB" then
        self.broc.drinkTimer = self.broc.drinkTimer - deltaTime / 1000
        if self.broc.drinkTimer <= 0 then
            self.broc.drinkTimer = 3
            self.broc.pissTimer = 5
            self.brocWater.state:become(self.brocWater.state:currently() - 1)
            self.broc.state:pop()
        end
    end
    
    if self.broc.state:any("idleA", "idleB", "idleC") then
        -- based on what actions are currently happening, increase the rate 
        -- the horny timer ticks. Default rate is 5s to full, gren petting 
        -- is 3s to full, broc petting is 2s to full, and gren pissing tics
        -- every second.
        local tickFactor = 1 / 5
        
            if self.gren.petting then tickFactor = 1 / 3
        elseif self.broc.petting then tickFactor = 1 / 2
        elseif self.gren.pissing then tickFactor = 1
        end

        self.broc.hornyTimer = self.broc.hornyTimer + deltaTime / 1000 * tickFactor
        if self.broc.hornyTimer > 1 then
            self.broc.hornyTimer = 0
            self.broc.hornyLevel = self.broc.hornyLevel + 1
            self.broc.state:become(self.hornyProgress[self.broc.hornyLevel])
        end
    end
end

function game:drawBroc()
    game:updateBroc()
end 

function game:drawDoggoTail()
    if not self.doggo.state:is("idle") then
        self:tickAnimation("doggoTail")
        image(self:lookUpFrame("doggoTail"), 0, 0)
    end
end 

function game:doggoChoosePissState()
    if not self.doggo.state:any("faceForwards", "faceBroc", "faceGren", "drinkGren", "drinkBroc") then
        local canBe = {"faceForwards"}
        if self.broc.pissing then
            table.insert(canBe, "faceBroc")
            table.insert(canBe, "drinkBroc")
        end
        if self.gren.pissing then
            table.insert(canBe, "faceGren")
            table.insert(canBe, "drinkGren")
        end
        self.doggo.state:push(random(canBe))
        self.sfx.panting:play()
    elseif self.doggo.state:is("drinkBroc") and not self.broc.pissing then
        self.doggo.state:become("drinkGren")
    elseif self.doggo.state:is("drinkGren") and not self.gren.pissing then
        self.doggo.state:become("drinkBroc")
    end
end

function game:drawDoggo()
    -- TODO: account for when the dog is getting pissed on
    if self.gren.pissing or self.broc.pissing then
        self:doggoChoosePissState()
    elseif self.doggo.state:any("faceForwards", "faceBroc", "faceGren", "drinkGren", "drinkBroc") then
        self.doggo.state:pop()
        self.sfx.panting:stop()
    end

    self:tickAnimation("doggo")
    image(self:lookUpFrame("doggo"), 0, 0)
end


function game:drawGrenPiss()
    if self.gren.pissing then
        self:tickAnimation("grenPiss")
        image(self:lookUpFrame("grenPiss"), 0, 0)
    end
end

function game:drawBrocPiss()
    if self.broc.pissing then
        self:tickAnimation("brocPiss")
        image(self:lookUpFrame("brocPiss"), 0, 0)
    end
end

function game:drawRecording()
    image(self.sprites.recording[1 + self.decor.frame], 0, 0)
end

function game:isOver()
    if self.brocWater.state:is(1) and self.grenWater.state:is(1) and self.gren.pissTimer == 0 and self.broc.pissTimer == 0 then
        self.endingTimer = self.endingTimer - deltaTime / 1000
        if self.endingTimer <= 0 then
            self.state = "ending"
            self.sfx.showering:play()
        end
    end
end

function game:drawShowering()
    self.endingTimer = self.endingTimer + deltaTime / 1000
    if self.endingTimer > 1 / 6 then
        self.endingTimer = 0
        self.endingFrame = (self.endingFrame + 1) % 6
    end
    
    image(self.sprites.ending[self.animations.ending[1 + self.endingFrame]], 0, 0)
end

function game:draw()
    background(246, 202, 159)
    if self.state == "playing" then
        game:tickDecor()
        game:drawDresser()
        game:hovingOnObject()
        game:drawDresser()
        game:drawGrensWater()
        game:drawBrocsWater()
        game:drawGren()
        game:drawBroc()
        game:drawDoggoTail()
        game:drawDoggo()
        game:drawGrenPiss()
        game:drawBrocPiss()
        game:drawRecording()
        game:isOver()
    else
        game:tickDecor()
        game:drawShowering()
    end
end

function game:mousePressed()
    if self:hoverOnGren() then
        if self.gren.state:any("idleA", "idleB", "idleC") then
            print(self.gren.state:currently())
            self.gren.petting = true
            self.sfx.panting:play()
            self.sfx.petting:play()
            self.doggo.state:push("grenPet")
            self.doggo.frame = 0
            self.gren.frame = 0
        elseif self.gren.state:is("idleD") then
            self.gren.state:push "undressA"
            local dressSound = self.sfx.liftDress:clone()
            dressSound:setPosition(1, 0, 0)
            dressSound:play()
        elseif self.gren.state:is("undressA") then   
            self.gren.state:push "undressB"
            local dressSound = self.sfx.liftDress:clone()
            dressSound:setPosition(1, 0, 0)
            dressSound:play()
        elseif self.gren.state:is("undressB") then
            self.gren.state:push "undressC"
            local dressSound = self.sfx.liftDress:clone()
            dressSound:setPosition(1, 0, 0)
            dressSound:play()
        elseif self.gren.state:is("undressC") and self.gren.pissTimer >= 0 and not self.gren.pissing then
            self.gren.pissing = true
            local piss = self.sfx.pissing:clone()
            piss:setPosition(0.1, 0, 0)
            piss:play()
        end
    elseif self:hoverOnBroc() then
        if self.broc.state:any("idleA", "idleB", "idleC") then
            self.broc.petting = true
            self.sfx.panting:play()
            self.sfx.petting:play()
            self.doggo.state:become("brocPet")
            self.doggo.frame = 0
            self.broc.frame = 0
        elseif self.broc.state:is("idleD") then
            self.broc.state:become("undressA")
        elseif self.broc.state:is("undressA") then   
            self.broc.state:become("undressB")
            self.sfx.zipper:setPosition(-1, 0, 0)
            self.sfx.zipper:play()
        elseif self.broc.state:is("undressB") then
            self.broc.state:become("undressC")
            local dressSound = self.sfx.liftDress:clone()
            dressSound:setPosition(-1, 0, 0)
            dressSound:play()
        elseif self.broc.state:is("undressC") and self.broc.pissTimer >= 0 and not self.broc.pissing then
            self.broc.pissing = true
            local piss = self.sfx.pissing:clone()
            piss:setPosition(-0.1, 0, 0)
            piss:play()
        end
    elseif self:hoverOnBrocsWater() then
        if self.broc.state:is("drinkA") then
            self.broc.state:become("drinkB")
            local drinkSound = self.sfx.drink:clone()
            drinkSound:setPosition(-1, 0, 0)
            drinkSound:play()
        else
            self.broc.state:push("drinkA")
        end
    elseif self:hoverOnGrensWater() then
        if self.gren.state:is("drinkA") then
            self.gren.state:become("drinkB")
            local drinkSound = self.sfx.drink:clone()
            drinkSound:setPosition(1, 0, 0)
            drinkSound:play()
        else
            self.gren.state:push("drinkA")
        end
    end
end

return {
      name = "Dog Sitting"
    , themes = {"Watersports", "Feral", "Anthro x Feral", "Technically incest because Broc and Gren are siblings, but they don't fuck each other."}
    , about = "Gren [they] and Broc [he] are dog sitting once again. Today, they've got their camera set up to record their favorite piss pup getting wet."
    , game = game
    , id = 4
}
