# frozen_string_literal: true

require 'curses'
require 'time'

module Cryptum
  # Cryptum::UI Module used for Presenting the
  # Cryptum Curses Interface
  module UI
    autoload :KeyPressEvent, 'cryptum/ui/key_press_event'
    autoload :TerminalWindow, 'cryptum/ui/terminal_window'
    autoload :Ticker, 'cryptum/ui/ticker'
    autoload :Portfolio, 'cryptum/ui/portfolio'
    autoload :OrderPlan, 'cryptum/ui/order_plan'
    autoload :OrderTimer, 'cryptum/ui/order_timer'
    autoload :MarketTrend, 'cryptum/ui/market_trend'
    autoload :SignalEngine, 'cryptum/ui/signal_engine'
    autoload :OrderExecution, 'cryptum/ui/order_execution'
    autoload :Command, 'cryptum/ui/command'

    # Initialize the UI
    public_class_method def self.init
      # Initialize curses Screen
      Curses.init_screen

      # Ensure the cursor is invisible
      Curses.curs_set(0)

      # Do not echo keystrokes back to the UI
      Curses.noecho

      # Used to immediately evaluate characters submitted
      # without pressing ENTER (e.g. b, r, & w key events)
      Curses.crmode
      # Curses.nocrmode
      # Curses.raw # NO
      # Curses.noraw
      # Disable Line Buffering
      Curses.ESCDELAY = 0

      # Start Color!
      Curses.start_color

      # This is important to ensure -1 maintains
      # the original background color in the terminal
      # when defining color pairs
      Curses.use_default_colors

      # This object is used to pass all of the UI sections
      # around to various Cryptum modules
      Cryptum::UI::TerminalWindow.new
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Create New Curses Window
    public_class_method def self.window(opts = {})
      height = opts[:height].to_i
      width = opts[:width].to_i
      top = opts[:top].to_i
      left = opts[:left].to_i

      window = Curses::Window.new(
        height,
        width,
        top,
        left
      )
      window.nodelay = true

      window
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Draw a Box Around a Window
    public_class_method def self.line(opts = {})
      ui_win = opts[:ui_win]
      out_line_no = opts[:out_line_no].to_i
      color = opts[:color]
      color ||= :white

      style = :normal
      style = :bold unless color == :white

      ui_win.setpos(out_line_no, 0)
      colorize(
        ui_win: ui_win,
        color: color,
        style: style,
        string: "\u2500" * Curses.cols
      )
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Jump to First Column
    public_class_method def self.colorize(opts = {})
      ui_win = opts[:ui_win]
      color = opts[:color].to_s.to_sym
      red = opts[:red].to_i
      green = opts[:green].to_i
      blue = opts[:blue].to_i

      style = opts[:style].to_s.to_sym
      style = :normal if opts[:style].nil?

      bg = opts[:bg].to_i
      bg = -1 if opts[:bg].nil?

      string = opts[:string]

      case color
      when :black
        color_id = 0
        color_fg = Curses::COLOR_BLACK
        color_bg = bg
      when :red
        color_id = 1
        color_fg = Curses::COLOR_RED
        color_bg = bg
      when :green
        color_id = 2
        color_fg = Curses::COLOR_GREEN
        color_bg = bg
      when :yellow
        color_id = 3
        color_fg = Curses::COLOR_YELLOW
        color_bg = bg
      when :blue
        color_id = 4
        color_fg = Curses::COLOR_BLUE
        color_bg = bg
      when :magenta
        color_id = 5
        color_fg = Curses::COLOR_MAGENTA
        color_bg = bg
      when :cyan
        color_id = 6
        color_fg = Curses::COLOR_CYAN
        color_bg = bg
      when :white
        color_id = 7
        color_fg = Curses::COLOR_WHITE
        color_bg = bg
      when :rainbow
        color_id = 254
        red = Random.rand(0..1000)
        green = Random.rand(0..1000)
        blue = Random.rand(0..1000)
        Curses.init_color(color_id, red, green, blue)
        color_fg = color_id
        color_bg = bg
      when :custom
        color_id = 255
        Curses.init_color(color_id, red, green, blue)
        color_fg = color_id
        color_bg = bg
      else
        raise "Color Not Implemented for this Method: #{color}"
      end

      case style
      when :blink
        font = Curses::A_BLINK
      when :bold
        font = Curses::A_BOLD
      when :highlight
        font = Curses::A_STANDOUT
      when :normal
        font = Curses::A_NORMAL
      when :reverse
        font = Curses::A_REVERSE
      else
        raise "Font Style Not Implemented for this Method: #{style}"
      end

      Curses.init_pair(color_id, color_fg, color_bg)
      ui_win.attron(Curses.color_pair(color_id) | font) do
        ui_win.addstr(string)
      end
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Jump to First Column
    public_class_method def self.col_center(opts = {})
      str = opts[:str]

      str_divided_by_two = str.length / 2
      (Curses.cols / 2) - str_divided_by_two
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    public_class_method def self.col_first
      0
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Jump to Second Column
    public_class_method def self.col_second
      (Curses.cols / 8) + 5
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Jump to Third Column
    public_class_method def self.col_third
      ((Curses.cols / 8) * 3) + 2
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Jump to Fourth Column
    public_class_method def self.col_fourth
      ((Curses.cols / 4) * 3) - 3
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    public_class_method def self.detect_key_press_in_ui(opts = {})
      key_press_event = opts[:key_press_event]
      ui_win = opts[:ui_win]

      key_press = ui_win.get_char

      # Useful for detecting and logging actual key presses to file
      # unless key_press.nil?
      #   File.open('/tmp/detect_key_press_in_ui-cryptum.txt', 'a') do |f|
      #     f.puts key_press.class
      #     f.print key_press.inspect
      #     f.puts "\n\n\n"
      #    end
      # end

      case key_press
      when 'C'
        key_press_event.key_c = true
      when 'G'
        key_press_event.key_g = true
      when 'r'
        key_press_event.key_r = true
      when 'u'
        key_press_event.key_u = true
      when 'w'
        key_press_event.key_w = true
      when 'x'
        key_press_event.key_x = true
      when "\t"
        key_press_event.key_tab = true
      when "\e"
        key_press_event.key_esc = true
      end

      # What a hack to detect up / down arrow key presses.
      key_press_event.key_ansi = true if key_press_event.key_esc && key_press == '['
      key_press_event.key_up_arrow = true if key_press_event.key_ansi && key_press == 'A'
      key_press_event.key_down_arrow = true if key_press_event.key_ansi && key_press == 'B'

      key_press_event
    rescue Interrupt
      # Exit Gracefully if CTRL+C is Pressed During Session
      Cryptum.exit_gracefully(which_self: self)
    rescue StandardError => e
      # Produce a Stacktrace for anything else
      Curses.close_screen
      raise e
    end

    # Display a List of Every UI Module

    public_class_method def self.help
      constants.sort
    end
  end
end