# frozen-string-literal: true

module Bioshogi
  module InputAdapter
    class Ki2Adapter < KifAdapter
      def self.flip_table
        @flip_table ||= yield_self do
          table = {:first => :last, :> => :<}
          table.merge(table.invert)
        end
      end

      include Ki2MotionWrapper

      # 移動元候補がない場合は打が省略されている
      def drop_trigger
        super || drop_abbreviation?
      end

      def place_from
        @place_from ||= yield_self do
          if soldier = origin_soldier
            soldier.place
          end
        end
      end

      def hard_validations
        super

        if !drop_trigger && candidate_soldiers.size >= 2 && motion_str.empty?
          errors_add AmbiguousFormatError, "#{place}に移動できる駒が#{candidate_soldiers.size}つありますが表記が曖昧なため特定できません。#{candidate_soldiers_as_string}"
        end

        if !drop_trigger && candidate_soldiers.count >= 2 && !place_from
          errors_add AmbiguousFormatError, "#{place}に移動できる駒が#{candidate_soldiers.size}つありますが「#{motion_str}」からは特定できません。#{candidate_soldiers_as_string}"
        end

        if force_drop_trigger && !player.piece_box.exist?(piece)
          errors_add HoldPieceNotFound, "打を明示しましたが#{piece}を持っていません"
        end

        if drop_abbreviation? && !player.piece_box.exist?(piece)
          message = []
          message << "#{place}に移動できる#{piece}がないため打の省略形と考えましたが#{piece}を持っていません"
          message += turn_error_messages
          message = message.join("。")
          errors_add HoldPieceNotFound2, message
        end

        if !drop_trigger && candidate_soldiers.empty?
          message = []
          message << "#{player.call_name}の手番で#{place}に移動できる#{input[:kif_piece]}が見つかりません"
          if promoted
            message << "「#{place}#{piece}成」の間違いかもしれません"
          end
          message += turn_error_messages
          message = message.join("。")
          errors_add MovableBattlerNotFound, message
        end

        if drop_trigger && promoted
          errors_add IllegibleFormat, "成・不成と打が干渉しています"
        end
      end

      def origin_soldier
        @origin_soldier ||= yield_self do
          unless force_drop_trigger
            v = candidate_soldiers_select
            if v.size == 1
              v.first
            end
          end
        end
      end

      private

      def candidate_soldiers_select
        @candidate_soldiers_select ||= yield_self do
          list = candidate_soldiers

          if list.size >= 2
            if v = left_right
              if md = v.match(/[左右]/)
                if piece.brave?
                  m = {"左" => :first, "右" => :last}.fetch(md.to_s)
                  m = flip_if_white(m)
                  list = list.sort_by { |e| e.place.x.value }.send(m, 1)
                else
                  m = {"左" => :>, "右" => :<}.fetch(md.to_s)
                  m = flip_if_white(m)
                  list = list.find_all do |e|
                    place.x.value.send(m, e.place.x.value)
                  end
                end
              end
            end
          end

          if list.size >= 2
            if v = up_down
              if md = v.match(/[上引]/)
                m = {"上" => :<, "引" => :>}.fetch(md.to_s)
                m = flip_if_white(m)
                list = list.find_all do |e|
                  place.y.value.send(m, e.place.y.value)
                end
              end
            end
          end

          # 竜が1つだけで15の竜が55に来たときも「1五竜寄」なので左右1マスの限定のチェックは入れてはいけない
          if list.size >= 2
            if v = up_down
              if v.include?("寄")
                list = list.find_all { |e| e.place.y == place.y }
              end
            end
          end

          # 真下にあるもの
          if list.size >= 2
            if one_up?
              y = place.y.value + player.location.which_value(1, -1)
              list = list.find_all { |e|
                e.place.x == place.x && e.place.y.value == y
              }
            end
          end

          list
        end
      end

      # 「打」の省略形かを推測する
      #
      #  1. 駒が成っていないこと
      #  2. 同・左右・打などのサフィックスがないこと
      #  3. 指定の位置に来れる駒がないこと
      #
      def drop_abbreviation?
        !promoted && !suffix_exist? && candidate_soldiers.empty?
      end

      def flip_if_white(key)
        if player.location.key == :white
          key = self.class.flip_table[key]
        end
        key
      end
    end
  end
end