# frozen_string_literal: true

module Vedeu

  module Menus

    # 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::Repositories::Model

      # @!attribute [w] collection
      # @return [Array]
      attr_writer :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

      # Returns a new instance of Vedeu::Menus::Menu.
      #
      # @param attributes [Hash]
      # @option attributes collection [Array]
      # @option attributes name [String|Symbol]
      # @option attributes current [Fixnum]
      # @option attributes selected [Fixnum|NilClass]
      # @return [Vedeu::Menus::Menu]
      def initialize(attributes = {})
        defaults.merge!(attributes).each do |key, value|
          instance_variable_set("@#{key}", value)
        end
      end

      # @return [Array]
      def collection
        @collection || []
      end

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

      # Returns a DSL instance responsible for defining the DSL
      # methods of this model.
      #
      # @param client [Object|NilClass] The client binding represents
      #   the client application object that is currently invoking a
      #   DSL method. It is required so that we can send messages to
      #   the client application object should we need to.
      # @return [Vedeu::Menus::DSL] The DSL instance for this model.
      def deputy(client = nil)
        Vedeu::Menus::DSL.new(self, client)
      end

      # Returns the item from the collection which shares the same
      # index as the value of {Vedeu::Menus::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|
          items << if index == @current && index == @selected
                     [true, true, item]

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

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

                   else
                     [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

      # @macro defaults_method
      def defaults
        {
          client:     nil,
          collection: [],
          current:    0,
          name:       nil,
          repository: Vedeu.menus,
          selected:   nil,
        }
      end

    end # Menu

  end # Menus

  # @api public
  # @!method menu
  #   @see Vedeu::Menus::DSL.menu
  def_delegators Vedeu::Menus::DSL,
                 :menu

end # Vedeu
