# frozen_string_literal: true

module Vedeu

  module Geometries

    # Move an interface/view via changing its geometry.
    #
    # When moving an interface/view;
    #
    # 1) Reset the alignment and maximised states to false;
    #    it wont be aligned to a side if moved, and cannot be moved
    #    if maximised.
    # 2) Get the current coordinates of the interface, then:
    # 3) Override the attributes with the new coordinates for
    #    desired movement; these are usually +/- 1 of the current
    #    state, depending on direction.
    #
    # @api private
    #
    class Move

      include Vedeu::Repositories::Defaults
      extend Forwardable

      def_delegators :geometry,
                     :x,
                     :xn,
                     :y,
                     :yn

      # @param (see #initialize)
      # @return (see #move)
      def self.move(attributes = {})
        new(attributes).move
      end

      # @return [Boolean|Vedeu::Geometries::Geometry]
      def move
        return false unless valid?

        Vedeu::Geometries::Geometry.store(new_attributes) do
          update_cursor!
          Vedeu.trigger(:_movement_refresh_, name)
        end
      end

      protected

      # @!attribute [r] direction
      # @return [Symbol] The direction to move; one of: :down, :left,
      #   :origin, :right, :up.
      attr_reader :direction

      # @!attribute [r] name
      # @macro return_name
      attr_reader :name

      # @!attribute [r] offset
      # @return [Symbol] The number of columns or rows to move by.
      attr_reader :offset

      private

      # @return [Hash<Symbol => Symbol>]
      def cursor_event
        {
          down:   :_cursor_down_,
          left:   :_cursor_left_,
          origin: :_cursor_origin_,
          right:  :_cursor_right_,
          up:     :_cursor_up_,
        }[direction]
      end

      # @macro defaults_method
      def defaults
        {
          direction: :none,
          name:      nil,
          offset:    1,
        }
      end

      # @return [Boolean]
      def direction?
        direction != :none
      end

      # @return [Hash<Symbol => Hash<Symbol => Fixnum>>]
      def directional_move
        movement.fetch(direction, {})
      end

      # @return [Hash<Symbol => Fixnum>]
      def down
        {
          y:  y + offset,
          yn: yn + offset,
        }
      end

      # @macro geometry_by_name
      def geometry
        @geometry ||= Vedeu.geometries.by_name(name)
      end

      # @return [Hash<Symbol => Fixnum>]
      def left
        {
          x:  x - offset,
          xn: xn - offset,
        }
      end

      # Moves the geometry in the direction given by the offset also
      # given.
      #
      # @return [Hash<Symbol => Hash<Symbol => Fixnum>>]
      def movement
        {
          down:   down,
          left:   left,
          none:   {},
          origin: origin,
          right:  right,
          up:     up,
        }
      end

      # @return [Hash<Symbol => Boolean|String|Symbol>]
      def new_attributes
        geometry.attributes.merge!(unalign_unmaximise)
      end

      # @return [Hash<Symbol => Fixnum>]
      def origin
        {
          x:  1,
          xn: (xn - x + 1),
          y:  1,
          yn: (yn - y + 1),
        }
      end

      # @return [Hash<Symbol => Fixnum>]
      def right
        {
          x:  x + offset,
          xn: xn + offset,
        }
      end

      # @return [Hash<Symbol => Boolean|Symbol]
      def unalign_unmaximise
        {
          horizontal_alignment: :none,
          maximised:            false,
          vertical_alignment:   :none,
        }.merge(directional_move)
      end

      # @return [Hash<Symbol => Fixnum>]
      def up
        {
          y:  y - offset,
          yn: yn - offset,
        }
      end

      # Refresh the cursor after moving.
      #
      # @return [void]
      def update_cursor!
        if direction == :origin
          Vedeu.trigger(cursor_event, name)

        else
          Vedeu.trigger(cursor_event, name, 0)

        end
      end

      # @return [Boolean]
      def valid?
        {
          down:   valid_down?,
          left:   valid_left?,
          origin: true,
          none:   false,
          right:  valid_right?,
          up:     valid_up?,
        }.fetch(direction, false)
      end

      # @return [Boolean]
      def valid_down?
        Vedeu::Point.valid?(value: yn + offset, max: Vedeu.height)
      end

      # @return [Boolean]
      def valid_left?
        Vedeu::Point.valid?(value: x - offset, min: 1)
      end

      # @return [Boolean]
      def valid_right?
        Vedeu::Point.valid?(value: xn + offset, max: Vedeu.width)
      end

      # @return [Boolean]
      def valid_up?
        Vedeu::Point.valid?(value: y - offset, min: 1)
      end

    end # Move

  end # Geometries

end # Vedeu
