import sumolib
from Car import Car
from Flow import Flow
from carInfo import Ui_carInfo
from varEdit import Ui_varEdit
from PySide6.QtWidgets import QWidget, QLabel, QToolBox
from PySide6.QtCore import Qt, Slot, Signal, QThread
from PySide6.QtCharts import QChart, QSplineSeries, QLineSeries
from math import ceil, dist

class carInfo(QWidget):
    def __init__(self,parent):
        super().__init__()
        self.ui = Ui_carInfo()
        self.ui.setupUi(self)
        self.maxV = 0
        self.parent = parent
        self.pointsCount = [0,0,0]
        self.minX = 0

        self.chart = self.ui.speedGraph.chart()
        #self.chart.setAnimationOptions(QChart.AllAnimations)

        speedsNames = ["vmax","vsec","Vitesse (m.s^-1)"]
        self.speedSeries = []
        for ind,s in enumerate(speedsNames):
            self.speedSeries.append(QLineSeries())
            self.speedSeries[ind].setName(s)
            # self.speedSeries[ind].setPointsVisible()
            self.chart.addSeries(self.speedSeries[ind])
        self.chart.createDefaultAxes()
    
    @Slot(tuple)
    def addSpeedPoint(self,params):
        ind=params[0]
        if self.parent.currentWidget() != self:
            return
        t=params[1]
        val=params[2]
        if(self.pointsCount[ind] > 200):
            self.speedSeries[ind].remove(0)
            self.minX = max(self.minX,self.speedSeries[ind].at(0).x())
            self.pointsCount[ind] -= 1
        self.speedSeries[ind].append(t,val)
        self.pointsCount[ind] += 1

        if self.speedSeries[ind].count()>2:
            i = self.speedSeries[ind].count()-1
            pm2 = self.speedSeries[ind].at(i-2)
            pm1 = self.speedSeries[ind].at(i-1)
            pm0 = self.speedSeries[ind].at(i)
            diffL = (pm1.y() - pm2.y())/(pm1.x() - pm2.x())
            diffR = (pm0.y() - pm1.y())/(pm0.x() - pm1.x())
            diff2 = diffR - diffL
            if diff2 == 0:
                self.speedSeries[ind].remove(i-1)
                self.pointsCount[ind] -= 1

        xAxis = self.chart.axes(Qt.Horizontal,self.speedSeries[ind])[0]
        xAxis.setRange(self.minX,t)

        if val>self.maxV:
            self.maxV = val
            yAxis = self.chart.axes(Qt.Vertical, self.speedSeries[ind])[0]
            yAxis.setMax(ceil(val+1))

        self.ui.speedGraph.update()
    
    @Slot(tuple)
    def setVal(self,params):
        key = params[0]
        val = params[1]
        obj = self.findChild(QLabel,key)
        if obj is None:
            return
        obj.setText(f"{key} : {val}")
        obj.update()

class varEdit(QWidget):
    def __init__(self, parent, carController, varName, value):
        super().__init__()
        self.ui = Ui_varEdit()
        self.ui.setupUi(self)

        self.hookName = varName
        self.ui.name.setText(varName)
        self.ui.value.setValue(value)

        self.CC = carController

        self.ui.value.valueChanged.connect(self.valueChanged)

    def valueChanged(self):
        value = self.ui.value.value()
        self.CC.updateConstant(self.hookName, value)

class CarController:
    def __init__(self, parentMap):
        self.map=parentMap
        self.cars=[]
        self.flows=[]
        self.t=0
        self.dt=1/60
        self.spawnFailed = 0
        self.totalStopped = 0
        self.carsDestroyed = 0
        self.speedPercentageTotal = 0

        self.infoWidget = None

        self.vroomEnable=True

    def addParent(self, mainWindow): 
        self.infoWidget=mainWindow.findChild(QToolBox, "carInfos")
        
        varWidget = mainWindow.ui.constEdit
        varWidget.addWidget(varEdit(varWidget, self, "gamma", 5))
        varWidget.addWidget(varEdit(varWidget, self, "delta", 0.5))
        varWidget.addWidget(varEdit(varWidget, self, "T", 0.3))
        varWidget.addWidget(varEdit(varWidget, self, "size", 3))
        varWidget.addWidget(varEdit(varWidget, self, "dt", self.dt))


    def fromPath(self,path):
        self.cars=[]
        self.flows=[]
        self.t=0
        
        while self.infoWidget is not None and self.infoWidget.count() != 0:
            self.infoWidget.removeItem(0)

        for vehicle in sumolib.xml.parse(path,["vehicle","flow"]):
            route=vehicle.route[0].edges.split()
            if vehicle.name == "vehicle":
                if(self.infoWidget is not None) :
                    wId=self.infoWidget.addItem(carInfo(self.infoWidget), vehicle.id)
                    self.cars.append(Car(vehicle.id,route,vehicle.depart,self.map,self,self.infoWidget.widget(wId)))
                else: 
                    self.cars.append(Car(vehicle.id,route,vehicle.depart,self.map,self,None))
            elif vehicle.name == "flow":
                randomFlow = 0
                if(vehicle.hasChild("param")):
                    params = vehicle.getChild("param")[0]
                    randomFlow = params.getAttributeSecure("value")
                self.flows.append(Flow(vehicle.id, route, vehicle.begin, vehicle.vehsPerHour, randomFlow,self.map, self))

    def prepareRoute(self):
        for car in self.cars:
            car.prepareRoute()

        for flow in self.flows:
            flow.prepareRoute()

    def getCarsOnEdge(self,edgeID):
        return filter(lambda c: c.route[c.index].getID()==edgeID,self.cars)

    def getCarsOnLane(self,edgeID,laneID):
        cars = self.getCarsOnEdge(edgeID)
        return filter(lambda c: c.laneId == laneID,cars)

    def getCarsClose(self, car):
        cars = self.getCarsOnEdge(car.getCurrentEdge().getID())
        return filter(lambda c: dist(c.pos, car.pos) < car.minSpace, cars)

    def update(self):
        if self.map.net is None or self.dt == 0:
            return
        self.t+=self.dt
        for car in self.cars:
            car.update(self.dt)
    
        for flow in sorted(self.flows, key=lambda f: f.priority, reverse = True):
            if flow.shouldSpawn(self.t):
                carsClose = self.getCarsClose(flow.carModel)
                try:
                    next(carsClose)
                except StopIteration:
                    self.cars.append(flow.spawnCar())
                else:
                    self.spawnFailed += 1
                    #flow.addCar2Counter()
                    flow.priority += 1
                    #print(f"nope, y as déjà une voiture ici : n°{self.spawnFailed}")

    def draw(self,painter):
        for ind,car in enumerate(self.cars):
            selectedId = self.infoWidget.currentIndex()
            selectedCar = self.cars[selectedId]
            if ind == selectedId:
                painter.setPen(Qt.green)
            elif(selectedCar.leader is not None and selectedCar.leader.id == car.id):
                painter.setPen(Qt.red)
            else:
                painter.setPen(Qt.white)
            car.draw(painter)

    def destroyCar(self, car):
        self.carsDestroyed += 1
        self.totalStopped += car.timeStopped
        self.speedPercentageTotal += car.speedPercentage / car.ticksLived
        self.cars.remove(car)

    def updateConstant(self, name, val):
        if name == "dt":
            self.dt = val
            return
        for car in self.cars:
            car.__dict__[name] = val

    def getFlowBacklog(self):
        return sum([f.backlog(self.t) for f in self.flows])
