local L = WeakAuras.L
---@type string
local AddonName = ...
---@class OptionsPrivate
local OptionsPrivate = select(2, ...)

local texture_types = WeakAuras.StopMotion.texture_types;
local texture_data = WeakAuras.StopMotion.texture_data;
local animation_types = WeakAuras.StopMotion.animation_types;

-- Returns value only for Blizzard flipbooks
function OptionsPrivate.GetFlipbookTileSize(name)
  if texture_data[name] then
    if texture_data[name].isBlizzardFlipbook then
      if texture_data[name].tileWidth and texture_data[name].tileHeight then
        return {["tileWidth"] = texture_data[name].tileWidth, ["tileHeight"] = texture_data[name].tileHeight}
      end
    end
  end
end

local function createOptions(id, data)
    local textureNameHasData = OptionsPrivate.Private.StopMotionBase.textureNameHasData
    local setTextureFunc = OptionsPrivate.Private.StopMotionBase.setTextureFunc
    local options = {
        __title = L["Stop Motion Settings"],
        __order = 1,
        foregroundTexture = {
            type = "input",
            width = WeakAuras.doubleWidth - 0.15,
            name = L["Texture"],
            order = 1,
        },
        chooseForegroundTexture = {
            type = "execute",
            width = 0.15,
            name = L["Choose"],
            order = 2,
            func = function()
                local path = {}
                local paths = {}
                for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
                  paths[child.id] = path
                end
                OptionsPrivate.OpenTexturePicker(data, paths, {
                  texture = "foregroundTexture",
                  color = "foregroundColor",
                  mirror = "mirror",
                  blendMode = "blendMode"
                }, texture_types, setTextureFunc, true);
            end,
            imageWidth = 24,
            imageHeight = 24,
            control = "WeakAurasIcon",
            image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse",
        },
        foregroundColor = {
          type = "color",
          width = WeakAuras.normalWidth,
          name = L["Color"],
          hasAlpha = true,
          order = 3
        },
        desaturateForeground = {
          type = "toggle",
          width = WeakAuras.normalWidth,
          name = L["Desaturate"],
          order = 3.5,
        },
        customForegroundRows = {
            type = "input",
            width = WeakAuras.doubleWidth / 3,
            name = L["Rows"],
            validate = WeakAuras.ValidateNumeric,
            get = function()
              return data.customForegroundRows and tostring(data.customForegroundRows) or "";
            end,
            set = function(info, v)
              data.customForegroundRows = v and tonumber(v) or 0
              WeakAuras.Add(data);
              WeakAuras.UpdateThumbnail(data);
            end,
            order = 4,
            hidden = function()
              return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture)
            end
        },
        customForegroundColumns = {
            type = "input",
            width = WeakAuras.doubleWidth / 3,
            name = L["Columns"],
            validate = WeakAuras.ValidateNumeric,
            get = function()
              return data.customForegroundColumns and tostring(data.customForegroundColumns) or "";
            end,
            set = function(info, v)
              data.customForegroundColumns = v and tonumber(v) or 0
              WeakAuras.Add(data);
              WeakAuras.UpdateThumbnail(data);
            end,
            order = 5,
            hidden = function()
              return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture)
            end
        },
        customForegroundFrames = {
            type = "input",
            width = WeakAuras.doubleWidth / 3,
            name = L["Frame Count"],
            validate = WeakAuras.ValidateNumeric,
            get = function()
              return data.customForegroundFrames and tostring(data.customForegroundFrames) or "";
            end,
            set = function(info, v)
              data.customForegroundFrames = v and tonumber(v) or 0
              WeakAuras.Add(data);
              WeakAuras.UpdateThumbnail(data);
            end,
            order = 6,
            hidden = function()
              return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture)
            end
        },
        customForegroundFileWidth = {
          type = "input",
          width = WeakAuras.normalWidth / 2,
          name = L["File Width"],
          desc = L["Must be a power of 2"],
          validate = function(info, val)
            if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then
              return false;
            end
            return true
          end,
          get = function()
            return data.customForegroundFileWidth and tostring(data.customForegroundFileWidth) or "";
          end,
          set = function(info, v)
            data.customForegroundFileWidth = v and tonumber(v) or 0
            WeakAuras.Add(data);
            WeakAuras.UpdateThumbnail(data);
          end,
          order = 7,
          hidden = function()
            return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture)
          end
        },
        customForegroundFileHeight = {
          type = "input",
          width = WeakAuras.normalWidth / 2,
          name = L["File Height"],
          desc = L["Must be a power of 2"],
          validate = function(info, val)
            if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then
              return false;
            end
            return true
          end,
          get = function()
            return data.customForegroundFileHeight and tostring(data.customForegroundFileHeight) or "";
          end,
          set = function(info, v)
            data.customForegroundFileHeight = v and tonumber(v) or 0
            WeakAuras.Add(data);
            WeakAuras.UpdateThumbnail(data);
          end,
          order = 8,
          hidden = function()
            return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture)
          end
        },
        customForegroundFrameWidth = {
          type = "input",
          width = WeakAuras.normalWidth / 2,
          name = L["Frame Width"],
          validate = WeakAuras.ValidateNumeric,
          desc = L["Can set to 0 if Columns * Width equal File Width"],
          get = function()
            return data.customForegroundFrameWidth and tostring(data.customForegroundFrameWidth) or "";
          end,
          set = function(info, v)
            data.customForegroundFrameWidth = v and tonumber(v) or 0
            WeakAuras.Add(data);
            WeakAuras.UpdateThumbnail(data);
          end,
          order = 9,
          hidden = function()
            return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture)
          end
        },
        customForegroundFrameHeight = {
          type = "input",
          width = WeakAuras.normalWidth / 2,
          name = L["Frame Height"],
          validate = WeakAuras.ValidateNumeric,
          desc = L["Can set to 0 if Rows * Height equal File Height"],
          get = function()
            return data.customForegroundFrameHeight and tostring(data.customForegroundFrameHeight) or "";
          end,
          set = function(info, v)
            data.customForegroundFrameHeight = v and tonumber(v) or 0
            WeakAuras.Add(data);
            WeakAuras.UpdateThumbnail(data);
          end,
          order = 10,
          hidden = function()
            return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture)
          end
        },
        blendMode = {
            type = "select",
            width = WeakAuras.normalWidth,
            name = L["Blend Mode"],
            order = 11,
            values = OptionsPrivate.Private.blend_types
        },
        animationType = {
            type = "select",
            width = WeakAuras.normalWidth,
            name = L["Animation Mode"],
            order = 12,
            values = animation_types
        },
        startPercent = {
            type = "range",
            control = "WeakAurasSpinBox",
            width = WeakAuras.normalWidth,
            name = L["Animation Start"],
            min = 0,
            max = 1,
            bigStep = 0.01,
            order = 13,
            isPercent = true
        },
        endPercent = {
            type = "range",
            control = "WeakAurasSpinBox",
            width = WeakAuras.normalWidth,
            name = L["Animation End"],
            min = 0,
            max = 1,
            bigStep  = 0.01,
            order = 14,
            isPercent = true
        },
        frameRate = {
           type = "range",
           control = "WeakAurasSpinBox",
           width = WeakAuras.normalWidth,
           name = L["Frame Rate"],
           min = 3,
           max = 120,
           step = 1,
           bigStep = 3,
           order = 15,
           disabled = function() return data.animationType == "progress" end;
        },
        inverse = {
          type = "toggle",
          width = WeakAuras.normalWidth,
          name = L["Inverse"],
          order = 15.5
        },
        customBackgroundHeader = {
          type = "header",
          name = L["Background Texture"],
          order = 16,
        },
        hideBackground = {
          type = "toggle",
          name = L["Hide Background"],
          order = 17,
          width = WeakAuras.normalWidth,
        },
        sameTexture = {
          type = "toggle",
          width = WeakAuras.normalWidth,
          name = L["Same texture as Foreground"],
          order = 18,
          disabled = function() return data.hideBackground; end,
          hidden = function() return data.hideBackground; end
        },
        backgroundTexture = {
            type = "input",
            width = WeakAuras.doubleWidth - 0.15,
            name = L["Background Texture"],
            order = 19,
            disabled = function() return data.sameTexture or data.hideBackground end,
            hidden = function() return data.hideBackground end,
            get = function() return data.sameTexture and data.foregroundTexture or data.backgroundTexture; end,
        },
        chooseBackgroundTexture = {
            type = "execute",
            width = 0.15,
            name = L["Choose"],
            order = 20,
            func = function()
              local path = {}
              local paths = {}
              for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
                paths[child.id] = path
              end
              OptionsPrivate.OpenTexturePicker(data, paths, {
                texture = "backgroundTexture",
                color = "backgroundColor",
                mirror = "mirror",
                blendMode = "blendMode"
              }, texture_types, setTextureFunc, true);
            end,
            disabled = function() return data.sameTexture or data.hideBackground; end,
            hidden = function() return data.hideBackground end,
            imageWidth = 24,
            imageHeight = 24,
            control = "WeakAurasIcon",
            image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse",
        },
        backgroundColor = {
            type = "color",
            width = WeakAuras.normalWidth,
            name = L["Color"],
            hasAlpha = true,
            order = 21,
            disabled = function() return data.hideBackground; end,
            hidden = function() return data.hideBackground; end
        },
        desaturateBackground = {
          type = "toggle",
          name = L["Desaturate"],
          order = 22,
          width = WeakAuras.normalWidth,
          disabled = function() return data.hideBackground; end,
          hidden = function() return data.hideBackground; end
      },
        backgroundColorHiddenSpacer = {
          type = "execute",
          width = WeakAuras.normalWidth,
          name = "",
          order = 23,
          image = function() return "", 0, 0 end,
          hidden = function() return not data.hideBackground end
        },
        customBackgroundRows = {
          type = "input",
          width = WeakAuras.doubleWidth / 3,
          name = L["Rows"],
          validate = WeakAuras.ValidateNumeric,
          get = function()
            return data.customBackgroundRows and tostring(data.customBackgroundRows) or "";
          end,
          set = function(info, v)
            data.customBackgroundRows = v and tonumber(v) or 0
            WeakAuras.Add(data);
            WeakAuras.UpdateThumbnail(data);
          end,
          order = 24,
          hidden = function()
            return data.sameTexture
                   or texture_data[data.backgroundTexture]
                   or textureNameHasData(data.backgroundTexture)
          end
      },
      customBackgroundColumns = {
          type = "input",
          width = WeakAuras.doubleWidth / 3,
          name = L["Columns"],
          validate = WeakAuras.ValidateNumeric,
          get = function()
            return data.customBackgroundColumns and tostring(data.customBackgroundColumns) or "";
          end,
          set = function(info, v)
            data.customBackgroundColumns = v and tonumber(v) or 0
            WeakAuras.Add(data);
            WeakAuras.UpdateThumbnail(data);
          end,
          order = 25,
          hidden = function()
            return data.sameTexture
                   or texture_data[data.backgroundTexture]
                   or textureNameHasData(data.backgroundTexture)
          end
      },
      customBackgroundFrames = {
          type = "input",
          width = WeakAuras.doubleWidth / 3,
          name = L["Frame Count"],
          validate = WeakAuras.ValidateNumeric,
          get = function()
            return data.customBackgroundFrames and tostring(data.customBackgroundFrames) or "";
          end,
          set = function(info, v)
            data.customBackgroundFrames = v and tonumber(v) or 0
            WeakAuras.Add(data);
            WeakAuras.UpdateThumbnail(data);
          end,
          order = 26,
          hidden = function()
            return data.sameTexture
                   or texture_data[data.backgroundTexture]
                   or textureNameHasData(data.backgroundTexture)
          end
      },
      customBackgroundFileWidth = {
        type = "input",
        width = WeakAuras.normalWidth / 2,
        name = L["File Width"],
        desc = L["Must be a power of 2"],
        validate = function(info, val)
          if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then
            return false;
          end
          return true
        end,
        get = function()
          return data.customBackgroundFileWidth and tostring(data.customBackgroundFileWidth) or "";
        end,
        set = function(info, v)
          data.customBackgroundFileWidth = v and tonumber(v) or 0
          WeakAuras.Add(data);
          WeakAuras.UpdateThumbnail(data);
        end,
        order = 27,
        hidden = function()
          return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture)
        end
      },
      customBackgroundFileHeight = {
        type = "input",
        width = WeakAuras.normalWidth / 2,
        name = L["File Height"],
        desc = L["Must be a power of 2"],
        validate = function(info, val)
          if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then
            return false;
          end
          return true
        end,
        get = function()
          return data.customBackgroundFileHeight and tostring(data.customBackgroundFileHeight) or "";
        end,
        set = function(info, v)
          data.customBackgroundFileHeight = v and tonumber(v) or 0
          WeakAuras.Add(data);
          WeakAuras.UpdateThumbnail(data);
        end,
        order = 28,
        hidden = function()
          return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture)
        end
      },
      customBackgroundFrameWidth = {
        type = "input",
        width = WeakAuras.normalWidth / 2,
        name = L["Frame Width"],
        validate = WeakAuras.ValidateNumeric,
        desc = L["Can set to 0 if Columns * Width equal File Width"],
        get = function()
          return data.customBackgroundFrameWidth and tostring(data.customBackgroundFrameWidth) or "";
        end,
        set = function(info, v)
          data.customBackgroundFrameWidth = v and tonumber(v) or 0
          WeakAuras.Add(data);
          WeakAuras.UpdateThumbnail(data);
        end,
        order = 29,
        hidden = function()
          return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture)
        end
      },
      customBackgroundFrameHeight = {
        type = "input",
        width = WeakAuras.normalWidth / 2,
        name = L["Frame Height"],
        validate = WeakAuras.ValidateNumeric,
        desc = L["Can set to 0 if Rows * Height equal File Height"],
        get = function()
          return data.customBackgroundFrameHeight and tostring(data.customBackgroundFrameHeight) or "";
        end,
        set = function(info, v)
          data.customBackgroundFrameHeight = v and tonumber(v) or 0
          WeakAuras.Add(data);
          WeakAuras.UpdateThumbnail(data);
        end,
        order = 30,
        hidden = function()
          return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture)
        end
      },
      backgroundPercent = {
        type = "range",
        control = "WeakAurasSpinBox",
        width = WeakAuras.normalWidth,
        name = L["Selected Frame"],
        min = 0,
        max = 1,
        order = 31,
        isPercent = true,
        hidden = function() return data.hideBackground; end
      }
    };

    return {
      stopmotion = options,
      progressOptions = OptionsPrivate.commonOptions.ProgressOptions(data),
      position = OptionsPrivate.commonOptions.PositionOptions(id, data, 2),
    }
end

local function createThumbnail()
    local borderframe = CreateFrame("Frame", nil, UIParent);
    borderframe:SetWidth(32);
    borderframe:SetHeight(32);

    local border = borderframe:CreateTexture(nil, "OVERLAY");
    border:SetAllPoints(borderframe);
    border:SetTexture("Interface\\BUTTONS\\UI-Quickslot2.blp");
    border:SetTexCoord(0.2, 0.8, 0.2, 0.8);

    local texture = borderframe:CreateTexture();
    borderframe.texture = texture;
    texture:SetPoint("CENTER", borderframe, "CENTER");

    return borderframe;
end

local function modifyThumbnail(parent, region, data, fullModify, size)
    region:SetParent(parent)

    size = size or 30;
    local scale;
    if(data.height > data.width) then
        scale = size/data.height;
        region.texture:SetWidth(scale * data.width);
        region.texture:SetHeight(size);
    else
        scale = size/data.width;
        region.texture:SetWidth(size);
        region.texture:SetHeight(scale * data.height);
    end

    local frame = 1;
    region.foreground = region.foreground or {}
    local tdata = texture_data[data.foregroundTexture];
    if (tdata) then
      local lastFrame = tdata.count - 1;
      region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1;
      region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1;
      region.foreground.rows = tdata.rows;
      region.foreground.columns = tdata.columns;
      region.foreground.fileWidth = 0
      region.foreground.fileHeight = 0
      region.foreground.frameWidth = 0
      region.foreground.frameHeight = 0
    else
      local pattern = "%.x(%d+)y(%d+)f(%d+)%.[tb][gl][ap]"
      local pattern2 = "%.x(%d+)y(%d+)f(%d+)w(%d+)h(%d+)W(%d+)H(%d+)%.[tb][gl][ap]"
      local rows, columns, frames = data.foregroundTexture:lower():match(pattern)
      if rows then
        local lastFrame = frames - 1;
        region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1;
        region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1;
        region.foreground.rows = tonumber(rows);
        region.foreground.columns = tonumber(columns);
        region.foreground.fileWidth = 0
        region.foreground.fileHeight = 0
        region.foreground.frameWidth = 0
        region.foreground.frameHeight = 0
      else
        local rows, columns, frames, frameWidth, frameHeight, fileWidth, fileHeight
              = data.foregroundTexture:match(pattern2)
        if rows then
          local lastFrame = frames - 1;
          region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1;
          region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1;
          region.foreground.rows = tonumber(rows)
          region.foreground.columns = tonumber(columns)
          region.foreground.fileWidth = tonumber(fileWidth)
          region.foreground.fileHeight = tonumber(fileHeight)
          region.foreground.frameWidth = tonumber(frameWidth)
          region.foreground.frameHeight = tonumber(frameHeight)
        else
          local lastFrame = data.customForegroundFrames - 1;
          region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1;
          region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1;
          region.foreground.rows = data.customForegroundRows;
          region.foreground.columns = data.customForegroundColumns;
          region.foreground.fileWidth = data.customForegroundFileWidth
          region.foreground.fileHeight = data.customForegroundFileHeight
          region.foreground.frameWidth = data.customForegroundFrameWidth
          region.foreground.frameHeight = data.customForegroundFrameHeight
        end
      end
    end

    if (region.startFrame and region.endFrame) then
      frame = floor(region.startFrame + (region.endFrame - region.startFrame) * 0.75);
    end

    local texture = data.foregroundTexture or "Interface\\AddOns\\WeakAuras\\Media\\Textures\\stopmotion";

    if (region.foreground.rows and region.foreground.columns) then
      OptionsPrivate.Private.SetTextureOrAtlas(region.texture, texture)
      local frameScaleW, frameScaleH = 1, 1
      if region.foreground.fileWidth and region.foreground.frameWidth
        and region.foreground.fileWidth > 0 and region.foreground.frameWidth > 0
      then
        frameScaleW = (region.foreground.frameWidth * region.foreground.columns) / region.foreground.fileWidth
      end

      if region.foreground.fileHeight and region.foreground.frameHeight
         and region.foreground.fileHeight > 0 and region.foreground.frameHeight > 0
      then
        frameScaleH = (region.foreground.frameHeight * region.foreground.rows) / region.foreground.fileHeight
      end

      WeakAuras.setTile(region.texture, frame, region.foreground.rows, region.foreground.columns, frameScaleW, frameScaleH);

      region.SetValue = function(self, percent)
        local frame = floor(percent * (region.endFrame - region.startFrame) + region.startFrame);
        WeakAuras.setTile(self.texture, frame, region.foreground.rows, region.foreground.columns, frameScaleW, frameScaleH);
      end
    else
      region.texture:SetTexture(texture .. format("%03d", frame));
      region.texture:SetTexCoord(0, 1, 0, 1);

      region.SetValue = function(self, percent)
        local frame = floor(percent * (region.endFrame - region.startFrame) + region.startFrame);
        self.texture:SetTexture((data.foregroundTexture) .. format("%03d", frame));
      end
    end

    region.texture:SetVertexColor(data.foregroundColor[1], data.foregroundColor[2],
                                  data.foregroundColor[3], data.foregroundColor[4])
    region.texture:SetBlendMode(data.blendMode);

    region.elapsed = 0;
    region:SetScript("OnUpdate", function(self, elapsed)
        region.elapsed = region.elapsed + elapsed;
        if(region.elapsed > 4) then
            region.elapsed = region.elapsed - 4;
        end
        region:SetValue(region.elapsed / 4);
    end);
end

local function createIcon()
    local data = {
        height = 30,
        width = 30,
        foregroundTexture = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\stopmotion",
        foregroundColor = {1, 1, 1, 1},
        blendMode = "ADD",
        rotate = false,
        rotation = 0,
        startPercent = 0,
        endPercent = 1,
        backgroundPercent = 1,
        animationType = "progress"
    };

    local thumbnail = createThumbnail();
    modifyThumbnail(UIParent, thumbnail, data, nil, 75);

    thumbnail.elapsed = 0;
    thumbnail:SetScript("OnUpdate", function(self, elapsed)
        thumbnail.elapsed = thumbnail.elapsed + elapsed;
        if(thumbnail.elapsed > 2) then
            thumbnail.elapsed = thumbnail.elapsed - 2;
        end
        thumbnail:SetValue(thumbnail.elapsed / 2);
    end);

    return thumbnail;
end

OptionsPrivate.registerRegions = OptionsPrivate.registerRegions or {}
table.insert(OptionsPrivate.registerRegions, function()
  OptionsPrivate.Private.RegisterRegionOptions("stopmotion", createOptions, createIcon, L["Stop Motion"],
                                      createThumbnail, modifyThumbnail, L["Shows a stop motion texture"]);
end)
