defmodule Raxol.Terminal.Commands.CSIHandler.ModeProcessor do
  @moduledoc false

  alias Raxol.Terminal.Commands.CursorUtils
  alias Raxol.Terminal.ModeManager

  def handle_h_or_l(emulator, params, intermediates, final_byte) do
    is_set = final_byte == ?h
    is_private = intermediates == "?"

    result =
      Enum.reduce(params, emulator, fn param, acc ->
        mode_value =
          if is_integer(param), do: param, else: String.to_integer(param)

        if is_private do
          handle_private_mode(acc, mode_value, is_set)
        else
          handle_standard_mode(acc, mode_value, is_set)
        end
      end)

    result
  end

  defp handle_private_mode(emulator, mode, is_set) do
    mode_name = private_mode_name(mode)

    if mode_name do
      category = private_mode_category(mode)

      updated_emulator =
        apply_mode_change(emulator, mode_name, category, is_set)

      apply_screen_buffer_switch(updated_emulator, mode_name, is_set)
    else
      emulator
    end
  end

  defp private_mode_name(mode) do
    case mode do
      1 -> :decckm
      3 -> :deccolm_132
      5 -> :decscnm
      6 -> :decom
      7 -> :decawm
      8 -> :decarm
      9 -> :decinlm
      12 -> :decsrm
      25 -> :dectcem
      47 -> :dec_alt_screen
      1000 -> :mouse_report_x10
      1002 -> :mouse_report_cell_motion
      1003 -> :mouse_any_event
      1004 -> :focus_events
      1047 -> :dec_alt_screen_save
      1048 -> :decsc_deccara
      1049 -> :alt_screen_buffer
      2004 -> :bracketed_paste
      _ -> nil
    end
  end

  defp private_mode_category(mode) do
    case mode do
      47 -> :screen_buffer
      1047 -> :screen_buffer
      1048 -> :screen_buffer
      1049 -> :screen_buffer
      _ -> :dec_private
    end
  end

  defp apply_screen_buffer_switch(emulator, mode_name, is_set) do
    case {mode_name, is_set} do
      {:dec_alt_screen, true} ->
        %{emulator | active_buffer_type: :alternate}

      {:dec_alt_screen, false} ->
        %{emulator | active_buffer_type: :main}

      {:dec_alt_screen_save, true} ->
        %{emulator | active_buffer_type: :alternate}

      {:dec_alt_screen_save, false} ->
        %{emulator | active_buffer_type: :main}

      {:alt_screen_buffer, true} ->
        emulator = save_cursor_position(emulator)
        %{emulator | active_buffer_type: :alternate}

      {:alt_screen_buffer, false} ->
        emulator = %{emulator | active_buffer_type: :main}
        restore_cursor_position(emulator)

      _ ->
        emulator
    end
  end

  defp handle_standard_mode(emulator, mode, is_set) do
    mode_name =
      case mode do
        4 -> :irm
        12 -> :srm
        20 -> :lnm
        _ -> nil
      end

    if mode_name do
      apply_mode_change(emulator, mode_name, :standard, is_set)
    else
      emulator
    end
  end

  defp apply_mode_change(emulator, mode_name, category, is_set) do
    if is_set do
      case ModeManager.set_mode(emulator, [mode_name], category) do
        {:ok, emu} -> emu
        _ -> emulator
      end
    else
      case ModeManager.reset_mode(emulator, [mode_name], category) do
        {:ok, emu} -> emu
        _ -> emulator
      end
    end
  end

  defp save_cursor_position(emulator),
    do: CursorUtils.save_cursor_position(emulator)

  defp restore_cursor_position(emulator),
    do: CursorUtils.restore_cursor_position(emulator)
end
