--[[

   A simple script that shows a pause indicator, on pause
   https://github.com/Samillion/ModernZ/tree/main/extras/pause-indicator-lite

--]]

local options = {
  -- indicator icon type
  indicator_icon = "pause", -- indicator icon type. "pause", "play"
  indicator_stay = true,    -- keep indicator visibile during pause
  indicator_timeout = 0.6,  -- timeout (seconds) if indicator doesn't stay

  -- keybind
  keybind_allow = true,       -- allow keybind to toggle pause
  keybind_set = "mbtn_left",  -- the used keybind to toggle pause
  keybind_mode = "onpause",   -- mode to activate keybind. "onpause", "always"
  keybind_eof_disable = true, -- disable keybind on eof (end of file)

  -- icon colors & opacity
  icon_color = "#FFFFFF",        -- icon fill color
  icon_border_color = "#111111", -- icon border color
  icon_border_width = 1.5,       -- icon border width
  icon_opacity = 40,             -- icon opacity (0-100)

  -- pause icon
  rectangles_width = 30,   -- width of rectangles
  rectangles_height = 80,  -- height of rectangles
  rectangles_spacing = 20, -- spacing between the two rectangles

  -- play icon
  triangle_width = 80,  -- width of triangle
  triangle_height = 80, -- height of triangle

  -- best with pause icon
  flash_play_icon = true,   -- flash play icon on unpause
  flash_icon_timeout = 0.3, -- timeout (seconds) for flash icon

  -- icon style used in ModernZ osc
  fluent_icons = false,  -- requires fonts/fluent-system-icons.ttf
  fluent_icon_size = 80, -- fluent icon size

  -- mute options
  mute_indicator = false,              -- show a mute indicator (requires fluent font)
  mute_indicator_pos = "middle_right", -- position of mute indicator. top_left, top_right, top_center
  -- also: middle_*, bottom_* same as top_* (ie: bottom_right)
}

local msg = require "mp.msg"
require 'mp.options'.read_options(options, "pause-indicator")

-- convert color from hex (adjusted from mpv/osc.lua)
local function convert_color(color)
  if color:find("^#%x%x%x%x%x%x$") == nil then
    msg.warn("'" .. color .. "' is not a valid color, using default '#FFFFFF'")
    return "FFFFFF" -- color fallback
  end
  return color:sub(6, 7) .. color:sub(4, 5) .. color:sub(2, 3)
end

-- convert percentage opacity (0-100) to ASS alpha values
local function convert_opacity(value)
  value = math.max(0, math.min(100, value))
  return string.format("%02X", (255 - (value * 2.55)))
end

-- colors and opaicty
local icon_color = convert_color(options.icon_color)
local icon_border_color = convert_color(options.icon_border_color)
local icon_opacity = convert_opacity(options.icon_opacity)
local icon_font = "fluent-system-icons"

-- pause icon
local function draw_rectangles()
  if options.fluent_icons then
    local pause_icon = "\238\163\140"
    return string.format([[{\\rDefault\\an5\\alpha&H%s\\bord%s\\1c&H%s&\\3c&H%s&\\fs%s\\fn%s}%s]],
      icon_opacity, options.icon_border_width, icon_color, icon_border_color, options.fluent_icon_size, icon_font,
      pause_icon)
  else
    return string.format(
      [[{\\rDefault\\p1\\an5\\alpha&H%s\\bord%s\\1c&H%s&\\3c&H%s&}m 0 0 l %d 0 l %d %d l 0 %d m %d 0 l %d 0 l %d %d l %d %d{\\p0}]],
      icon_opacity, options.icon_border_width, icon_color, icon_border_color, options.rectangles_width,
      options.rectangles_width,
      options.rectangles_height, options.rectangles_height, options.rectangles_width + options.rectangles_spacing,
      options.rectangles_width * 2 + options.rectangles_spacing,
      options.rectangles_width * 2 + options.rectangles_spacing,
      options.rectangles_height, options.rectangles_width + options.rectangles_spacing, options.rectangles_height)
  end
end

-- play icon
local function draw_triangle()
  if options.fluent_icons then
    local play_icon = "\238\166\143"
    return string.format([[{\\rDefault\\an5\\alpha&H%s\\bord%s\\1c&H%s&\\3c&H%s&\\fs%s\\fn%s}%s]],
      icon_opacity, options.icon_border_width, icon_color, icon_border_color, options.fluent_icon_size, icon_font,
      play_icon)
  else
    return string.format([[{\\rDefault\\p1\\an5\\alpha&H%s\\bord%s\\1c&H%s&\\3c&H%s&}m 0 0 l %d %d l 0 %d{\\p0}]],
      icon_opacity, options.icon_border_width, icon_color, icon_border_color, options.triangle_width,
      options.triangle_height / 2, options.triangle_height)
  end
end

-- mute icon
local function draw_mute()
  if not options.fluent_icons then return end

  local mute_icon = "\238\173\138"
  local mute_pos_list = {
    ["top_left"] = 7,
    ["top_center"] = 8,
    ["top_right"] = 9,
    ["middle_left"] = 4,
    ["middle_center"] = 5,
    ["middle_right"] = 6,
    ["bottom_left"] = 1,
    ["bottom_center"] = 2,
    ["bottom_right"] = 3,
  }
  local mute_pos = mute_pos_list[options.mute_indicator_pos:lower()] or 6
  return string.format([[{\\rDefault\\an%s\\alpha&H%s\\bord%s\\1c&H%s&\\3c&H%s&\\fs%s\\fn%s}%s]],
    mute_pos, icon_opacity, options.icon_border_width, icon_color, icon_border_color, options.fluent_icon_size, icon_font,
    mute_icon)
end

-- initiate overlay
local indicator = mp.create_osd_overlay("ass-events")
local flash = mp.create_osd_overlay("ass-events")
local mute = mp.create_osd_overlay("ass-events")

-- keep track of pause toggle and end of file
local toggled, eof

-- draw and update indicator
local function update_indicator()
  local _, _, display_aspect = mp.get_osd_size()
  if display_aspect == 0 or (indicator.visible and not toggled) then return end

  indicator.data = options.indicator_icon == "play" and draw_triangle() or draw_rectangles()
  indicator:update()

  if not options.indicator_stay then
    mp.add_timeout(options.indicator_timeout, function() indicator:remove() end)
  end
end

-- flash play icon
local function flash_icon()
  if not options.flash_play_icon then return flash:remove() end
  flash.data = draw_triangle()
  flash:update()
  mp.add_timeout(options.flash_icon_timeout, function() flash:remove() end)
end

-- draw mute icon
local function mute_icon()
  mute.data = draw_mute()
  mute:update()
end

-- check if file is video
local function is_video()
  local t = mp.get_property_native("current-tracks/video")
  return t and not (t.image or t.albumart) and true or false
end

-- remove overlays
local function shutdown()
  if flash then flash:remove() end
  if indicator then indicator:remove() end
  mp.unobserve_property("pause")
end

-- end of file keybind check
if options.keybind_eof_disable then
  mp.observe_property("eof-reached", "bool", function(_, val)
    eof = val
  end)
end

-- observe when pause state changes
mp.observe_property("pause", "bool", function(_, paused)
  if not is_video() then return shutdown() end
  if paused then
    update_indicator()
    toggled = true
    if options.flash_play_icon then flash:remove() end
  else
    indicator:remove()
    if toggled then
      flash_icon()
      toggled = false
    end
  end

  -- keybind setup (if options allow it)
  if options.keybind_allow == true then
    mp.set_key_bindings({
      { options.keybind_set, function() mp.commandv("cycle", "pause") end }
    }, "pause-indicator", "force")

    if options.keybind_mode == "always" or (options.keybind_mode == "onpause" and paused) then
      if not eof then mp.enable_key_bindings("pause-indicator") end
    else
      mp.disable_key_bindings("pause-indicator")
    end
  end
end)

-- update pause indicator position if window size changes
mp.observe_property("osd-dimensions", "native", function()
  if indicator and indicator.visible then
    update_indicator()
  end
end)

if options.mute_indicator and options.fluent_icons then
  mp.observe_property("mute", "bool", function(_, val)
    if val and not mute.visible then mute_icon() else mute:remove() end
  end)
else
  mute:remove()
end
