; SPDX-FileCopyrightText: 2023 Jummit
;
; SPDX-License-Identifier: GPL-3.0-or-later

(local {: get-start-cells-for
        : get-online-of
        : get-cards-on
        : is-online
        : get-cell} (require :match.info))

(local {: move} (require :move))
(local {: make-state} (require :match.state))
(local {: get-possible-actions} (require :match.actions))
(local {: perform-action} (require :match.action))
(local {: add-cell-action
        : add-card-action
        : add-slot-action
        : remove-cell-actions
        : remove-card-actions
        : remove-slot-actions} (require :match.interact))

(fn remove-actions [world]
  (remove-card-actions world)
  (remove-slot-actions world)
  (remove-cell-actions world))

(fn card-action [world card action]
  (add-card-action card (fn []
                          (perform-action world action)
                          (remove-actions world))))

(fn slot-action [world slot action]
  (add-slot-action slot (fn []
                          (perform-action world action)
                          (remove-actions world))))

(fn player-setup [world]
  "System which adds an interface for the player to place online cards
in the setup phase. After all cards are placed the setup action is
performed."
  (case world.phase
    {:kind :setup : player}
    (when (= (. world.players player) :player)
      (when (not world.phase.placing)
        (let [online (get-online-of world player)
              not-placed (. (icollect [_ card (ipairs online)]
                              (if (not card.circuit-x) card))
                            1)]
          (when (not not-placed)
            (let [cards (icollect [_ entity (ipairs world)]
                          (match entity
                            {:card {:owner player : kind}
                             :circuit-x x
                             :circuit-y y} {: kind
                                                                        : x
                                                                        : y}))]
              (perform-action world {:kind :setup : player : cards})))
          (when not-placed
            (set world.phase.placing not-placed)
            (move not-placed 110 80)
            (set not-placed.card.open true))))
      (each [_ {:circuit-x x :circuit-y y &as cell} (ipairs (get-start-cells-for world
                                                                                 player))]
        (when (= (length (get-cards-on world x y)) 0)
          (add-cell-action world cell
                           (fn []
                             (set world.phase.placing.circuit-x x)
                             (set world.phase.placing.circuit-y y)
                             (set world.phase.placing nil)
                             (remove-actions world))))))))

(fn handle-selected-card [world player selected-card]
  (case selected-card
    (where {: card} (is-online card))
    (do
      (when selected-card.boosted
        (each [_ entity (ipairs world)]
          (match entity
            {:slot {:owner player :kind :line-boost}}
            (slot-action world entity {: player :kind :remove-line-boost}))))
      (each [_ action (ipairs (get-possible-actions (make-state world player)))]
        (match action
          {:kind :move
           :from-x selected-card.circuit-x
           :from-y selected-card.circuit-y
           : to-x
           : to-y} (if (. (get-cell world to-x to-y) :cell :core)
                                 (let [slot (. (icollect [_ entity (ipairs world)]
                                                 (match entity
                                                   {:slot {:owner player
                                                           :kind selected-card.card.kind
                                                           :used nil}} entity))
                                               1)]
                                   (slot-action world slot
                                                {: player
                                                 :x selected-card.circuit-x
                                                 :y selected-card.circuit-y
                                                 :kind :infiltrate}))
                                 (add-cell-action world
                                                  (get-cell world action.to-x
                                                            action.to-y)
                                                  (fn []
                                                    (tset action :player player)
                                                    (perform-action world
                                                                    action)
                                                    (remove-actions world)))))))
    {:card {:kind :fire-wall}}
    (do
      (each [_ entity (ipairs world)]
        (case entity
          (where {: circuit-x : circuit-y : cell}
                 (and (not cell.core) (not cell.exit-for)
                      (not (-?> (get-cards-on world circuit-x circuit-y)
                                (. 1)
                                (. :card :owner)
                                (not= player)))))
          (add-cell-action world entity
                           (fn []
                             (perform-action world
                                             {: player
                                              :kind :fire-wall
                                              :x circuit-x
                                              :y circuit-y})
                             (remove-actions world))))))
    {:card {:kind :404}}
    (do
      (set world.phase.swapping true)
      (each [_ entity (ipairs world)]
        (case entity
          (where {:card {: owner}}
                 (and (= owner player) (not entity.captured)
                      (is-online entity.card)))
          (add-card-action entity
                           (fn []
                             (each [_ other (ipairs world)]
                               (case other
                                 (where {:card {: owner}}
                                        (and (= owner player)
                                             (not= entity other)
                                             (not other.captured)
                                             (is-online other.card)))
                                 (card-action world other
                                              {: player
                                               :kind :404
                                               :xa entity.circuit-x
                                               :ya entity.circuit-y
                                               :xb other.circuit-x
                                               :yb other.circuit-y}))))))))
    {:card {:kind :virus-check}}
    (do
      (each [_ entity (ipairs world)]
        (case entity
          (where {:card {: owner}}
                 (and (not= owner player) (not entity.captured)
                      (is-online entity.card)))
          (card-action world entity
                       {: player
                        :kind :virus-check
                        :x entity.circuit-x
                        :y entity.circuit-y}))))
    {:card {:kind :line-boost}}
    (do
      (each [_ entity (ipairs world)]
        (case entity
          (where {:card {: owner}}
                 (and (= owner player) (not entity.captured)
                      (is-online entity.card)))
          (card-action world entity
                       {: player
                        :kind :line-boost
                        :x entity.circuit-x
                        :y entity.circuit-y}))))))

(fn handle-player-turn [world player]
  (each [_ entity (ipairs world)]
    (match entity
      {:card {:owner player} :captured nil} (add-card-action entity
                                                             (fn []
                                                               (set world.phase.selected-card
                                                                    entity)
                                                               (remove-cell-actions world)))
      {:slot {:kind :fire-wall :owner player :used nil}
       :circuit-x x
       :circuit-y y}
      (slot-action world entity {: player :kind :remove-fire-wall : x : y}))))

(fn player-turn [world]
  "System which adds interface elements for turn actions when it's the
players turn."
  (when (= (. world.players world.phase.player) :player)
    (case world.phase
      {:kind :turn :swapping true}
      nil
      {:kind :turn : player : selected-card}
      (handle-selected-card world player selected-card)
      {:kind :turn : player}
      (handle-player-turn world player))))

{: player-turn : player-setup}