module VER
  class View < Tk::Frame
    autoload :Entry,   'ver/view/entry'
    autoload :List,    'ver/view/list'
    autoload :Console, 'ver/view/console'

    attr_reader :layout, :text, :status

    def initialize(layout, options = {})
      super
      @layout = layout
      @text = @status = @ybar = @xbar = nil
      configure takefocus: false
      setup
    end

    # +-------+---+
    # |       | @ |
    # |       | y |
    # | @text | b |
    # |       | a |
    # |       | r |
    # +-------+---+
    # | @xbar |   |
    # +-------+---+
    # |  @status  |
    # +-----------+
    def setup
      setup_text
      # setup_scrollbars # enable if you really want some.
      setup_status
      setup_grid
      setup_misc
      setup_events
    end

    def setup_text
      font, tabstop = VER.options.font, VER.options.tabstop
      tabs = font.measure('0') * tabstop

      @text = VER::Text.new(
        self,
        autoseparators:   true, # insert separators into the undo flow
        borderwidth:      0,
        exportselection:  true, # copy into X11 buffer automatically
        font:             font,
        insertofftime:    VER.options.insertofftime,
        insertontime:     VER.options.insertontime,
        setgrid:          true, # tell the wm that this is a griddy window
        takefocus:        true,
        tabs:             tabs,
        tabstyle:         :wordprocessor,
        undo:             true, # enable undo capabilities
        wrap:             :word
      )
    end

    def setup_scrollbars
      # vertical scrollbar
      @ybar = Tk::Tile::YScrollbar.new(self)
      @text.yscrollbar(@ybar)

      # horizontal scrollbar
      @xbar = Tk::Tile::XScrollbar.new(self)
      @text.xscrollbar(@xbar)
    end

    def setup_status
      @status = Status.new(self, font: VER.options[:font], takefocus: false)
    end

    def setup_grid
      @text.grid_configure   row: 0, column: 0, sticky: :nsew              if @text
      @ybar.grid_configure   row: 0, column: 1, sticky: :ns                if @ybar
      @xbar.grid_configure   row: 1, column: 0, sticky: :ew                if @xbar
      @status.grid_configure row: 2, column: 0, sticky: :ew, columnspan: 2 if @status

      grid_columnconfigure 0, weight: 1
      grid_columnconfigure 1, weight: 0
      grid_rowconfigure    0, weight: 1
      grid_rowconfigure    1, weight: 0
    end

    def setup_misc
      @text.status = @status
      @text.view = self
      @status.mode = :status_query
    end

    def setup_events
      %w[Modified Focus Movement].each do |name|
        @text.bind("<<#{name}>>"){|event| __send__("on_#{name.downcase}", event) }
      end
    end

    def open_path(path, line = 1)
      @text.open_path(path, line)
    end

    def open_empty
      @text.open_empty
    end

    # handling events

    def on_movement(event)
      @text.see :insert
      @text.refresh_selection
      @text.status_projection(@status)
    end

    def on_modified(event)
      @text.see :insert
      # @text.refresh_highlight
      @text.status_projection(@status)
    end

    def on_focus(event)
      @text.set_window_title
      @text.see(:insert)
    end

    def focus
      text.focus
    end

    def create(path = nil, line = nil)
      layout.create_view do |view|
        path ? view.open_path(path, line) : view.open_empty
        yield(view) if block_given?
      end
    end

    def find_or_create(path, line = nil, &block)
      needle = Pathname(path.to_s).expand_path

      if found = layout.views.find{|view| view.filename == needle }
        found.push_top
        found.focus
        found.text.go_line(line) if line
        yield(found) if block_given?
      else
        create(needle, line, &block)
      end
    end

    def close
      text.may_close do
        layout.close_view(self)
      end
    end

    def focus_next
      layout.focus_next(self)
    end

    def focus_prev
      layout.focus_prev(self)
    end

    def push_up
      layout.push_up(self)
    end

    def push_down
      layout.push_down(self)
    end

    def push_top
      layout.push_top(self)
    end

    def push_bottom
      layout.push_bottom(self)
    end

    def destroy
      [@text, @ybar, @xbar, @status].compact.each(&:destroy)

      super
    end

    def filename
      text.filename
    end
  end
end