from django.db import models
from .names import WorkerNames, OrderNames
from .timer import Timer
from .timer import Timer
from .company import Company
from .order import Order

# from .action import Action
from .offer import Offer
from ..initialdatabase import InitialDatabase
from .engineeringmodel import (
    ModelChaotic,
    ModelScrum,
    ModelSpiral,
    ModelV,
    ModelWaterfall,
    ModelHeyJoe,
)


# Spielklasse kontrollier das gamegeschehen
class Game(models.Model):
    name = models.CharField(default="Game", max_length=20)
    gamemaster = models.CharField(max_length=20)
    running = models.BooleanField(default=False)
    participants = models.ManyToManyField(Company)
    maxRounds = models.IntegerField(default=10)
    currentRound = models.IntegerField(default=1)
    ordermarket = models.ManyToManyField(Order)
    actionpool = models.ManyToManyField("Action")
    timer = models.ForeignKey(Timer, default=1, on_delete=models.CASCADE)
    acceptedOffers = models.ManyToManyField(Offer, blank=True)
    ordermarketsize = models.IntegerField(default=5)

    def __str__(self):
        return self.name

    # Standard Datenbankeinträge generieren die hart codiert sind (in initialdatabase.py)
    def initializeDatabase(self):
        data = InitialDatabase()
        self.createWorkernames(data)
        self.createOrdernames(data)
        self.createEngineeringmodels(data)

    def createWorkernames(self, data):
        for name in data.workernames:
            WorkerNames.objects.get_or_create(name=name)

    def createOrdernames(self, data):
        for name in data.ordernames:
            OrderNames.objects.get_or_create(name=name)

    def createEngineeringmodels(self, data):
        ModelV.objects.get_or_create(
            name=data.engineeringmodelnames[0],
            description=data.engineeringmodeldescription[0],
            pros=data.engineeringmodelprocon[0][0],
            cons=data.engineeringmodelprocon[0][1],
        )
        ModelSpiral.objects.get_or_create(
            name=data.engineeringmodelnames[1],
            description=data.engineeringmodeldescription[1],
            pros=data.engineeringmodelprocon[1][0],
            cons=data.engineeringmodelprocon[1][1],
        )
        ModelWaterfall.objects.get_or_create(
            name=data.engineeringmodelnames[2],
            description=data.engineeringmodeldescription[2],
            pros=data.engineeringmodelprocon[2][0],
            cons=data.engineeringmodelprocon[2][1],
        )
        ModelScrum.objects.get_or_create(
            name=data.engineeringmodelnames[3],
            description=data.engineeringmodeldescription[3],
            pros=data.engineeringmodelprocon[3][0],
            cons=data.engineeringmodelprocon[3][1],
        )
        ModelChaotic.objects.get_or_create(
            name=data.engineeringmodelnames[4],
            description=data.engineeringmodeldescription[4],
            pros=data.engineeringmodelprocon[4][0],
            cons=data.engineeringmodelprocon[4][1],
        )
        ModelHeyJoe.objects.get_or_create(
            name=data.engineeringmodelnames[5],
            description=data.engineeringmodeldescription[5],
            pros=data.engineeringmodelprocon[5][0],
            cons=data.engineeringmodelprocon[5][1],
        )

    # -----------------------------------------------------------------------------------

    # Bearbeitung des Gameablaufs mit diesen Funktionen kontrollieren:
    def startGame(self):
        # klarmachen dass workernames ordernames engineeringmodels und spezialorders
        # alle definiert sind -> sonst datenbankeinträge generieren
        self.setDefault()
        self.initializeDatabase()
        # spiel auf running stellen
        self.running = True
        self.save()
        # runde starten
        self.startRound()
        # timer starten
        self.runTimer()

    def setDefault(self):
        self.currentRound = 1
        self.save()
        for company in self.participants.all():
            for worker in company.workers.all():
                worker.delete()
            for order in company.orders.all():
                order.delete()
            company.money = 300000
            company.customersatisfaction = 100
            company.workersatisfaction = 100
            company.save()

    def endGame(self):
        # game running False stellen
        self.running = False
        self.save()

    def startRound(self):
        # ordermarket generieren
        self.generateOrdermarket()
        # companys ihren workermarket generieren lassen
        self.generateWorkermarket()

    def processGameround(self):
        # events von LETZTER RUNDE clearen()
        self.clearEvents()
        # arbeiter bezahlen
        self.paySalary()
        # schauen ob worker quittet (zufällig mit steigender wahrscheinlichkeit je niedriger)
        self.checkWorkerQuit()
        # companyOrder verbleibende Workload neu berechnen
        self.calculateOrderWorkload()
        # alle Aktionen ausführen und clearen
        self.executeActions()
        # Krankheit zufällig generieren
        self.calculateSick()
        # unlock locked workers wenn countdown auf 0; dann zähle countdown runter
        self.countdownLockedWorkers()
        # recalculate der models auf orders anwenden
        self.modelsRecalculateOrders()
        # SpecialOrders passieren lassen()
        self.specialordersOccure()
        # beste offer für jede order auf ordermarket rausfinden
        self.calculateBestOffers()
        # offers an beste berechnete company verteilen
        self.distributeBestOffers()
        # passive regeln anpassen
        self.passiveGamerules()
        # checken ob es letzte runde ist
        gameend = self.checkLastRound()
        # runde hochzählen
        self.increaseRound()
        # nächste Runde starten
        self.startRound()
        # gameende signalisieren
        return gameend

    # -----------------------------------------------------------------------------------
    def passiveGamerules(self):
        for company in self.participants.all():
            company.calculateWorkerSatisfaction(2)
            company.calculateCustomerSatisfaction(2)
            for worker in company.workers.all():
                worker.calculateHappyness(1)

    # Spielexecution Funktionen die Funktionen zur Spielkontorlle implementieren
    # adde 5 elemente zu ordermarket
    def generateOrdermarket(self):
        self.clearOrdermarket()
        for x in range(self.ordermarketsize):
            order = Order.createRandom()
            self.addOrder(order)

    def generateWorkermarket(self):
        for company in self.participants.all():
            company.generateWorkermarket()

    def clearParticipants(self, username):
        for player in self.participants.all():
            # alle comapnys ausser gamemaster company kicken
            if not (player.user == username):
                self.participants.remove(player)

    def clearEvents(self):
        for company in self.participants.all():
            company.clearEvents()

    def callbackTimerCompleted(self):
        gameend = self.processGameround()
        if gameend:
            self.timer.stop()

    def runTimer(self):
        self.timer.start(callback=self.callbackTimerCompleted)

    def getRemainingTimer(self):
        return self.timer.remainingtimer

    def checkWorkerQuit(self):
        for company in self.participants.all():
            company.checkWorkerQuit()

    def countdownLockedWorkers(self):
        for company in self.participants.all():
            for worker in company.workers.all():
                # unlocken wenn locked und countdown = 0 und setze sick false
                if worker.lockedRounds <= 0 and worker.status == 3:
                    worker.setStatus(1)
                    worker.setSick(False)
                # countdown lockedRounds wenn größer 0
                elif worker.lockedRounds > 0:
                    rounds = worker.lockedRounds - 1
                    worker.setLockedRounds(rounds)

    def calculateSick(self):
        for company in self.participants.all():
            for worker in company.workers.all():
                worker.calculateSick()

    def executeActions(self):
        for company in self.participants.all():
            for action in company.actions.all():
                action.execute()
            for action in company.actions.all():
                action.delete()

    def calculateOrderWorkload(self):
        for company in self.participants.all():
            company.calculateOrderWorkload()

    def calculateBestOffers(self):
        for order in self.ordermarket.all():
            bestoffer = order.calculateBestOffer()
            self.acceptedOffers.add(bestoffer)

    def modelsRecalculateOrders(self):
        for company in self.participants.all():
            for order in company.orders.all():
                order.recalculate(company)

    def distributeBestOffers(self):
        for acceptedoffer in self.acceptedOffers.all():
            acceptedoffer.order.setStatus(1)
            self.ordermarket.remove(acceptedoffer.order)
            acceptedoffer.company.addOrder(acceptedoffer.order)
            acceptedoffer.offerAcceptedEvent()
            # offers deleten da nicht mehr gebraucht
            acceptedoffer.delete()

    def specialordersOccure(self):
        for company in self.participants.all():
            for order in company.orders.all():
                order.specialorder.occure(company=company)

    def increaseRound(self):
        self.currentRound += 1
        self.save()

    def checkLastRound(self):
        if self.maxRounds <= self.currentRound:
            self.endGame()  # beende spiel
            return True
        return False

    def addOrder(self, order):
        self.ordermarket.add(order)

    def removeOrder(self, order):
        self.ordermarket.remove(order)

    def clearOrdermarket(self):
        for order in self.ordermarket.all():
            if order.status == 0:
                order.delete()
        self.ordermarket.clear()

    def paySalary(self):
        for company in self.participants.all():
            company.paySalary()

    def createLeaderboard(self):
        tuples = []
        for company in self.participants.all():
            tuples.append((company.money, company))
        tuples = sorted(tuples, key=lambda x: x[0], reverse=True)
        # companys aus den tuples ziehen
        leaderboard = []
        i = 1
        for element in tuples:
            leaderboard.append((i, element[1]))
            i = i + 1

        return leaderboard
