local ____exports = {}
local ____constants = require("core.constants")
local EMPTY_PNG_PATH = ____constants.EMPTY_PNG_PATH
local VectorZero = ____constants.VectorZero
local ____color = require("functions.color")
local copyColor = ____color.copyColor
local ____kColor = require("functions.kColor")
local kColorEquals = ____kColor.kColorEquals
local ____utils = require("functions.utils")
local eRange = ____utils.eRange
--- Helper function to check if two texels on a sprite are equivalent to each other.
function ____exports.texelEquals(self, sprite1, sprite2, position, layerID)
    local kColor1 = sprite1:GetTexel(position, VectorZero, 1, layerID)
    local kColor2 = sprite2:GetTexel(position, VectorZero, 1, layerID)
    return kColorEquals(nil, kColor1, kColor2)
end
--- Helper function to clear all layers or specific layers from a sprite without unloading the
-- attached anm2 file.
-- 
-- This function is variadic, which means you can pass as many layer IDs as you want to clear. If no
-- specific layers are passed, the function will clear every layer.
-- 
-- If you want to clear all of the layers of a sprite and don't care about unloading the attached
-- anm2 file, then use the `Sprite.Reset` method instead.
-- 
-- Since there is no official API method to clear specific layers from a sprite, we work around it
-- by setting the spritesheet to a transparent PNG file corresponding to the `EMPTY_PNG_PATH`
-- constant.
-- 
-- This function will still work identically if PNG file does not exist, but it will cause a
-- spurious error to appear in the "log.txt" file. If silencing these errors is desired, you can
-- create a transparent 1 pixel PNG file in your mod's resources folder at `EMPTY_PNG_PATH`.
-- 
-- @allowEmptyVariadic
function ____exports.clearSprite(self, sprite, ...)
    local layerIDs = {...}
    if #layerIDs == 0 then
        local numLayers = sprite:GetLayerCount()
        layerIDs = eRange(nil, numLayers)
    end
    for ____, layerID in ipairs(layerIDs) do
        sprite:ReplaceSpritesheet(layerID, EMPTY_PNG_PATH)
    end
    sprite:LoadGraphics()
end
--- Helper function that returns the number of the final frame in a particular animation for a
-- sprite. By default, it will use the currently playing animation, but you can also specify a
-- specific animation to check.
-- 
-- Note that this function is bugged with the Stop Watch or the Broken Watch, since using the
-- `Sprite.SetFrame` method will reset the internal accumulator used to slow down the playback speed
-- of the animation. (The `PlaybackSpeed` field of the sprite is not used.) Thus, it is only safe to
-- use this function on animations that are not slowed down by Stop Watch or Broken Watch, such as
-- player animations.
function ____exports.getLastFrameOfAnimation(self, sprite, animation)
    local currentAnimation = sprite:GetAnimation()
    local currentFrame = sprite:GetFrame()
    if animation ~= nil and animation ~= currentAnimation then
        sprite:SetAnimation(animation)
    end
    sprite:SetLastFrame()
    local finalFrame = sprite:GetFrame()
    if animation ~= nil and animation ~= currentAnimation then
        sprite:Play(currentAnimation, true)
    end
    sprite:SetFrame(currentFrame)
    return finalFrame
end
--- Helper function to load a new sprite and play its default animation.
-- 
-- @param anm2Path The path to the "anm2" file that should be loaded.
-- @param pngPath Optional. The path to a custom PNG file that should be loaded on layer 0 of the
-- sprite.
function ____exports.newSprite(self, anm2Path, pngPath)
    local sprite = Sprite()
    if pngPath == nil then
        sprite:Load(anm2Path, true)
    else
        sprite:Load(anm2Path, false)
        sprite:ReplaceSpritesheet(0, pngPath)
        sprite:LoadGraphics()
    end
    local defaultAnimation = sprite:GetDefaultAnimation()
    sprite:Play(defaultAnimation, true)
    return sprite
end
--- Helper function to keep a sprite's color the same values as it already is but set the opacity to
-- a specific value.
-- 
-- @param sprite The sprite to set.
-- @param alpha A value between 0 and 1 that represents the fade amount.
function ____exports.setSpriteOpacity(self, sprite, alpha)
    local fadedColor = copyColor(nil, sprite.Color)
    fadedColor.A = alpha
    sprite.Color = fadedColor
end
--- Helper function to check if two sprite layers have the same sprite sheet by using the
-- `Sprite.GetTexel` method.
-- 
-- Since checking every single texel in the entire sprite is very expensive, this function requires
-- that you provide a range of specific texels to check.
function ____exports.spriteEquals(self, sprite1, sprite2, layerID, xStart, xFinish, xIncrement, yStart, yFinish, yIncrement)
    do
        local x = xStart
        while x <= xFinish do
            do
                local y = yStart
                while y <= yFinish do
                    local position = Vector(x, y)
                    if not ____exports.texelEquals(
                        nil,
                        sprite1,
                        sprite2,
                        position,
                        layerID
                    ) then
                        return false
                    end
                    y = y + yIncrement
                end
            end
            x = x + xIncrement
        end
    end
    return true
end
return ____exports
