<?php

class HauptManager extends IPSModule
{
    public function Create()
    {
        parent::Create();

        // Systemvariablen registrieren
        $this->RegisterPropertyInteger("Peakleistung", 0);
        $this->RegisterPropertyInteger("Ueberschussleistung", 0);
        $this->RegisterPropertyInteger("Netzbezug", 0); // Initialisierung mit 0
        $this->RegisterPropertyString("Verbraucher_Liste", "[]");
        $this->RegisterPropertyInteger("Interval", 3); // Recheninterval

        $this->RegisterVariableInteger("Gesamtnetzbezug", "Gesamtnetzbezug", "", 0);
        $this->RegisterVariableBoolean("Is_Peak_Shaving", "Peakshaving", "",  false); 

        // Timer registrieren
        $this->RegisterTimer("Timer_DistributeEnergy",$this->ReadPropertyInteger("Interval")*1000,"IPS_RequestAction(" .$this->InstanceID .', "DistributeEnergy", "");');
    }

    public function ApplyChanges()
    {
        parent::ApplyChanges();
        $this->SetTimerInterval("Timer_DistributeEnergy",$this->ReadPropertyInteger("Interval")*1000); 

        //Liste aller Verbraucher einlesen
    }

    public function RequestAction($Ident, $Value)
    {
        switch ($Ident) {
            case "DistributeEnergy":
                $this->DistributeEnergy();
                break;
            case "ApplyChanges":
                $this->ApplyChanges();
                break;
            default:
                throw new Exception("Invalid Ident");
        }
    }

    public function DistributeEnergy()
    {
        $Verbraucher_Liste = json_decode($this->ReadPropertyString("Verbraucher_Liste"), true);

        $currentTime = time();
        $Netzbezug = 0;
        $Verbraucher_Liste_Korr = []; 

        foreach ($Verbraucher_Liste as $user) {

           $decodedUser = json_decode(GetValue($user["User_Up"]), true);

            if (isset($decodedUser["Timestamp"]) && ((($currentTime - $decodedUser["Timestamp"])) < 30) && array_key_exists('User', $decodedUser)) {
                
                foreach ($decodedUser["User"] as $subuser) {
                    $subuser['Writeback'] = $user["User_Down"];

                    $Verbraucher_Liste_Korr[0]["User"][] = $subuser;
             
                }
                $Netzbezug += $decodedUser["Netzbezug"];
            }else{

                SetValue($user["User_Down"],'{"Timestamp":'.time().',"Is_Peak_Shaving":'.true.',"User":[]}');
            }
        
        }


        $this->SetValue("Gesamtnetzbezug", $Netzbezug); 


        $Peakleistung = $this->ReadPropertyInteger("Peakleistung");
        $Ueberschussleistung = $this->ReadPropertyInteger("Ueberschussleistung");
        $Is_Peak_Shaving = false; 


        // Fallunterscheidung ob auf Solarladen oder Peakshaving gerregelt wird. 
        if ($Netzbezug < ($Peakleistung + $Ueberschussleistung) / 2) {
            $remainingPower = -1 * (-1 * $Ueberschussleistung + $Netzbezug);
            $Is_Peak_Shaving = false;
        } else {
            $remainingPower = $Peakleistung - $Netzbezug;
            $Is_Peak_Shaving = true;
        }
        $resultArray = [];
        // Alle Energieverbraucher auslesen und dekodieren

        if (empty($Verbraucher_Liste_Korr[0]["User"])) {
            // Liste ist leer, daher nichts zu tun
            IPS_LogMessage("Manager", "aufgerufen leere liste");
            return;
        }

        $allIdle = true; // Variable zur Überprüfung, ob alle Benutzer Idle = true sind
        $totalAktuelle_Leistung = 0; // Variable zur Summierung der Aktuelle_Leistung Werte

        // Fülle das Array mit allen entsprechenden Werten der Verbraucher ab
        foreach ($Verbraucher_Liste_Korr[0]["User"] as $user) {
          
            // Überprüfen, ob alle Benutzer Idle = true sind, wenn einer nicht ist, wird später verworfen...
            if (!$user["Idle"]) {
                $allIdle = false;
                IPS_LogMessage("Manager", "nciht idle");

            }

            // Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen
            //if(in_array(0, $user["PowerSteps"], true)){

                // Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen
                $totalAktuelle_Leistung += ($user["Aktuelle_Leistung"]- $user["Leistung_Delta"]);
    
                //}
            }

        // Berücksichtigung der bereits verteilten Leistungen (nachher kann dafür wieder bei 0 begonnen werden zu verteilen)
        $remainingPower += $totalAktuelle_Leistung;

        // Wenn nicht alle Benutzer Idle = true sind, rufe SetAktuelle_Leistung mit Aktuelle_Leistung Werten auf, (alle Verbraucher behalten die aktuelle Leistung)
        if (!$allIdle || ($Is_Peak_Shaving != $this->GetValue("Is_Peak_Shaving"))) {
            // Schritt 1: Benutzer nach Writeback-Wert aufteilen
            $writebackArrays = [];
            foreach ($Verbraucher_Liste_Korr[0]["User"] as $user) {
                $writeback = $user['Writeback'];
                IPS_LogMessage("Manager", $writeback);

                if (!isset($writebackArrays[$writeback])) {
                    $writebackArrays[$writeback] = [];
                }
                $writebackArrays[$writeback][] = $user;
            }

            // Schritt 2: Foreach-Schleife pro Writeback-Array
            foreach ($writebackArrays as $writeback => $users) {
                $resultArray = [
                    'timestamp' => time(),
                    'Is_Peak_Shaving' => $Is_Peak_Shaving,
                    'User' => []
                ];

                foreach ($users as $user) {
                    $resultArray['User'][] = [
                        'InstanceID' => $user['InstanceID'],
                        'Set_Leistung' => $user['Aktuelle_Leistung']
                    ];
                }

                // Schritt 3: Array in String konvertieren
                $resultString = json_encode($resultArray);

                // Schritt 4: RequestAction aufrufen
                RequestAction($writeback, $resultString);
            }
            $this->SetValue("Is_Peak_Shaving", $Is_Peak_Shaving); 

            return;
        }
        $this->SetValue("Is_Peak_Shaving", $Is_Peak_Shaving); 

        // Sortiere die Verbruacher nach Priorität entweder der PV_Prio oder der Peak Prio
        usort($Verbraucher_Liste_Korr[0]["User"], function ($a, $b) use (
            $Is_Peak_Shaving
        ) {
            $primaryKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio";
                // Wenn die Prio geleich ist, sortiere danach welcher verbraucher bisher am wenigsten Energie bekommen hat.
            if ($a[$primaryKey] == $b[$primaryKey]) {
                return round($a["Bezogene_Energie"]/2000) <=> round($b["Bezogene_Energie"]/2000);
            }
            return $a[$primaryKey] <=> $b[$primaryKey];
        });

        // Primärschlüssel für die Priorität basierend auf dem Parameter auswählen (für sortierung in gruppen anschliessend)
        $priorityKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio";

        // Schleife durch alle Prioritäten
        $priorities = array_unique(
            array_column($Verbraucher_Liste_Korr[0]["User"], $priorityKey)
        );
        $groupedUsers = [];

        foreach ($priorities as $priority) {
            $groupedUsers[$priority] = array_filter(
                $Verbraucher_Liste_Korr[0]["User"],
                function ($user) use ($priority, $priorityKey) {
                    return $user[$priorityKey] == $priority;
                }
            );
        }



        // Jetzt werden die energien pro gruppe verteilt (Immer alle pro prio in einer gruppe miteinander)
        foreach ($groupedUsers as $priority => $users) {
            // Verbraucher mit gleicher Priorität sammeln
            $samePriorityUsers = isset($groupedUsers[$priority])
                ? $groupedUsers[$priority]
                : [];
            

            // Wenn keine Verbraucher mit gleicher Priorität vorhanden sind, überspringen
            if (empty($samePriorityUsers)) {
                continue;
            }

            $withZero = [];
            $withoutZeroHigh = [];
            $withoutZeroLow = [];
            /* Alter Verteilalgor. zu testzwecken auskommentiert, wird dann gelöscht wenns funktioniert.  
            foreach ($samePriorityUsers as $entry) {
                if (min($entry["PowerSteps"]) <= 0) {
                    $withZero[] = $entry;
                } else {
                    $withoutZero[] = $entry;
                }
            }

            // Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
            if (!empty($withoutZero)) {
                foreach ($withoutZero as $entry) {
                    $instanceID = $entry["InstanceID"];
                    $minPowerStep = min($entry["PowerSteps"]);

                    IPS_RequestAction($instanceID,"SetAktuelle_Leistung",$minPowerStep);
                    //$remainingPower -= $entry["Aktuelle_Leistung"];
                }
            }
            */

            /* Neuer Block */
            foreach ($samePriorityUsers as $entry) {
                $withZero[] = $entry;

                if (min($entry["PowerSteps"]) > 0) {
                
                    $withoutZeroHigh[] = $entry;
                }
                if (max($entry["PowerSteps"]) < 0) {
                
                    $withoutZeroLow[] = $entry;
                }
            }
            
            // Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
            if (!empty($withoutZeroHigh)) {
                foreach ($withoutZeroHigh as $entry) {
                    $instanceID = $entry["InstanceID"];
                    $minPowerStep = min($entry["PowerSteps"]);

                    $remainingPower -= $minPowerStep;
                }
            }
                        // Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert
            if (!empty($withoutZeroLow)) {
                foreach ($withoutZeroLow as $entry) {
                    $instanceID = $entry["InstanceID"];
                    $minPowerStep = max($entry["PowerSteps"]);

                    $remainingPower += $minPowerStep;
                }
            }


            /* Neuer Block Ende */ 



            // Nun die verteilen, die 0 erhalten können. 
            $samePriorityUsers = $withZero;
            // Array für die verteilte Energie pro User erstellen
            $userEnergyProv = [];

            foreach ($samePriorityUsers as $user) {
                $userEnergyProv[$user['InstanceID']] = [
                    'user' => $user['InstanceID'],
                    'Writeback' => $user['Writeback'],
                    //'Leistung_Delta' =>  $user["Leistung_Delta"],
                    'Set_Leistung' => 0
                ];
            }

            if($remainingPower>=0){
            // Alle Schritte der Benutzer in einem Array sammeln
            $allSteps = [];
            foreach ($samePriorityUsers as $user) {
                foreach ($user["PowerSteps"] as $step) {
                    $allSteps[] = [
                        "user" => $user["InstanceID"],
                        "Writeback" => $user["Writeback"],
                        "step" => $step
                        //"Leistung_Delta" =>  $user["Leistung_Delta"]
                    ];
                }
            }


            // Sortiere die Schritte nach Größe
            usort($allSteps, function ($a, $b) {
                return $a["step"] <=> $b["step"];
            });


            // Iteriere durch alle Schritte
            foreach ($allSteps as $entry) {
                $user = $entry["user"];
                $manager = $entry["Writeback"];
                $powerstep = $entry["step"];


                $aktleistung = array_filter($userEnergyProv, function($entry2) use ($user, $manager) {

                    return $entry2["user"] == $user && $entry2["Writeback"] == $manager;
                }); 
                IPS_LogMessage("Aktuelle LEistung vor foreahc", print_r($aktleistung)); 

                foreach($aktleistung as $entry){
                    $aktleistung = $entry; 
                }

                IPS_LogMessage("Aktuelle LEistung Mnaagear", print_r($aktleistung)); 
                // Überprüfe, ob noch genügend verbleibende Energie für den nächsten Schritt vorhanden ist
                if ($remainingPower >= $powerstep - $aktleistung['Set_Leistung']) {
                    // Aktualisiere die verbleibende Energie und die bereitgestellte Energie für den Benutzer
                    $remainingPower -= $powerstep - $aktleistung['Set_Leistung'];

                    array_walk($userEnergyProv, function(&$entry3) use ($user, $manager, $powerstep) {
                        if ($entry3["user"] == $user && $entry3["Writeback"] == $manager) {
                            $entry3["Set_Leistung"] = $powerstep;
                        }
                    });                
                } 
          
            }

            // Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt
            foreach ($userEnergyProv as $userInstanceID => $leistung) {
                    $positiveValues = array_filter(
                        array_column(
                            $samePriorityUsers,
                            'PowerSteps',
                            'InstanceID'
                        )[$userInstanceID],
                        function ($l) {
                            return $l >= 0;
                        }
                    );
                    // 2. Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen
                    $fallbackMinimum = empty($positiveValues)
                        ? max(                        array_column(
                            $samePriorityUsers,
                            'PowerSteps',
                            'InstanceID'
                        )[$userInstanceID])
                        : min($positiveValues);

                    // 3. minimalleistung = dieser Fallback
                    $minimalleistung = $fallbackMinimum;

                    // 4. den höheren Wert wählen und für IPS negativieren
                    //$schreibleistung = max($leistung, $minimalleistung);

                    if (abs($leistung["Set_Leistung"]) > abs($minimalleistung)) {
                        $leistung = $leistung["Set_Leistung"];
                    } else {
                        $leistung = $minimalleistung;
                    }
                

               // Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen

                    $resultArray[] = $userEnergyProv[$userInstanceID]; //[
                 //       'InstanceID' => $user['InstanceID'],
                   //     'Writeback' => $user['Writeback'],
                   //     'Set_Leistung' => $leistung
                   // ];

            }
             
                
        }
        else{

            // Alle Schritte der Benutzer in einem Array sammeln
            $allSteps = [];
            foreach ($samePriorityUsers as $user) {
                foreach ($user["PowerSteps"] as $step) {
                    $allSteps[] = [
                        "user" => $user["InstanceID"],
                        "Writeback" => $user["Writeback"],
                        "step" => $step*-1
                        //"Leistung_Delta" =>  $user["Leistung_Delta"]
                    ];
                }
            }


            // Sortiere die Schritte nach Größe
            usort($allSteps, function ($a, $b) {
                return $a["step"] <=> $b["step"];
            });

            $remainingPower = $remainingPower *-1; 

            // Iteriere durch alle Schritte
            foreach ($allSteps as $entry) {
                $user = $entry["user"];
                $manager = $entry["Writeback"];
                $powerstep = $entry["step"];


                $aktleistung = array_filter($userEnergyProv, function($entry2) use ($user, $manager) {


                    return $entry2["user"] == $user && $entry2["Writeback"] == $manager;
                }); 


                foreach($aktleistung as $entry){
                    $aktleistung = $entry; 
                }
              
                // Überprüfe, ob noch genügend verbleibende Energie für den nächsten Schritt vorhanden ist
                if ($remainingPower >= $powerstep - $aktleistung['Set_Leistung']) {
                    // Aktualisiere die verbleibende Energie und die bereitgestellte Energie für den Benutzer
                    $remainingPower -= $powerstep - $aktleistung['Set_Leistung'];

                    array_walk($userEnergyProv, function(&$entry3) use ($user, $manager, $powerstep) {
                        if ($entry3["user"] == $user && $entry3["Writeback"] == $manager) {
                            $entry3["Set_Leistung"] = $powerstep;
                        }
                    });                
                } 
          
            }
            $remainingPower = $remainingPower *-1; 

            // Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt
            foreach ($userEnergyProv as $userInstanceID => $leistung) {
                    $positiveValues = array_filter(
                        array_column(
                            $samePriorityUsers,
                            'PowerSteps',
                            'InstanceID'
                        )[$userInstanceID],
                        function ($l) {
                            return $l <= 0;
                        }
                    );
                    // 2. Falls keine Werte ≥ 0 vorhanden sind, auf 0 zurückfallen
                    $fallbackMinimum = empty($positiveValues)
                        ? min(                        array_column(
                            $samePriorityUsers,
                            'PowerSteps',
                            'InstanceID'
                        )[$userInstanceID])
                        : max($positiveValues);

                    // 3. minimalleistung = dieser Fallback
                    $minimalleistung = $fallbackMinimum;

                    // 4. den höheren Wert wählen und für IPS negativieren
                    //$schreibleistung = max($leistung, $minimalleistung);

                    if (abs($leistung["Set_Leistung"]) > abs($minimalleistung)) {
                        $leistung = $leistung["Set_Leistung"]*-1;
                    } else {
                        $leistung = $minimalleistung;
                    }
                // Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen
                

               // Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen

                    $resultArray[] = $userEnergyProv[$userInstanceID]; //[
                 //       'InstanceID' => $user['InstanceID'],
                   //     'Writeback' => $user['Writeback'],
                   //     'Set_Leistung' => $leistung
                   // ];

            }

        }
        }   

            //IPS_LogMessage("Manager", print_r($resultArray));

            $writebackArrays = [];
            foreach ($resultArray as $user) {
                $writeback = $user['Writeback'];

                if (!isset($writebackArrays[$writeback])) {
                    $writebackArrays[$writeback] = [];
                }
                $writebackArrays[$writeback][] = $user;
            }

            // Schritt 2: Foreach-Schleife pro Writeback-Array
            foreach ($writebackArrays as $writeback => $users) {
                $resultArray = [
                    'Timestamp' => time(),
                    'Is_Peak_Shaving' => $Is_Peak_Shaving,
                    'User' => []
                ];

                foreach ($users as $user) {
                    $resultArray['User'][] = [
                        'InstanceID' => $user['user'],
                        'Set_Leistung' => $user['Set_Leistung']
                    ];
                }

                // Schritt 3: Array in String konvertieren
                $resultString = json_encode($resultArray);

                // Schritt 4: RequestAction aufrufen
                SetValue($writeback, $resultString);
            }

        
        
    }
}
?>