package me.msoucy.ptures.controller

import me.msoucy.ptures.model.*
import me.msoucy.ptures.model.Target
import me.msoucy.ptures.view.BattleView
import me.msoucy.ptures.view.CreatureView
import me.msoucy.ptures.model.SkillChoice

sealed class BattleType(vararg val teams: Team) {
    init {
        assert(teams.isNotEmpty())
    }

    abstract val numGroups: Int
}

sealed class IndividualBattle(vararg teams: Team) : BattleType(*teams) {
    override val numGroups: Int
        get() = teams.size
}

class SingleBattle(vararg teams: Team) : IndividualBattle(*teams)
class DoubleBattle(vararg teams: Team) : IndividualBattle(*teams)
class TripleBattle(vararg teams: Team) : IndividualBattle(*teams)
class TeamBattle(override val numGroups: Int, vararg teams: Team) : BattleType(*teams)
class RaidBattle(val boss: Creature, vararg teams: Team) : BattleType(*teams) {
    override val numGroups: Int
        get() = 2
}


class Engine(private val battle: BattleType) {

    private lateinit var currentCreature: CreatureView

    // List of all creatures that are involved in the battle
    private val creatures get() = battle.teams.flatMap { it.activeCreatures }

    // The current attacking creature
    private var attacker: CreatureView
        get() {
            return currentCreature
        }
        private set(value) {
            currentCreature = value
        }

    private val activeCreatures
        get() = creatures
                .filter { !it.creature.hasStatus<KnockedOut>() }
                .sortedBy { it.creature.spd }

    fun resolveTurn(view: BattleView) {

        // All preconditions
        for (creature in activeCreatures) {
            for (status in creature.creature.statuses) {
                status.onTurnStart(creature.creature)
            }
        }

        // Get the moves each creature will use this turn
        activeCreatures.forEach {
            it.chooseSkill(activeCreatures.map { c -> c.creature })
        }

        // Resolve each move
        for (c in activeCreatures) {
            // Resolve move
            val (skill, targets) = c.creature.activeSkill!!
            currentCreature = c
            val attackingCreature = currentCreature
            for (step in skill.damageSteps) {
                for (targetCreature in targets) {
                    if (attackingCreature.creature.hits(targetCreature, step)) {
                        targetCreature.apply(step, attackingCreature.creature)
                        step.applyStatus(targetCreature)
                    }
                }
            }
            for (step in skill.postSteps) {
                for (targetCreature in targets) {
                    step.apply(targetCreature)
                }
            }
        }

        // All post conditions
        for (creatureView in activeCreatures) {
            val creature = creatureView.creature
            for (status in creature.statuses) {
                status.onTurnEnd(creature)
            }
        }
    }

    private fun getTargetList(target: Target, selected: Creature): List<Creature> = when (target) {
        Target.Self -> listOf(attacker.creature)
        Target.Selected -> listOf(selected)
        Target.Others -> creatures.filter { it.creature != attacker.creature }.map { it.creature }
        Target.Opponents -> creatures.filter { it.playerId != attacker.playerId }.map { it.creature }
        Target.All -> creatures.map { it.creature }
    }
}