module Textbringer
  module Commands
    class BufferPosition
      attr_reader :buffer, :mark

      def initialize(buffer, mark)
        @buffer = buffer
        @mark = mark
      end

      def to_s
        @mark.location.to_s
      end
    end

    REGISTERS = {}

    def REGISTERS.[]=(name, val)
      old_val = REGISTERS[name]
      if old_val.is_a?(BufferPosition)
        old_val.mark.delete
      end
      super(name, val)
    end

    def read_register(prompt)
      Window.echo_area.show(prompt)
      Window.redisplay
      begin
        register = read_char
        register
      ensure
        Window.echo_area.clear
        Window.redisplay
      end
    end

    define_command(:point_to_register) do
      |register = read_register("Point to register:")|
      unless register.is_a?(String)
        raise ArgumentError, "Invalid register: #{register}"
      end
      buffer = Buffer.current
      mark = buffer.new_mark
      REGISTERS[register] = BufferPosition.new(buffer, mark)
    end

    define_command(:jump_to_register) do
      |register = read_register("Jump to register:")|
      if !register.is_a?(String)
        raise ArgumentError, "Invalid register: #{register}"
      end
      position = REGISTERS[register]
      if !position.is_a?(BufferPosition)
        raise ArgumentError, "Register doesn't contain a buffer position"
      end
      switch_to_buffer(position.buffer)
      position.buffer.point_to_mark(position.mark)
    end

    define_command(:copy_to_register) do
      |register = read_register("Copy to register:"),
        s = Buffer.current.mark, e = Buffer.current.point,
        delete_flag = current_prefix_arg|
      buffer = Buffer.current
      str = s <= e ? buffer.substring(s, e) : buffer.substring(e, s)
      REGISTERS[register] = str
      if delete_flag
        buffer.delete_region(s, e)
      end
    end

    define_command(:append_to_register) do
      |register = read_register("Append to register:"),
        s = Buffer.current.mark, e = Buffer.current.point,
        delete_flag = current_prefix_arg|
      buffer = Buffer.current
      str = s <= e ? buffer.substring(s, e) : buffer.substring(e, s)
      val = REGISTERS[register]
      if !val.is_a?(String)
        raise ArgumentError, "Register doesn't contain text"
      end
      REGISTERS[register] = val + str
      if delete_flag
        buffer.delete_region(s, e)
      end
    end

    define_command(:insert_register) do
      |register = read_register("Insert register:"),
        arg = current_prefix_arg|
      buffer = Buffer.current
      str = REGISTERS[register]
      if arg
        buffer.push_mark
      end
      pos = buffer.point
      insert(str)
      if !arg
        buffer.push_mark
        buffer.goto_char(pos)
      end
    end

    define_command(:number_to_register) do
      |n = number_prefix_arg,
        register = read_register("Number to register:")|
      REGISTERS[register] = n
    end

    define_command(:increment_register) do
      |n = current_prefix_arg,
        register = read_register("Increment register:")|
      i = REGISTERS[register]
      case i
      when Integer
        REGISTERS[register] = i + prefix_numeric_value(n)
      when String
        append_to_register(register,
                           Buffer.current.mark, Buffer.current.point, n)
      else
        raise ArgumentError, "Register doesn't contain a number or text"
      end
    end
  end
end
