//
//  GameVM.swift
//  DouShouQiIOS
//
//  Created by Pierre FERREIRA on 12/06/2024.
//

import Foundation
import DouShouQiModel
import SpriteKit
import SwiftUI

/**
 * GameVM
 * Cette classe est utilisée pour gérer la logique du jeu
 */
class GameVM : ObservableObject, Identifiable {
    
    @Published var game: Game

    @Published var gameScene: GameScene
    @Published var isGameOver: Bool = false
    
    ///Error
    @Published var hasError : Bool = false
    
    ///Message
    @Published var displayMessage : String = ""
    
    public init(withGame _game : Game, andScene _gameScene : GameScene) {

        self.game = _game
        self.gameScene = _gameScene

        ///Abonnement aux événements
        self.game.addGameStartedListener(onGameStart)
        self.game.addPlayerNotifiedListener(onPlayerNotified)
        self.game.addMoveChosenCallbacksListener(onMoveChosen)
        self.game.addInvalidMoveCallbacksListener(onInvalidMove)
        self.game.addBoardChangedListener(onBoardChanged)
        self.game.addGameOverListener(onGameOver)
        //self.game?.addGameChangedListener({}) - //? cas persistance
        
        self.subscribesToMeeple()
        
        ///Lancement du jeu
        Task{
            try! await self.game.start()
        }
    }
    
    /// Début du jeu
    func onGameStart(board : Board){
        displayMessage = "==>> 🎉 GAME STARTS! 🎉 <<=="
        self.gameScene.displayBoard(board)
    }
    
    /// Notification du joueur
    func onPlayerNotified(board : Board, player : Player) async{
        if player is HumanPlayer {
            displayMessage = "Player \(player.id == .player1 ? "🟡 1" : "🔴 2") - \(player.name), it's your turn!"
        }
        else {
            do{
                _ = try await player.chooseMove(in: board, with: self.game.rules)
            }
            catch{
                hasError = true
            }
        }
    }
    
    /// Mouvement choisi
    func onMoveChosen(board : Board, move : Move, player : Player){
        ///Récupération du Meeple :
        let movedPiece = board.grid[move.rowOrigin][move.columnOrigin]
        let meeples = gameScene.pieces[move.owner]
        
        let meeple = meeples?.first(where: {
            $0.key == movedPiece.piece?.animal
        })
        meeple?.value.cellPosition = CGPoint(x: move.rowDestination, y: move.columnDestination)

    }
    
    ///Mouvement invalide
    func onInvalidMove(board : Board, move : Move, player : Player, result : Bool){
        displayMessage = "⚠️⚠️⚠️⚠️ Invalid Move detected: \(move) by \(player.name) (\(player.id))"

        if result { ///* invalidité terminante
            if let piece = board.grid[move.rowDestination][move.columnDestination].piece{
                let meeples = gameScene.pieces[player.id == .player1 ? .player2 : .player1]
                let meeple = meeples?.first(where: {
                    $0.key == piece.animal
                })
                meeple?.value.parent?.removeChildren(in: [meeple!.value])
            }
            return
        }

        ///* invalidité non terminante
        let piece = board.grid[move.rowOrigin][move.columnOrigin]
        let meeples = gameScene.pieces[move.owner]
        let meeple = meeples?.first(where: {
            $0.key == piece.piece?.animal
        })
        
        meeple?.value.cellPosition = CGPoint(x: move.rowOrigin, y: move.columnOrigin);
    }

    ///Changement de plateau
    func onBoardChanged(board : Board){
        /// Bruit d'un placement de pion ?
    }
    
    ///Fin du jeu
    func onGameOver(board : Board, result : Result, winner : Player?){
        displayMessage = "Game Over!!!"
        isGameOver = true ///Gestion par l'appelant
    }
    
    ///*Meeples
    ///Abonnement aux mouvements des Meeples
    func subscribesToMeeple(){
        for meeple in gameScene.pieces[.player1]!{
            meeple.value.observers.append(meepleMoved)
        }
        for meeple in gameScene.pieces[.player2]!{
            meeple.value.observers.append(meepleMoved)
        }
    }

    ///*Mouvement d'un Meeple
    func meepleMoved(spriteMeeple: SpriteMeeple, startX: Int, startY: Int, endX: Int, endY: Int) async{
        let owner : Owner = game.board.grid[startX][startY].piece!.owner
        let otherPlayer : Owner = self.game.rules.getNextPlayer()
        
        let move: Move = Move(of: owner, fromRow: startX, andFromColumn: startY, toRow: endX, andToColumn: endY)
        if(otherPlayer != owner){ ///Mauvais Joueur pour ce jeton
            onInvalidMove(board: game.board, move: move, player: game.players[otherPlayer]!, result: false)
            return
        }
    
        if let player: HumanPlayer = game.players[owner] as? HumanPlayer{
            try! await player.chooseMove(move)
        }
//        else { ///! IMPOSSIBLE DE CAST LE JOUEUR HUMAIN ==> SIGABRT
//            let player: HumanPlayer = game.players[.player1] as! HumanPlayer
//            try! await player.chooseMove(move)
//        }
    }

}
