module Fusuma
  # manage actions
  class ActionStack < Array
    def initialize(*args)
      super(*args)
    end

    # return { finger:, direction:, action: } or nil
    def gesture_info
      return unless enough_actions?
      MultiLogger.debug(enough_actions?: enough_actions?)
      direction = detect_direction
      finger    = detect_finger
      action    = detect_action
      clear
      MultiLogger.debug(finger: finger, direction: direction, action: action)
      GestureInfo.new(finger, direction, action)
    end

    def push(gesture_action)
      super(gesture_action)
      clear if action_end?
    end
    alias << push

    private

    GestureInfo = Struct.new(:finger, :direction, :action)
    Direction = Struct.new(:move, :pinch)

    def detect_direction
      direction_hash = sum_direction
      move = detect_move(direction_hash)
      pinch = detect_pinch(direction_hash)
      Direction.new(move, pinch)
    end

    def detect_move(direction_hash)
      if direction_hash[:move][:x].abs > direction_hash[:move][:y].abs
        return direction_hash[:move][:x] > 0 ? 'right' : 'left'
      end
      direction_hash[:move][:y] > 0 ? 'down' : 'up'
    end

    def detect_pinch(direction_hash)
      direction_hash[:pinch] > 1 ? 'in' : 'out'
    end

    def detect_finger
      last.finger
    end

    def sum_direction
      move_x = sum_attrs(:move_x)
      move_y = sum_attrs(:move_y)
      pinch  = mul_attrs(:pinch)
      { move: { x: move_x, y: move_y }, pinch: pinch }
    end

    def sum_attrs(attr)
      send('map') do |gesture_action|
        gesture_action.send(attr.to_sym.to_s)
      end.compact.inject(:+)
    end

    def mul_attrs(attr)
      send('map') do |gesture_action|
        num = gesture_action.send(attr.to_sym.to_s)
        # NOTE: ignore 0.0, treat as 1(immutable)
        num.zero? ? 1 : num
      end.compact.inject(:*)
    end

    def action_end?
      last_action_name =~ /_END$/
    end

    def last_action_name
      return false if last.class != GestureAction
      last.action
    end

    def enough_actions?
      length > 7 # TODO: should be detected by move per time
    end

    def detect_action
      first.action =~ /GESTURE_(.*?)_/
      Regexp.last_match(1).downcase
    end
  end
end