module Vedeu

  # Converts the collection passed into a list of menu items which can be
  # navigated using the instance methods or events provided.
  #
  # @api private
  class Menu

    include Vedeu::Model

    # @!attribute [rw] collection
    # @return [Array]
    attr_accessor :collection

    # Returns the index of the value in the collection which is current.
    #
    # @!attribute [rw] current
    # @return [Fixnum]
    attr_accessor :current

    # The name of the menu. Used to reference the menu throughout the
    # application's execution lifetime.
    #
    # @!attribute [rw] name
    # @return [String]
    attr_accessor :name

    # Returns the index of the value in the collection which is selected.
    #
    # @!attribute [rw] selected
    # @return [Fixnum]
    attr_accessor :selected

    # Register a menu by name which will display a collection of items for
    # your users to select; and provide interactivity within your application.
    #
    # @param name [String]
    # @param block [Proc] A set of attributes which define the features of the
    #   menu. See {Vedeu::DSL::Menu#items} and {Vedeu::DSL::Menu#name}.
    #
    # @example
    #   Vedeu.menu 'my_interface' do
    #     items [:item_1, :item_2, :item_3]
    #     # ...
    #   end
    #
    #   Vedeu.menu do
    #     name 'menus_must_have_a_name'
    #     items Track.all_my_favourites
    #     # ...
    #   end
    #
    # @raise [Vedeu::InvalidSyntax] The required block was not given.
    # @return [API::Menu]
    def self.menu(name = '', &block)
      fail Vedeu::InvalidSyntax, 'block not given' unless block_given?

      build({ name: name }, &block).store
    end

    # Returns a new instance of Vedeu::Menu.
    #
    # @param attributes [Hash]
    # @option attributes collection [Array]
    # @option attributes name [String]
    # @option attributes current [Fixnum]
    # @option attributes selected [Fixnum|NilClass]
    # @return [Vedeu::Menu]
    def initialize(attributes = {})
      @attributes = defaults.merge!(attributes)

      @attributes.each { |key, value| instance_variable_set("@#{key}", value) }
    end

    # Returns the item from the collection which shares the same index as the
    # value of {Vedeu::Menu#current}.
    #
    # @return [void]
    def current_item
      @collection[@current]
    end

    # Returns the item from the collection which shares the same index as the
    # value of {Vedeu::Menu#selected}.
    #
    # @return [|NilClass]
    def selected_item
      return nil unless @selected

      @collection[@selected]
    end

    # Returns a new collection of items.
    # Each element of the collection is of the format:
    #
    #   [selected, current, item]
    #
    # `selected` is a boolean indicating whether the item is selected.
    # `current`  is a boolean indicating whether the item is current.
    # `item`     is the item itself.
    #
    # @return [Array]
    def items
      items = []
      @collection.each_with_index do |item, index|
        if index == @current && index == @selected
          items << [true, true, item]

        elsif index == @current
          items << [false, true, item]

        elsif index == @selected
          items << [true, false, item]

        else
          items << [false, false, item]

        end
      end
      items
    end

    # Returns a subset of all the items.
    #
    # @return [Array]
    def view
      items[@current, @collection.size]
    end

    # Sets the value of current to be the first item of the collection.
    #
    # @return [Array]
    def top_item
      @current = 0

      items
    end

    # Sets the value of current to be the last item of the collection.
    #
    # @return [Array]
    def bottom_item
      @current = last

      items
    end

    # Sets the value of current to be the next item in the collection until we
    # reach the last.
    #
    # @return [Array]
    def next_item
      @current += 1 if @current < last

      items
    end

    # Sets the value of current to be the previous item in the collection until
    # we reach the first.
    #
    # @return [Array]
    def prev_item
      @current -= 1 if @current > 0

      items
    end

    # Sets the selected item to be the same value as the current item.
    #
    # @return [Array]
    def select_item
      @selected = @current

      items
    end

    # Removes the value of `selected`, meaning no items are selected.
    #
    # @return [Array]
    def deselect_item
      @selected = nil

      items
    end

    # Returns the last index of the collection.
    #
    # @return [Fixnum]
    def last
      @collection.size - 1
    end

    # Returns the size of the collection.
    #
    # @return [Fixnum]
    def size
      @collection.size
    end

    private

    # The default values for a new instance of this class.
    #
    # @return [Hash]
    def defaults
      {
        client:     nil,
        collection: [],
        current:    0,
        name:       '',
        repository: Vedeu.menus,
        selected:   nil,
      }
    end

  end # Menu

end # Vedeu