module Picky

  # A simple terminal based search.
  #
  class Cursed # :nodoc:all

    require 'curses'
    include Curses

    attr_reader :client

    def initialize given_uri, id_amount = nil
      check_picky_client_gem

      init_screen
      curs_set 1
      stdscr.keypad(true)

      require 'uri'
      uri = URI.parse given_uri

      # If the user gave a whole url without http, add that and reparse.
      #
      unless uri.path
        uri = URI.parse "http://#{given_uri}"
      end

      # If the user gave a path without / in front, add one.
      #
      unless uri.path =~ /^\//
        uri.path = "/#{uri.path}"
      end

      @searches  = 0
      @durations = 0

      @current_text  = ''
      @id_amount     = id_amount && Integer(id_amount) || 20
      @client        = Picky::Client.new uri

      install_trap
    end
    def check_picky_client_gem # :nodoc:
      require 'picky-client'
    rescue LoadError
      warn_gem_missing 'picky-client', 'the terminal interface'
      exit 1
    end

    # Install the Ctrl-C handler.
    #
    def install_trap
      Signal.trap('INT') do
        move_to_error
        puts "Performed #{@searches} searches (#{"%.3f" % @durations} seconds)."
        sleep 1
        exit
      end
    end

    # Positioning.
    #
    def move_to_counts
      setpos 3, 0
    end
    def move_to_input
      setpos 3, (12 + @current_text.size)
    end
    def move_to_results
      setpos 4, 12
    end
    def move_to_error
      setpos 5, 0
    end

    # Delete one character.
    #
    def backspace
      chop_text
      move_to_input
      clrtoeol
    end
    # Chop off one character.
    #
    def chop_text
      @current_text.chop!
    end

    # Add the given text to the current text.
    #
    def add_text text
      @current_text << text
    end

    # Type the given text into the input area.
    #
    def type_search character
      add_text character
    end

    # Write the result ids.
    #
    def write_results results
      move_to_results
      addstr "#{results.total ? results.ids(@id_amount) : []}"
      move_to_input
    rescue StandardError => e
      p e.message
      p e.backtrace
    end
    # Clear the result ids.
    #
    def clear_results
      move_to_results
      clrtoeol
      move_to_input
    end

    # Write the amount of result ids.
    #
    def write_counts results
      move_to_counts
      addstr "%11d" % (results && results.total || 0)
      move_to_input
    end

    # Log a search.
    #
    def log results
      @searches += 1
      @durations += (results[:duration] || 0)
    end

    # Perform a search.
    #
    def search full = false
      client.search @current_text, :ids => (full ? @id_amount : 0)
    end

    # Perform a search and write the results.
    #
    # Handles 404s and connection problems.
    #
    def search_and_write full = false
      results = search full
      results.extend Picky::Convenience

      clear_error
      log results

      full ? write_results(results) : clear_results

      write_counts results
      move_to_input
    rescue Errno::ECONNREFUSED => e
      error "Please start a Picky server listening to #{@client.path}."
    rescue Yajl::ParseError => e
      error "Got a 404. Maybe the path #{@client.path} isn't a correct one?"
    end

    # Display an error text.
    #
    def error text
      move_to_error
      flash
      addstr text
      move_to_input
    end
    def clear_error
      move_to_error
      addstr @error_clear_string ||= " "*80
      move_to_input
    end

    # Display an intro text.
    #
    def intro
      addstr "Type and see the result count update. Press enter for the first #{@id_amount} result ids."
      setpos 1, 0
      addstr "Break with Ctrl-C."
      setpos 2, 0
    end

    # Run the terminal.
    #
    # Note: Uses a simple loop to handle input.
    #
    def run
      intro

      move_to_input
      search_and_write

      loop do
        input = getch

        case input
        when 10 # Curses::Key::ENTER
          search_and_write true
        when 127 # Curses::Key::BACKSPACE
          delch
          backspace
          search_and_write
        when (256..1000000)

        else
          type_search input.chr
          search_and_write
        end
      end
    end

  end

end