<?php
//exit("sjhdhjd");
//function to check if cahnges made on scores (puting old structure into considiration for backward compactibility)

function IsChangesMade($scoreb4 = "",$ScoreArr = []){
    $scoreb4 = explode("|",$scoreb4);
   //check if no changes found - if normal save, and CA and Exam are the same (cos the system will use the result info so there will be no change)
   //check if changes made
   $changesmade = false;
   //get the scoreb4 scores
   if(strpos($scoreb4[0],",") !== false){ // if new score structure (>=v4)
      //get all scores
      $Scoreb4Arr = explode(",",$scoreb4[0]);
      if(count($Scoreb4Arr) == count($ScoreArr)){ //if same signature
         //confirm individual score
         foreach($Scoreb4Arr as $scind=>$Scoreb4Val){
           if((float)$Scoreb4Val != $ScoreArr[$scind]){
            $changesmade = true; break;
           }
         }
      }else{
        $changesmade = true;
      }
  
   }else{ //if old structure
    if(count($ScoreArr) == 2){ //CA and Exam
      if($ScoreArr[0] != (float)$scoreb4[0] || $ScoreArr[1] != (float)$scoreb4[1]){
        $changesmade = true;
      }
    }else{
      $changesmade = true;
    }
   }
   return $changesmade;
  }

  $dump = '';
$errs = 0; $fix =0; $note = 0;$rsthtml='';

  //function to write response
function Push($txt,$err = ''){
    global $dump; global $errs; global $fix; global $note; global $fixnow;
    $cnt = "";
    $logo = "terminal";
    if($err == "err"){
        $errs++;
      $cnt = "<strong>(Issue ".$errs.")</strong>";
      $logo = "ban";
    }else if($err == "ok"){
        $fix++;
      $cnt = "<strong>(Fix ".$fix.")</strong>";
      $logo = $fixnow?"check":"exclamation-triangle";
    }else if($err == "note"){
        $note++;
        $cnt = "<strong>(Note ".$note.")</strong>";
        $logo = "list-alt";
    }
    $dump .= '<div class="'.$err.'"><i style="opacity:0.5" class="fa fa-'.$logo.'"></i>&nbsp;&nbsp;&nbsp;'.$txt.' '.$cnt.'</div>';
}
  
  //function to update repeat courses field
  function UpdateRepeat($Rept,$grds,$processNumFailed = true,$CID="",$CDet =[]){
   // return "aaaa";
   // global $grds;
   //$processNumFailed => CourseNotExistInRst Insert
    global $courseID; //the course id to update in repeat
    global $courseDet; //the details of the course
   // global $Lvl; 
    //global  $Sem;
    //global  $studyID;
    $courseIDn = $CID == ""?$courseID:$CID; //if sent into the function use the sent else use the global one
    $courseDet = count($CDet) == 0?$courseDet:$CDet; //if sent into the function use the sent else use the global one
    //break down the current repeat string to get the old repeats + new repeats
    $ReptArr = explode('+',$Rept);
   
    //$grds -> carry the current grade details (from db) the student have for the coures, sent to the function
    //      -> structure is `ID`, `Grade`, `Level`, `Desc`, `Status`, `PASS`(0|1), `RstInfoID`
  
    if((int)$grds['PASS'] > 0){ //if passed
             //remmove in new reapet if exist;
             $NewRepeat = "";
             if(isset($ReptArr[1])){
               //replace course code in new string to empty
              $NewRepeat = str_replace(":$courseIDn:","",$ReptArr[1]);
             }
             //reform the repeat string
             $Rept = $ReptArr[0]."+".$NewRepeat;
             
             }else{ //if failed
               $allowfail = (int)$courseDet['AllowFail']; // number of times a student can fail the course
               if($allowfail != 0){ //if student not allow to resit atall i.e number of fail is 0 dont insert in repeat
                //add to new repeat
                if(isset($ReptArr[1])){
                  
                  // echo "@NewRept=".$ReptArr[1]."@";
                    if(strpos($ReptArr[1],":$courseIDn:") === false || ($allowfail < 0 && $processNumFailed)){ //if not exist in repeat or student can fail the course (allow to resit) at any number of times (i,e $allowfail is negative ) and the course result is just inputed the first time
                      $ReptArr[1] .= ":$courseIDn:";
                    }else{//if exist in repaet
                     if($processNumFailed){ //if insert operation i.e result inserted newly check allowable number of fail
  
                      //check the number of times it exixt in old repeat string
                      $numocc = substr_count($ReptArr[0],":$courseIDn:");
  
                      if($numocc >= ($allowfail - 1)){ //if student has reach number of alowable fail, remove all occurence from repeat
                        //remove it totally from new repeat string
                       $ReptArr[1] = str_replace(":$courseIDn:","",$ReptArr[1]);
  
                      }else{ //if not ecceded number of fails add it
                       $ReptArr[1] .= ":$courseIDn:";
                      }
                     }
                      
                    }
                }else{ //if new repeat string not exist, add it
                  $ReptArr[1] = ":$courseIDn:";
                }
               }
               //reform the repeat string
               $Rept = $ReptArr[0]."+".$ReptArr[1];
             }
             
      return $Rept; //updated repeat
  }


  //function to form rstinfo by the result string
function FormRstInfo($Rst){
    if(trim($Rst) == "")return ''; //if no result found return empty detials
    global $dbo;
    //make sure result is arraay
    $crstArr = is_array($Rst)?$Rst:$dbo->DataArray($Rst);
    if(count($crstArr) < 1)return ''; //if empty array return empty detials
    $RstInfo = []; //will hold the result details (each course)
    
               //loop through all result and form the result info
               foreach($crstArr as $CID => $ScoreDet){
                 //get the course details
                 $cdet = $dbo->SelectFirstRow("course_tb c, coursegroup_tb cg","c.*,cg.AllowFail","c.CourseID=$CID AND c.GroupID = cg.ID LIMIT 1");
                 if(is_array($cdet)){ //if course details found
                  $RstInfo[$CID] = ["CourseCode"=>$cdet['CourseCode'],"CourseStatus"=>$cdet['CourseStatus'],"Title"=>$cdet['Title'],"Lvl"=>$cdet['Lvl'],"DeptID"=>$cdet['DeptID'],"Sem"=>$cdet['Sem'],"CH"=>$cdet['CH'],"Elective"=>$cdet['Elective'],"StudyID"=>$cdet['StudyID'],"GroupID"=>$cdet['GroupID'],"StartSesID"=>$cdet['StartSesID'],"EndSesID"=>$cdet['EndSesID'],"AllowFail"=>$cdet['AllowFail']];
                 }
               }
        return json_encode($RstInfo);
  }

  //function to update result info details
function UpdateRstInfo($RstInfo,$courseDet){
    $RstInfoArr = json_decode($RstInfo,true);  //decode to an array
               $RstInfoArr = !is_array($RstInfoArr)?[]:$RstInfoArr;
               //Update it if exist, add if not exist
               $RstInfoArr[$courseDet['CourseID']] = ["CourseCode"=>$courseDet['CourseCode'],"CourseStatus"=>$courseDet['CourseStatus'],"Title"=>$courseDet['Title'],"Lvl"=>$courseDet['Lvl'],"DeptID"=>$courseDet['DeptID'],"Sem"=>$courseDet['Sem'],"CH"=>$courseDet['CH'],"Elective"=>$courseDet['Elective'],"StudyID"=>$courseDet['StudyID'],"GroupID"=>$courseDet['GroupID'],"StartSesID"=>$courseDet['StartSesID'],"EndSesID"=>$courseDet['EndSesID'],"AllowFail"=>$courseDet['AllowFail']];
               return json_encode($RstInfoArr);
  }

  //function to recalculate Result
function Recalculate($RstDet,$update = true,$upward = true,$prevRept = NULL,$RstInfoIDS = 0,$prevOut = NULL){
    //$update => help to determine if the query formed is to update an existing result or adding new result
    //$upward => help to determine all other student result upward is to be rcalculated as well
    //Push("Starting Recalculation ")
    global $dbo;
    global $schgrdstr;
    global $grdstr;
    global $classPassStr;
    global $classPassDetAll;
    global $updatetype; //determine if normal (0=>use result details) save or update save (1=>use global details)
    if(!isset($updatetype))$updatetype=0;
    global $RstInfoID;
    
    if($RstInfoIDS > 0)$RstInfoID=$RstInfoIDS; //if sent use the sent one
    if(!isset($RstInfoID))$RstInfoID=1; //if not set use 1
  
    if(!isset($grdstr)){
       $grdstr = $dbo -> SelectFirstRow("resultinfo_tb","","ID = ".$RstInfoID);
       $classPassStr = $grdstr['ClassOfPass'];
       $grdstr = is_array($grdstr)?$grdstr[1]:""; //The Grading e,g 34-60=2&<34=1
    }
    //global $courseID;
   if(!isset($schgrdstr))$schgrdstr = GetGradeDetAll($RstInfoID);
   if(!isset($classPassDetAll))$classPassDetAll = GetClassPassDetAll($RstInfoID);
    
  //check rst brouth forward is 0 and not the first result and not a upper result calculation, get it from imdiate previous result
  if(($RstDet['BCH'] == 0 || $RstDet['BGP'] == 0) && ($RstDet['Lvl'] > 1 || ($RstDet['Lvl'] == 1 && $RstDet['Sem'] > 1) ) && $upward == true){ //if not first result and no brought forward
    //get the imediate previos result
  $imprev = $dbo->Select("result_tb","","((Lvl = {$RstDet['Lvl']} && Sem < {$RstDet['Sem']}) OR Lvl < {$RstDet['Lvl']}) AND RegNo = '{$RstDet['RegNo']}' ORDER BY Lvl DESC, Sem DESC, GroupID DESC LIMIT 1");
  //if imediate previous Found
  if(is_array($imprev) && $imprev[1] > 0){
    //return ["SubQuery"=>"Prev seen"];
    $imprev = $imprev[0]->fetch_assoc();
    //Update the brouth forwards
    $RstDet['BCH'] = $imprev['CCH'];
    $RstDet['BGP'] = $imprev['CGP'];
  }
  }
  //$GPA = $TCH > 0?number_format($TGP/$TCH,2):"0.00";
          $CGPA = (float)$RstDet['BCH'] > 0?(float)$RstDet['BGP']/(float)$RstDet['BCH']:0;
  
          //set default for the proposed new result calculation
  $rtn = ["TCH"=>0,"TGP"=>0,"OTS"=>0,"OAS"=>0,"CCH"=>$RstDet['BCH'],"CGP"=>$RstDet['BGP'],"BCH"=>$RstDet['BCH'],"BGP"=>$RstDet['BGP'],"SubQuery"=>"","GPA"=>0,"CGPA"=>$CGPA];
    $TCCH =0; $TGP =0;
    
    //return ["SubQuery"=>$dbo->DataString($RstDet['Rst'])];
  
    //check the Result type array or string, makes sure is an array 
    $Rst = is_array($RstDet['Rst'])?$RstDet['Rst']:$dbo->DataArray($RstDet['Rst']);
  
    //if(count($Rst) < 1)return $rtn; //return empty result
  
  //makes sure the result info is an array  
  $RstInfo = is_array($RstDet['RstInfo'])?$RstDet['RstInfo']:is_string($RstDet['RstInfo']) && trim($RstDet['RstInfo']) != ""?json_decode($RstDet['RstInfo'],true):[];
  $RstInfo = is_null($RstInfo)?[]:$RstInfo;
  
  $newRstInfo = []; //will hold the end result info array
  $Rept = ""; //will hold the end result repeat string
  $Outs = ""; //will hold the end result repeat string
  
  //Forming new reapet string
  if(!is_null($prevRept)){ //if previous repet record sents (meaning repet need modification)
    $preptarr = explode("+",$prevRept); //get old and new string in array
  if(count($preptarr) == 1){ //if only old found, pack all old as new
    $Rept = $preptarr[0] ."+".$preptarr[0];
  }else if(count($preptarr) > 1){ //if new found, make the new to be both old and new (forming a new result)
    $Rept = $preptarr[1] ."+".$preptarr[1];
  }
  }

  //Forming new otstanding string
  /* if(!is_null($prevOut)){ //if previous repet record sents (meaning repet need modification)
    $pOutarr = explode("+",$prevOut); //get old and new string in array
  if(count($pOutarr) == 1){ //if only old found, pack all old as new
    $Outs = $pOutarr[0] ."+".$pOutarr[0];
  }else if(count($pOutarr) > 1){ //if new found, make the new to be both old and new (forming a new result)
    $Outs = $pOutarr[1] ."+".$pOutarr[1];
  }
  } */
  
  $newrststr = []; //will hold updated result
  $TRC = 0; //total number of valid student result
    //loop through each result and calculate (main)
    foreach($Rst as $CID => $Score){
      
      $newrststr[$CID] = $Score;
      //get the individual score
        $indscore = explode("|",$Score);
        if(count($indscore) >= 2){ //if valid rst
          $TRC++;
          //#$#$
          //check if old version structure
          if(strpos($indscore[0],",") === false){
            //convert to new structure
            $indscore[0] = $indscore[0].",".$indscore[1]; //form the scores
            $indscore[1] = "0,0"; //form the max scroes 
          }
           $Tot = array_sum(explode(",",$indscore[0])); //total score
  //if(count($indscore) > 2){ 
    //if the grade details exist
        if(isset($RstInfo[$CID]) && (int)$updatetype < 1 && isset($RstInfo[$CID]["GradeStruc"])){ //if to update to result details and rst info exit
          $grds = GetGradeFromString($Tot,$RstInfo[$CID]["GradeStruc"]);
        }else{
          $grds = GetGrade($Tot,$grdstr,$schgrdstr,$RstInfoID);
        }
        
  //}else{
  
  //}
          
          if(isset($RstInfo[$CID]) && (int)$updatetype < 1){ //if course details exist and update type is to update to result details
            $cdet = $RstInfo[$CID];
          }else{
            $cdet = $dbo->Select("course_tb c, coursegroup_tb cg","c.*,cg.AllowFail","c.CourseID=$CID AND c.GroupID = cg.ID LIMIT 1");
            if(is_array($cdet) && $cdet[1] > 0){
              $cdet = $cdet[0]->fetch_assoc();
            //get the current grading structure
             $ngrdstruc = GetGrade(-1,$grdstr,$schgrdstr,$RstInfoID);
             $cdet["GradeStruc"] = $ngrdstruc;
            }
          }
        
          
  //get course CH
  $CH = 1;
  if(is_array($cdet)){
    $CH = $cdet['CH'];
  }
          $GP = $CH * $grds['Level'];
          $rtn['TCH'] += $CH;
          $rtn['TGP'] += $GP;
          $rtn['OTS'] += $Tot;
  
          //Add the result details to RstInfo
         
          /* $cdet['CA'] = $indscore[0];
          $cdet['Exam'] = $indscore[1]; */
           $cdet['Scores'] = $indscore[0];
          $cdet['MaxScores'] = $indscore[1];
          
          // $cdet['Total'] = (float)$indscore[0] + (float)$indscore[1];
          // $cdet['Total'] = array_sum(explode(",",$cdet['Scores']));
          $cdet['Total'] = $Tot;
          $cdet['Point'] = $grds['Level'];
          $cdet['Grade'] = $grds['Grade'];
          $cdet['GradeName'] = $grds['Desc'];
          $cdet['Pass'] = $grds['PASS'];
          $cdet['GradePoint'] = $GP;
  
  
          $newRstInfo[$CID] = $cdet;
          //process Repeat
  //if(trim($Rept) != ""){
    
    $Rept = UpdateRepeat($Rept,$grds,true,$CID,$cdet);
  
    
  //}
  
  $newrststr[$CID] = $indscore[0]."|".$indscore[1]."|".$grds['Level']."|".rawurlencode($grds['Grade'])."|".rawurlencode($grds['Desc'])."|".$grds['PASS']."|".$CH."|".$GP;
  
        }
    }
  
    if(trim($Rept) != ""){
      $RstDet['Rept'] = $Rept;
    }
    Push($RegNo." Pre Outstanding => ".$prevOut,"err");
    list($Outs,$curUnreg) = UpdateOutstand($RstDet,NULL,$prevOut); //return the formed accumulated db otstanding string and the current only
    Push($RegNo." Post Outstanding => ".$Outs,"ok");
    //if(trim($Outs) != ""){
        $RstDet['Outst'] = $Outs;
    //}
    //update 
   // return ["SubQuery"=>json_encode($Rst)];
  //calculate the cumulative
    $rtn['CCH'] = $rtn['TCH'] + $rtn['BCH'];
    $rtn['CGP'] = $rtn['TGP'] + $rtn['BGP'];
    $rtn['GPA'] = $rtn['TCH'] > 0?$rtn['TGP']/$rtn['TCH']:0;
    $rtn['CGPA'] = $rtn['CCH'] > 0?$rtn['CGP']/$rtn['CCH']:0;
    $rtn['OAS'] = $TRC > 0?$rtn['OTS']/$TRC:0;
  
    //for internal update, reset the class of pass parameters to interner record
  if((int)$updatetype < 1 && isset($RstDet['COPRule']) && !is_null($RstDet['COPRule']) && trim($RstDet['COPRule']) != "" && isset($RstDet['COPDetails']) && !is_null($RstDet['COPDetails']) && trim($RstDet['COPDetails']) != ""){
    $classPassStr = $RstDet['COPRule'];
    $classPassDetAll = json_decode($RstDet['COPDetails'],true);
  }
    $classPassdet = GetClassPass($rtn['CGPA'],$classPassStr,$classPassDetAll,$RstInfoID);
    $classOfPass = is_array($classPassdet)?$classPassdet['ClassName']:"";
  
    $query = "";
    //$RstDet['Rst'] = is_array($RstDet['Rst'])?$dbo->DataString($RstDet['Rst']):$RstDet['Rst'];
    $RstDet['Rst'] = $dbo->DataString($newrststr,false);
    
    //$newrststr
    if($update){ //if update
      $query .= "UPDATE result_tb SET Rst = '".$dbo->SqlSafe($RstDet['Rst'])."', Rept='{$RstDet['Rept']}', Outst='{$RstDet['Outst']}', RstInfo='".$dbo->SqlSafe(json_encode($newRstInfo))."', TCH={$rtn['TCH']} , TGP={$rtn['TGP']}, BCH={$rtn['BCH']} , BGP={$rtn['BGP']} , CGP={$rtn['CGP']} , CCH={$rtn['CCH']}, GPA={$rtn['GPA']}, CGPA={$rtn['CGPA']},TRC=$TRC , COP='".$dbo->SqlSafe($classOfPass)."', COPRule='".$dbo->SqlSafe($classPassStr)."', COPDetails='".$dbo->SqlSafe(json_encode($classPassDetAll))."', OTS={$rtn['OTS']}, OAS={$rtn['OAS']}, RstInfoID=$RstInfoID WHERE ID = ".$RstDet["ID"].";";
    /*   if($RstDet['RegNo'] == 'AK15/NAS/BIO/081'){
        exit("UPDATE result_tb SET Rst = '".$dbo->SqlSafe($RstDet['Rst'])."', Rept='{$RstDet['Rept']}', RstInfo='".$dbo->SqlSafe(json_encode($newRstInfo))."', TCH={$rtn['TCH']} , TGP={$rtn['TGP']}, BCH={$rtn['BCH']} , BGP={$rtn['BGP']} , CGP={$rtn['CGP']} , CCH={$rtn['CCH']} WHERE ID = ".$RstDet["ID"].";");
      } */
    }else{ //if insert
      $query .= "INSERT INTO result_tb (`RegNo`, `Lvl`, `Sem`, `SesID`, `Rst`, `Outst`,`Rept`,`GroupID`,`CGP`,`CCH`,`TGP`,`TCH`,`BGP`,`BCH`,RstInfo,GPA,CGPA,TRC,COP,COPRule,COPDetails,OTS,OAS,RstInfoID) VALUES ('{$RstDet['RegNo']}',{$RstDet['Lvl']},{$RstDet['Sem']},{$RstDet['SesID']},'".$dbo->SqlSafe($RstDet['Rst'])."', '{$RstDet['Outst']}', '{$RstDet['Rept']}', {$RstDet['GroupID']},{$rtn['CGP']} , {$rtn['CCH']},{$rtn['TGP']},{$rtn['TCH']},{$rtn['BGP']},{$rtn['BCH']},'".$dbo->SqlSafe(json_encode($newRstInfo))."', {$rtn['GPA']},{$rtn['CGPA']},$TRC,'".$dbo->SqlSafe($classOfPass)."','".$dbo->SqlSafe($classPassStr)."','".$dbo->SqlSafe(json_encode($classPassDetAll))."',{$rtn['OTS']},{$rtn['OAS']},$RstInfoID);";
    }
  
   
    //check down result calculation
  if($upward){
   
    //get all upward resultfrom the current result
  $upwardRst = $dbo->Select("result_tb","","((Lvl = {$RstDet['Lvl']} AND Sem > {$RstDet['Sem']}) OR Lvl > {$RstDet['Lvl']}) AND RegNo = '{$RstDet['RegNo']}' ORDER BY Lvl ASC,Sem ASC,GroupID DESC");
  
  if(is_array($upwardRst) && $upwardRst[1] > 0){
    $GroupManage = [];
    $CarryBCH = $rtn['CCH'];$CarryBGP = $rtn['CGP'];$PreRepeat = $RstDet['Rept'];$PreOuts = $RstDet['Outst'];
    //return ["SubQuery"=>$RstDet['RegNo'] . " ; ".$upwardRst[1]];
     while($upwrst = $upwardRst[0]->fetch_assoc()){ //loop trough all the upward results
      $rstsign = $upwrst['Lvl']."_".$upwrst['Sem']."_".$upwrst['SesID'];
      if(in_array($rstsign,$GroupManage))continue; //if a copy of the result already seen means this one is outdatated copy, skip it;
      $GroupManage[] = $rstsign;
      $upwrst['BCH'] = $CarryBCH; $upwrst['BGP'] = $CarryBGP; //update the Brought forwards
      
  //recalculate the result
  //use the result RstInfoID if exist, else use the Main RstInfoID;
  $WRstInfoID = (int)$upwrst['RstInfoID'] == 0?$RstInfoID:(int)$upwrst['RstInfoID'];
  
        $newrst =  Recalculate($upwrst,true,false,$PreRepeat,$WRstInfoID,$PreOuts); //recalculate the result only
        
        //exit(json_encode($newrst));
        $query .= $newrst["SubQuery"]; //get the update query
        //return ["SubQuery"=>json_encode($upwrst)];
        $CarryBCH = $newrst['CCH'];$CarryBGP = $newrst['CGP']; //set the next Brought forward to the current cummulatives
        $PreRepeat = $newrst['Rept'];
        $PreOuts = $newrst['Outst'];
     }
  }
  
  //$query
  
  }
  
  
  //add the query
  $rtn["SubQuery"] = $query;
  $rtn["Rept"] = $RstDet['Rept'];
  
  //exit(json_encode($rtn));
  return $rtn;
  
  }

//global cache
$Cache = [];

  function UpdateOutstand($studrst,$studDet=NULL,$prevOut=NULL){
    
    /* $outsrtarr = explode("+",$outsrt);
    if(!isset($outsrtarr[1]))$outsrtarr[1] = $outsrtarr[0]; */
      global $dbo; global $Cache;
    $RegNo = $studrst['RegNo']; //get the student regno
    //Push("$RegNo Result (".$studrst['ID'].") => Fixing Unregistered Courses","note");
    if($prevOut == NULL){
        $prevOut = "";
        //get the student imm prev result
        //get the imediate previos result
  $imprev = $dbo->SelectFirstRow("result_tb","","((Lvl = {$studrst['Lvl']} && Sem < {$studrst['Sem']}) OR Lvl < {$studrst['Lvl']}) AND RegNo = '".$dbo->SqlSafe($RegNo)."' ORDER BY Lvl DESC, Sem DESC, GroupID DESC LIMIT 1");
  if(is_array($imprev)){
    $prevOut = $imprev['Outst']; 
  }
    }

    //get unregistered courses
$unregcourses = '';
$outs = '';
    if(trim($prevOut) == ""){
        $outs = '+';
    }else{
        $prevOutArr = explode("+",$prevOut);
        if(isset($prevOutArr[1])){
            $outs = $prevOutArr[1]."+".$prevOutArr[1];
        }else{
            $outs = $prevOutArr[0]."+".$prevOutArr[0];
        }
    }

    //if not sent but already in cache
    if(is_null($studDet) && isset($Cache[str_replace("/","_",$RegNo)."_Det"])){
        $studDet = $Cache[str_replace("/","_",$RegNo)."_Det"];
    }
    if(is_null($studDet)){//if student details not send and not in cache
        //get the required student details
        $studDet = $dbo->SelectFirstRow("studentinfo_tb","ProgID,StudyID,ClassID","RegNo='".$dbo->SqlSafe($RegNo)."' OR JambNo = '".$dbo->SqlSafe($RegNo)."' LIMIT 1",MYSQLI_ASSOC);
        if(!is_array($studDet)) return "";
        //keep a copy in cache
        $Cache[str_replace("/","_",$RegNo)."_Det"] = $studDet;
    }

    //Registered courses management
    $dtudregCourses = NULL;
    $cregkey = "creg_".str_replace("/","_",$RegNo)."_".$studrst['Lvl']."_".$studrst['Sem']."_".$studrst['SesID']; //cache key
    if(isset($Cache[$cregkey])){ //if already in cache
        $dtudregCourses = $Cache[$cregkey]; //use the cache copy
    }else{
        //if not in cache get from database
        $cregquery = "SELECT * FROM coursereg_tb WHERE RegNo='".$dbo->SqlSafe($RegNo)."' AND Lvl = {$studrst['Lvl']} AND Sem = {$studrst['Sem']} AND SesID = {$studrst['SesID']} UNION SELECT * FROM coursereg_tb WHERE RegNo='".$dbo->SqlSafe($RegNo)."' AND Lvl = {$studrst['Lvl']} AND Sem = {$studrst['Sem']} LIMIT 1";
        
        $studregcourses = $dbo->RunQuery($cregquery);
        if(is_array($studregcourses) && $studregcourses[1] > 0){
            $dtudregCourses = $studregcourses[0]->fetch_assoc();
            //save it in cache
            $Cache[$cregkey] = $dtudregCourses;
        }
    }
    //get the student registered courses
    //$dtudregCourses = $dbo->SelectFirstRow("coursereg_tb","","RegNo='".$dbo->SqlSafe($RegNo)."' AND Lvl = {$studrst['Lvl']} AND Sem = {$studrst['Sem']}");

//Get all Semester Courses 
//cach key
/* $allsemcoukey = $studDet['ProgID']."_".$studrst['Lvl']."_".$studrst['Sem'];
if(isset($Cache[$allsemcoukey])){ //if already in cache
    $SemCourses = $Cache[$allsemcoukey];
}else{
 $SemCourses = GetCourses($studDet['ProgID'],$studrst['Lvl'],$studrst['Sem']);
 $Cache[$allsemcoukey] = $SemCourses;
}
    

 $CoursesNow = $SemCourses['Current'];
 $OldCourses = $SemCourses['Old']; */
    

//variable to hold registered courses
$regcourses = [];

    if(!is_array($dtudregCourses)){ //if course reg not found
       // Push("$RegNo Result (".$studrst['ID'].") => Coures Registration Not Found in Strict Mode");
        //Push("$RegNo Result (".$studrst['ID'].") => Reading Student Registered Courses (Relax Mode) ...");
       /*  $dtudregCourses = $dbo->SelectFirstRow("coursereg_tb","","RegNo='$RegNo' AND Lvl = {$studrst['Lvl']} AND Sem = {$studrst['Sem']}");
        if(!is_array($dtudregCourses)){ */
            Push("$RegNo Result (".$studrst['ID'].") => Coures Registration Not Found in Relax Mode");
            Push("$RegNo Result (".$studrst['ID'].") => Result Courses will be asummed as Registered Courses","note");
            //$dtudregCourses = [];
        /* }else{
            Push("$RegNo Result (".$studrst['ID'].") => Coures Registration Found in Relax Mode");
            $regcourses = explode("~",$dtudregCourses['CoursesID']);
        } */
    }else{
        Push("$RegNo Result (".$studrst['ID'].") => Coures Registration Found in Strict Mode");

        $regcourses = explode("~",$dtudregCourses['CoursesID']);
    }
    sort($regcourses);
//course registration session
//if(!is_array($dtudregCourses)) {
    $CSesID = !is_array($dtudregCourses)?$studrst['SesID']:$dtudregCourses['SesID'];
//}
$expckey = $studDet['ProgID']."_".$studrst['Lvl']."_".$studrst['Sem']."_".$studDet['StudyID']."_".$CSesID;
if(isset($Cache[$expckey])){
    $expcourses = $Cache[$expckey];
}else{
  //get expected registered courses
    $expcourses = $dbo->RunQuery("SELECT GROUP_CONCAT(CourseID SEPARATOR '~') as ExpCourses FROM course_tb WHERE DeptID = {$studDet['ProgID']} AND Lvl = {$studrst['Lvl']} AND Sem={$studrst['Sem']} AND StudyID = {$studDet['StudyID']} AND StartSesID <= $CSesID AND (EndSesID >= $CSesID OR EndSesID = 0) AND CourseStatus = 0");
    //exit("SELECT GROUP_CONCAT(CourseID SEPARATOR '~') as ExpCourses FROM course_tb WHERE DeptID = {$studDet['ProgID']} AND Lvl = {$studrst['Lvl']} AND Sem={$studrst['Sem']} AND StudyID = {$studDet['StudyID']} AND StartSesID <= $CSesID AND (EndSesID >= $CSesID OR EndSesID = 0) AND CourseStatus = 0");
    if(is_array($expcourses) && $expcourses[1] > 0){
        $expcourses = $expcourses[0]->fetch_assoc();
    //Push("ExpCourses => ".$expcourses ['ExpCourses']);
        $expcourses = explode("~",$expcourses ['ExpCourses']);
        $Cache[$expckey] = $expcourses;
    }else{
    // Push("ExpCourses => "."SELECT GROUP_CONCAT(CourseID SEPARATOR '~') as ExpCourses FROM course_tb WHERE DeptID = {$studDet['ProgID']} AND Lvl = {$studrst['Lvl']} AND Sem={$studrst['Sem']} AND StudyID = {$studDet['StudyID']} AND StartSesID <= $CSesID");
    Push("$RegNo Result (".$studrst['ID'].") => Expected Registered Courses Not Found","note");

        $expcourses = [];
    }  
}

Push("$RegNo Result (Level:".$studrst['Lvl'].", Sem:".$studrst['Sem'].") Expected Courses => ".json_encode($expcourses),"note");


//sort the expcourse
sort($expcourses);

//get the course settings
//$csetting = COURSE();


$ElectiveGropArr = []; //hold the courses registered for a particular group
$ElectiveGropArrUnReg = []; //hold the courses not registered for a particular group
$unregcoseok = true;

//form the unreg courses
if(count($expcourses) > 0 ){
    //loop all expected courses
   foreach($expcourses as $expCID){
       if(isset($Cache['cdet_'.$expCID])){
        $cdetElect = $Cache['cdet_'.$expCID];
       }else{
          //get the course details
       $cdetElect = $dbo->SelectFirstRow("course_tb c, coursegroup_tb cg","c.*,cg.AllowFail","c.CourseID=$expCID AND c.GroupID = cg.ID LIMIT 1"); 
       if(is_array($cdetElect))$Cache['cdet_'.$expCID] = $cdetElect;
       }
       
       $ElectGrp = 0;
       if(is_array($cdetElect)){ //if course details found
        
          //get the elective group
          $ElectGrp = (int)$cdetElect['Elective'];
       }

       //if not registerd
       if(!in_array($expCID,$regcourses)){
           //and not included in outstanding string
        /* if(strpos($outsrtarr[1],":$expCID:") === false){
            $unregcoseok = false;
        } */
        //
        if($ElectGrp == 0){//if course is compulsary
            $unregcourses .= ":$expCID:";
        }else{ //if elective
           if($ElectGrp > 1){ //if course is not in none-or-above group, because none or above group will never be considered as outstanding if not registered
              //add to un reg elective group array
              $ElectiveGropArrUnReg[$ElectGrp][] = $expCID;
           }
        }
        
       }else{// if student register for the course
        //add to the elective group array
        if($ElectGrp > 1){ //if course is not in none-or-above group, because none or above group will never be considered as outstanding if not registered 
        $ElectiveGropArr[$ElectGrp][] = $expCID;
        }
       }
   }
   //process the elective group un reg
   //$maxElectGrp = $csetting['MaxElectiveGrp'];
    foreach($ElectiveGropArrUnReg as $GrpID=>$courses){
      //get the total registered courses in the group
      $totreg = isset($ElectiveGropArr[$GrpID])?count($ElectiveGropArr[$GrpID]):0;
      //get the total number of courses remaining to be registered in the group
      //1. Get the total expected in the group
      $GrpExp = $GrpID - 1;
      //2. subtract the tot reg from expect
      $Rem = $GrpExp - $totreg;
       if($Rem > 0){ // there are elective remaining
        if(count($courses) <= $Rem){ //if the unreg elective group courses are less than or equal to the total remaining, just use all unregistered elective courses
            $unregcourses .= ":".implode("::",$courses).":";
          }else{ // if the unreg elective courses is greater than the remaining, then pick the first number of remaining of the unregelective group array
            for($dd=0;$dd<$Rem;$dd++){
                $unregcourses .= ":".$courses[$dd].":";
            }

          }
        //loop through a
       }
    }
}

//form new
$outs .= $unregcourses;

//remove registered courses from new unreg
if(count($regcourses) > 0){
    $unregcoursesarr = explode("+",$outs);
$newunregcourses = $unregcoursesarr[1];
    foreach($regcourses as $rgCID){
        $newunregcourses = str_replace(":$rgCID:","",$newunregcourses);
    }
    $outs = $unregcoursesarr[0] ."+".$newunregcourses;
}
//exit($outs);
return [$outs,$unregcourses];
//
  }



?>