local async_util = require("lib_async")

local paused = false

-- EMPs lock up the comms board. They won't clear
-- while we're paused, so we need to be careful.
local function isEMPActive()
  return (hv.Player:getEMP() or 0) > 0
end

local function toggleClassic()
  local gauge = hu.getHUDGaugeHandle("Comm Menu")
  if ScpuiSystem.data.ScpuiOptionValues.Comm_Menu_Style == "Radial" then
	--gauge:setRenderOverride(true) Needs an unmerged PR!
  else
    --gauge:setRenderOverride(false)
  end
end

local pause = (function()

  local sfx = ad.loadSoundfile("slowtime.ogg")

  return function()
    if ScpuiSystem.data.ScpuiOptionValues.Comm_Menu_Style == "Classic" then return end
    if paused or isEMPActive() then return end
    paused = true
    mn.runSEXP('( set-time-compression 0.1 1000 )')
	keylock()
    sfx:play(1.0, 0, false)
    async.run(function()
      async.await(async_util.wait_for(0.2))
      if hu.isCommMenuOpen() then
        ScpuiSystem:pauseAllAudio(true)
      end
    end, async.OnFrameExecutor)
  end

end)()

local resume = (function()

  local sfx = ad.loadSoundfile("restoretime.ogg")

  return function()
    if not paused then return end
    paused = false
    mn.runSEXP('( set-time-compression 100 1000 )')
	keyunlock()
    sfx:play(1.0, 0, false)
    async.run(function()
      async.await(async_util.wait_for(0.8))
      if not hu.isCommMenuOpen() then
        ScpuiSystem:pauseAllAudio(false)
        mn.runSEXP('( reset-time-compression )')
      end
    end, async.OnFrameExecutor)
  end

end)()

local function reset()
  paused = false
  mn.runSEXP('( reset-time-compression )')
end

-- Our "pause" is really just "extremely slow", so
-- it's possible for an EMP to start while paused.
local function check()
  toggleClassic() -- Just in case the player changes ships or options mid-mission
  
  -- Handle some edge cases where the option may be changed while the comm menu is active
  -- this should probably move into custom_options-sct.tbm to run only when the option actually changes
  -- but I'm too lazy for that right now.. but doing this string comparison every frame that the menu is open is NOT IDEAL!!!
  if hu.isCommMenuOpen() then
    if paused and ScpuiSystem.data.ScpuiOptionValues.Comm_Menu_Style == "Classic" then
	  resume()
	end
	
	if not paused and ScpuiSystem.data.ScpuiOptionValues.Comm_Menu_Style ~= "Classic" then
	  pause()
	end
  end
  
  -- Now do the EMP check
  if paused and isEMPActive() then resume() end
end

function keylock()
	if ScpuiSystem.data.ScpuiOptionValues.Comm_Menu_Style == "Classic" then return end

	local name = hv.Player.Name

	mn.runSEXP("( ignore-key !-1! !T! !Shift-T! !H! !Shift-H! !Alt-H! !F! !Shift-F! !Y! !G! !Alt-Y! !Alt-T! !V! !S! !Shift-S! !Alt-S! !M! !Alt-M! !.! !,! !/! !Shift-/! !X! !A! !Z! !Pad 7! )")
	mn.runSEXP("( ignore-key !-1! !Pad 9! !Pad 8! !Pad 2! !Pad 4! !Pad 6! !Backspace! !\\! ![! !]! !=! !-! !Shift-A! !Shift-Z! !Shift-D! !Shift-V! !Shift-X! !Shift-E! !Shift-W! !Shift-I! !Shift-P! !Shift-C! !Shift-J! !Shift-R! !R! !Pad *! !Pad .! !Pad Enter! !Pad 0! !Pad /! !Pad +! )")
	mn.runSEXP("( ignore-key !-1! !Pad -! !Pad 5! !'! !Alt-J! !J! !Tab! !Insert! !Delete! !Home! !End! !Page Up! !Page Down! !Alt-D! !Q! !Up Arrow! !Down Arrow! !Left Arrow! !Right Arrow! !Scroll Lock! !Shift-Scroll Lock! !Alt-E! !Alt-Shift-E! !E! !Alt-R! !U! !Shift-U! !N! !K! !Shift-K! )")
	mn.runSEXP("( ignore-key !-1! !B! !Shift-B! !1! !2! !3! !4! !Alt-X! !Shift-.! !Shift-,! !L! !Shift-N! !Shift-End! !Shift-O! !Shift-3! !Shift-1! !Shift-Pad +! !Shift-Pad Enter! !Alt-Shift-Q! !Alt-A! !Alt-N! !Alt-G! !O! )")

	mn.runSEXP("(lock-primary-weapon !" .. name .. "!)")
	mn.runSEXP("(lock-secondary-weapon !" .. name .. "!)")
	mn.runSEXP("(lock-afterburner !" .. name .. "!)") 
	mn.runSEXP("(disable-ets !" .. name .. "!)")
	mn.runSEXP("(lock-perspective (true)))")
	mn.runSEXP("(ignore-key !-1! !T! !H! !F! !Shift-T! !Shift-H! !Alt-H! !Shift-F! !Alt-M! !Alt-J! )")

end

function keyunlock()

	local name = hv.Player.Name

	mn.runSEXP("( ignore-key !0! !T! !Shift-T! !H! !Shift-H! !Alt-H! !F! !Shift-F! !Y! !G! !Alt-Y! !Alt-T! !V! !S! !Shift-S! !Alt-S! !M! !Alt-M! !.! !,! !/! !Shift-/! !X! !A! !Z! !Pad 7! )")
	mn.runSEXP("( ignore-key !0! !Pad 9! !Pad 8! !Pad 2! !Pad 4! !Pad 6! !Backspace! !\\! ![! !]! !=! !-! !Shift-A! !Shift-Z! !Shift-D! !Shift-V! !Shift-X! !Shift-E! !Shift-W! !Shift-I! !Shift-P! !Shift-C! !Shift-J! !Shift-R! !R! !Pad *! !Pad .! !Pad Enter! !Pad 0! !Pad /! !Pad +! )")
	mn.runSEXP("( ignore-key !0! !Pad -! !Pad 5! !'! !Alt-J! !J! !Tab! !Insert! !Delete! !Home! !End! !Page Up! !Page Down! !Alt-D! !Q! !Up Arrow! !Down Arrow! !Left Arrow! !Right Arrow! !Scroll Lock! !Shift-Scroll Lock! !Alt-E! !Alt-Shift-E! !E! !Alt-R! !U! !Shift-U! !N! !K! !Shift-K! )")
	mn.runSEXP("( ignore-key !0! !B! !Shift-B! !1! !2! !3! !4! !Alt-X! !Shift-.! !Shift-,! !L! !Shift-N! !Shift-End! !Shift-O! !Shift-3! !Shift-1! !Shift-Pad +! !Shift-Pad Enter! !Alt-Shift-Q! !Alt-A! !Alt-N! !Alt-G! !O! )")

	mn.runSEXP("(unlock-primary-weapon !" .. name .. "!)")
	mn.runSEXP("(unlock-secondary-weapon !" .. name .. "!)")
	mn.runSEXP("(unlock-afterburner !" .. name .. "!)" ) 
	mn.runSEXP("(enable-ets !" .. name .. "!)")
	mn.runSEXP("(lock-perspective (false) )")
	mn.runSEXP("(ignore-key !0! !T! !H! !F! !Shift-T! !Shift-H! !Alt-H! !Shift-F! !Alt-M! !Alt-J! )")

end

local selection = nil -- Variable to store the currently selected wedge
local includeNonActive = true -- Toggle to include non-active items in the menu
local wedgeSpacing = 0.05 -- Space between wedges, in radians
local innerRadiusRatio = 0.5 -- Ratio of the gap's radius to the wedge's radius
local textArcRadiusRatio = 0.75 -- Radius for text placement as a fraction of wedge radius
local lineSpacing = 15 -- Vertical spacing between two lines of text
local displayShortcuts = true -- Toggle to display shortcut numbers in wedges
local iconAlpha = 75

-- Global colors
local wedgeActiveColor = gr.createColor(0, 128, 128, 128)       -- Teal with 128 alpha
local wedgeInactiveColor = gr.createColor(64, 64, 64, 128)      -- Dimmed gray
local wedgeHighlightColor = gr.createColor(0, 128, 128, 200)      -- Bright blue

local wedgeBorderColor = gr.createColor(128, 128, 128, 255)  -- Grey with full alpha

local textActiveColor = gr.createColor(255, 255, 255, 175)      -- White
local textInactiveColor = gr.createColor(100, 100, 100, 255)    -- Light gray
local textHighlightColor = gr.createColor(255, 255, 255, 255)     -- Cyan

local dx, dy = 0, 0 -- Current movement
local DeltaX, DeltaY = 0, 0 -- Delta changes since last frame
local useFlightControls = true -- Toggle to switch between flight controls and mouse
local deltaThreshold = 0.3 -- Minimum delta required to register movement

local mouseXStart, mouseYStart -- Variables to store the initial mouse position

local function updateMouseInput()
    if not mouseXStart or not mouseYStart then
        mouseXStart, mouseYStart = io.getMouseX(), io.getMouseY()
    end
	
	local centerX = gr.getScreenWidth() / 2
    local centerY = gr.getScreenHeight() / 2

    local mouseX = io.getMouseX()
    local mouseY = io.getMouseY()

    -- Update deltas
    DeltaX = mouseX - mouseXStart
    DeltaY = mouseY - mouseYStart

    -- Update dx, dy
    dx, dy = mouseX - centerX, mouseY - centerY

    -- Reset start positions for next frame
    mouseXStart, mouseYStart = mouseX, mouseY
end

local inputPitch = 0
local inputHeading = 0

local function setPitch(value)
	inputPitch = value
end

local function setHeading(value)
	inputHeading = value
end

local lastHeading, lastPitch = 0, 0 -- Track last Heading and Pitch

local function updateFlightControlsInput()
    local currentHeading = inputHeading -- Input heading value
    local currentPitch = inputPitch     -- Input pitch value

    -- Only update if there is actual input
    if currentHeading ~= 0 and currentPitch ~= 0 then
        -- Calculate deltas
        DeltaX = currentHeading - lastHeading
        DeltaY = currentPitch - lastPitch

        -- Update dx, dy with current input
        dx = currentHeading
        dy = currentPitch
    end

    -- Store the last known input for delta calculation
    lastHeading = currentHeading
    lastPitch = currentPitch
end

local mouseInactiveTime = 0    -- Time since the last mouse movement
local mouseInactivityThreshold = 0.5 -- Time (in seconds) before falling back to flight controls
local mouseUsed = true

-- Global table mapping order names to icon paths
local iconPaths = {
    ["Ships"] = "ships.png",
    ["Wings"] = "wings.png",
    ["All Fighters"] = "allfighters.png",
    ["Reinforcements"] = "reinforcements.png",
    ["Rearm/Repair Subsys"] = "rearmrepair.png",
    ["Abort Rearm"] = "abortrearm.png",
    ["Destroy my target"] = "destroytarget.png",
    ["Disable my target"] = "disabletarget.png",
    ["Disarm my target"] = "disarmtarget.png",
    ["Destroy subsystem"] = "destroysubsystem.png",
    ["Protect my target"] = "protecttarget.png",
    ["Ignore my target"] = "ignoretarget.png",
    ["Form on my wing"] = "formonwing.png",
    ["Cover me"] = "coverme.png",
    ["Engage enemy"] = "engageenemy.png",
    ["Depart"] = "depart.png"
}

local iconTextures = {}

local function getIconFilename(name)

	if iconPaths[name] then
		return iconPaths[name]
	end
	
	if mn.Wings[name]:isValid() then
		return iconPaths["Wings"]
	end
	
	if mn.Ships[name]:isValid() then
		return iconPaths["Ships"]
	end
	
	if mn.ParsedShips[name]:isValid() then
		return iconPaths["Ships"]
	end
	
end

local function updateInput()
    -- Attempt to update using flight controls
    updateFlightControlsInput()
    mouseUsed = false

    -- Check if flight controls provided valid input
    if DeltaX == 0 and DeltaY == 0 then
        -- Fallback to mouse input if no flight control input
        updateMouseInput()
        mouseUsed = true
    end
end

local function splitStringBySpace(str)
    local spaceIndex = str:find(" ")
    if spaceIndex then
        return str:sub(1, spaceIndex - 1), str:sub(spaceIndex + 1)
    end
    return str, nil
end

local function getNextActiveWedge(currentIndex, numItems, filteredItems, direction)
    -- Iterate through the wedges to find the next active one
    local index = currentIndex
    repeat
        index = (index + direction - 1) % numItems + 1 -- Wrap around using modulo
    until filteredItems[index].Active or index == currentIndex
    return index
end

local function drawRadialMenu()

	if ScpuiSystem.data.ScpuiOptionValues.Comm_Menu_Style ~= "Radial" then
		return
	end

    -- Get screen dimensions
    local screenWidth = gr.getScreenWidth()
    local screenHeight = gr.getScreenHeight()
    local smallestDimension = math.min(screenWidth, screenHeight)
    local radius = smallestDimension * 0.35 -- 70% of half the smallest dimension
    local innerRadius = radius * innerRadiusRatio -- Radius of the inner circular gap
    local textArcRadius = radius * textArcRadiusRatio -- Radius for text placement
    local centerX = screenWidth / 2
    local centerY = screenHeight / 2

    -- Fade the background
    gr.setColor(0, 0, 0, 200) -- Semi-transparent black with alpha 200
    gr.drawRectangle(0, 0, screenWidth, screenHeight, true) -- Draw filled rectangle

    -- Get the current menu title
    local menuTitle = "Menu"
    if #mn.CommItems > 0 then
        menuTitle = mn.CommItems[1]:getMenuTitle()
    end

    -- Get the title width
    local titleWidth = gr.getStringWidth(menuTitle)

    -- Filter the menu items based on the local toggle, keeping track of original indices
    local filteredItems = {}
    local originalIndices = {}
    for i = 1, #mn.CommItems do
        local item = mn.CommItems[i]
        if includeNonActive or item.Active then
            table.insert(filteredItems, item)
            table.insert(originalIndices, i) -- Save the original index
        end
    end

    -- Get the number of menu items after filtering
    local numItems = #filteredItems
    if numItems == 0 then return end -- Nothing to draw if there are no items to display

    -- Special case for one menu item
    if numItems == 1 then
        local item = filteredItems[1]
        local textColor = item.Active and textHighlightColor or textInactiveColor
		local iconColor = gr.createColor(wedgeInactiveColor.Red, wedgeInactiveColor.Green, wedgeInactiveColor.Blue, iconAlpha)
		local wedgeColor = wedgeInactiveColor

        -- Highlight the circle
        if item.Active then
			wedgeColor = wedgeHighlightColor
			iconColor = gr.createColor(wedgeHighlightColor.Red, wedgeHighlightColor.Green, wedgeHighlightColor.Blue, iconAlpha)
			selection = originalIndices[1]
        end
		
		-- Determine the icon to render
		local icon = getIconFilename(item.Name)
		
		gr.setColor(wedgeColor)

        -- Draw the circle (highlighted or default)
        gr.drawCircle(radius, centerX, centerY, true)
		
		-- Draw the border for the circle
        gr.setColor(wedgeBorderColor)
        gr.drawCircle(radius, centerX, centerY, false)

        -- Draw the inner circle to create the gap
        gr.setColor(0, 0, 0, 255) -- Solid black for the inner gap
        gr.drawCircle(innerRadius, centerX, centerY, true)
		
		-- Draw the border for the inner circle
        gr.setColor(wedgeBorderColor)
        gr.drawCircle(innerRadius, centerX, centerY, false)

        -- Draw the menu title in the center of the black circle
        gr.setColor(textActiveColor) -- White color for text
        gr.drawString(menuTitle, centerX - (titleWidth / 2), centerY)

        -- Draw the text for the single menu item
        local text = item.Name
        local line1, line2 = splitStringBySpace(text)
		
		-- Render the icon if available
		if icon then
			local iconX = centerX
			local iconY = centerY - radius + 32
			gr.setColor(iconColor)
			gr.drawImage(icon, iconX - 32, iconY - 32, iconX + 32, iconY + 32, 0, 0, 1, 1, 0.5, true)
		end

        gr.setColor(textColor)

        if line1 then
            local line1Width = gr.getStringWidth(line1)
            gr.drawString(line1, centerX - line1Width / 2, centerY - radius + 20) -- Top center with padding
        end

        if line2 then
            local line2Width = gr.getStringWidth(line2)
            gr.drawString(line2, centerX - line2Width / 2, centerY - radius + 20 + lineSpacing)
        end
		
		-- Draw shortcut number if the item is bound to a shortcut (1-10)
		if displayShortcuts then
			local shortcutNumber = 1
			local shortcutX = centerX
			local shortcutY = centerY - radius + 5 -- Place it slightly below the outer edge

			local newColor = gr.createColor(textColor.Red, textColor.Green, textColor.Blue, 75)

            gr.setColor(newColor)
			gr.drawString(tostring(shortcutNumber), shortcutX, shortcutY)
		end

        return -- Exit early since the menu is fully drawn
    end
	
	updateInput()

    -- Calculate the angle of movement relative to the center
    local angle = math.atan2(dy, dx) -- Calculate angle in radians

    -- Normalize angle to [0, 2π]
    if angle < 0 then angle = angle + (2 * math.pi) end
	
	-- Set our threshold based on input type
	local threshold = deltaThreshold
	if not mouseUsed then
		threshold = deltaThreshold * 10
	end

    -- Determine the wedge based on the angle
    if math.sqrt(DeltaX ^ 2 + DeltaY ^ 2) > deltaThreshold then
        local angleStep = (2 * math.pi) / numItems
        local newSelection = math.floor(angle / angleStep) + 1 -- Calculate wedge index
        if not filteredItems[newSelection].Active then
            newSelection = nil
        end
        selection = newSelection
    end

    -- Draw the wedges
    local angleStep = (2 * math.pi) / numItems
    for i = 1, numItems do
        local item = filteredItems[i]
        local startAngle = (i - 1) * angleStep + wedgeSpacing / 2
        local endAngle = i * angleStep - wedgeSpacing / 2
		
		-- Calculate the endpoints for the wedge borders
        local startX = centerX + math.cos(startAngle) * radius
        local startY = centerY + math.sin(startAngle) * radius
        local endX = centerX + math.cos(endAngle) * radius
        local endY = centerY + math.sin(endAngle) * radius
		
		local iconColor

        -- Set color for wedge
        if selection == i then
            gr.setColor(wedgeHighlightColor) -- Highlighted wedge
			iconColor = gr.createColor(wedgeHighlightColor.Red, wedgeHighlightColor.Green, wedgeHighlightColor.Blue, iconAlpha)
        elseif item.Active then
            gr.setColor(wedgeActiveColor) -- Active wedge
			iconColor = gr.createColor(wedgeActiveColor.Red, wedgeActiveColor.Green, wedgeActiveColor.Blue, iconAlpha)
        else
            gr.setColor(wedgeInactiveColor) -- Inactive wedge
			iconColor = gr.createColor(wedgeInactiveColor.Red, wedgeInactiveColor.Green, wedgeInactiveColor.Blue, iconAlpha)
        end
		
		-- Determine the icon to render
		local icon = getIconFilename(item.Name)

        -- Draw the wedge
        gr.drawArc(radius, centerX, centerY, math.deg(startAngle), math.deg(endAngle), true)
		
		-- Draw the border for the wedge
        gr.setColor(wedgeBorderColor)
        gr.drawArc(radius, centerX, centerY, math.deg(startAngle), math.deg(endAngle), false)
		
		-- Draw the side borders of the wedge
        gr.setColor(wedgeBorderColor)
        gr.drawLine(centerX, centerY, startX, startY) -- From center to start of the wedge
        gr.drawLine(centerX, centerY, endX, endY)     -- From center to end of the wedge

        -- Render the wedge text
        local text = item.Name
        local line1, line2 = splitStringBySpace(text)

        -- Calculate the center position for the text
        local midAngle = startAngle + (endAngle - startAngle) / 2
        local textX = centerX + math.cos(midAngle) * textArcRadius
        local textY = centerY + math.sin(midAngle) * textArcRadius

        -- Determine text color
        local textColor = textInactiveColor
        if item.Active then
            textColor = (selection == i) and textHighlightColor or textActiveColor
        end
		
		-- Render the icon if available
		if icon then
			local iconX = centerX + math.cos(midAngle) * (radius - 50)
			local iconY = centerY + math.sin(midAngle) * (radius - 50)
			gr.setColor(iconColor)
			gr.drawImage(icon, iconX - 32, iconY - 32, iconX + 32, iconY + 32, 0, 0, 1, 1, 0.5, true)
		end

        -- Draw the first line
        if line1 then
            local line1Width = gr.getStringWidth(line1)
            gr.setColor(textColor)
            gr.drawString(line1, textX - line1Width / 2, textY - (line2 and lineSpacing / 2 or 0))
        end

        -- Draw the second line (if any)
        if line2 then
            local line2Width = gr.getStringWidth(line2)
            gr.setColor(textColor)
            gr.drawString(line2, textX - line2Width / 2, textY + lineSpacing / 2)
        end
		
		-- Draw shortcut number for items 1-10
        if displayShortcuts and originalIndices[i] <= 10 then
            local shortcutNumber = originalIndices[i] % 10 -- Wrap to 0 after 9
            -- Position the shortcut along the outer circle's radius for this wedge
			local shortcutX = centerX + math.cos(midAngle) * (radius - 10) -- Slightly inward from the edge
			local shortcutY = centerY + math.sin(midAngle) * (radius - 10)
			
			local newColor = gr.createColor(textColor.Red, textColor.Green, textColor.Blue, 75)

            gr.setColor(newColor)
            gr.drawString(tostring(shortcutNumber), shortcutX, shortcutY)
        end
    end

    -- Draw the inner circle to create the gap
    gr.setColor(0, 0, 0, 255) -- Solid black for the inner gap
    gr.drawCircle(innerRadius, centerX, centerY, true)
	
	-- Draw the border for the inner circle
    gr.setColor(wedgeBorderColor)
    gr.drawCircle(innerRadius, centerX, centerY, false)

    -- Draw the menu title in the center of the black circle
    gr.setColor(textActiveColor) -- White color for text
    gr.drawString(menuTitle, centerX - (titleWidth / 2), centerY)
end

local function makeSelection()
	if selection ~= nil and mn.CommItems[selection]:isValid() then
		if mn.CommItems[selection].Active then
			mn.CommItems[selection]:selectItem()
		end
	end
	
	io.flushMouse()
end

return {
  toggleClassic = toggleClassic,
  pause = pause,
  resume = resume,
  reset = reset,
  check = check,
  drawRadialMenu = drawRadialMenu,
  makeSelection = makeSelection,
  setPitch = setPitch,
  setHeading = setHeading
}

