<?php
/**
 * @copyright (c) Kufer Software GmbH, Oderstraße 7, D-84453 Mühldorf
 */

class MathOperations
{
  
  public static function floatToString($float, $decimals = 2)
  {
    $formattednumberstring = self::floatToStringWithThousandSeps($float, $decimals);
    $thousandsep = self::getThousandSep();

    return str_replace($thousandsep, '', $formattednumberstring);
  }

  
  public static function floatToStringWithThousandSeps($float, $decimals = 2)
  {
    $decimalSep = self::getDecimalSep();
    $thousandsep = self::getThousandSep();

    if (StringOperations::strpos($float, ',')) {
      $float = MathOperations::stringToFloat($float);
    }
    if (!isset($float) || $float == '') {
      $float = '0' . $decimalSep . '00';
    }
    $formattednumberstring = number_format((double)$float, $decimals, $decimalSep, $thousandsep);
    if ($formattednumberstring == '-0' . $decimalSep . '00') {
      $formattednumberstring = '0' . $decimalSep . '00';
    }

    return $formattednumberstring;
  }

  
  public static function getDecimalSep()
  {
    if (!defined('DECIMAL_SEPARATOR')) {
      $basysSettings = BASysSettingsAPI::getBASysSettings();
      if (StringOperations::substr($basysSettings->bundesland, 0, 3) == BUNDESLANDCH) {
        define('DECIMAL_SEPARATOR', '.');
      } else {
        define('DECIMAL_SEPARATOR', ',');
      }
    }

    return DECIMAL_SEPARATOR;
  }

  
  public static function getThousandSep()
  {
    $decimalSep = self::getDecimalSep();
    if ($decimalSep === '.') { 
      return "'";
    } else {
      return '.';
    }
  }

  
  public static function floatToStringTruncateNull($float, $decimals = 2)
  {
    $tmp = MathOperations::floatToString($float, $decimals);

    return self::truncateNull($tmp, $decimals);
  }

  public static function truncateNull($floatString, $decimals = 2)
  {
    
    $strToReplace = DECIMAL_SEPARATOR . \StringOperations::pad('', $decimals, '0');
    $strPos = StringOperations::strpos($floatString, $strToReplace);
    if ($strPos !== false) {
      return StringOperations::substr($floatString, 0, $strPos);
    }

    return $floatString;
  }

  
  public static function stringToFloat($string)
  {
    if (!defined('DECIMAL_SEPARATOR')) {
      $basysSettings = BASysSettingsAPI::getBASysSettings();
      if (StringOperations::substr($basysSettings->bundesland, 0, 3) == BUNDESLANDCH) {
        define('DECIMAL_SEPARATOR', '.');
      } else {
        define('DECIMAL_SEPARATOR', ',');
      }
    }
    $thousandsep = '.';
    if (DECIMAL_SEPARATOR === '.') {
      $thousandsep = "'";
    }
    if ($string == '') {
      $string = '0' . DECIMAL_SEPARATOR . '00';
    }
    $string = str_replace($thousandsep, '', $string);

    return (float)str_replace(',', '.', $string);
  }

  
  public static function fuelleAufMit0($pFloat, $pStellenVorKomma)
  {
    $stellen = 1;
    
    if ($pFloat > 0) {
      $stellen = floor(log10($pFloat) + 1);
    }

    for (;$stellen < $pStellenVorKomma; $stellen++) {
      $pFloat = '0' . $pFloat;
    }

    return $pFloat;
  }

  
  public static function allgRund($pBetrag, $pTyp = 0)
  {
    
    
    
    $pBetrag = round($pBetrag, 2);
    switch ($pTyp) {
      case 1:
        return floor($pBetrag * 10) / 10;
      case 2:
        return floor(($pBetrag * 10) + 0.5) / 10;
      case 3:
        if (floor($pBetrag * 10) != $pBetrag * 10) {    
          return round(($pBetrag + 0.05) * 10) / 10;
        } else {
          return $pBetrag;
        }
        
      case 4:
        if (floor($pBetrag) != floor($pBetrag - 0.5)) { 
          return floor($pBetrag);
        } else {
          return floor($pBetrag) + 0.5;
        }
        
      case 5:
        return floor(($pBetrag * 2) + 0.5) / 2; 
      case 6:
        if (floor($pBetrag * 2) != $pBetrag * 2) { 
          return floor($pBetrag * 2 + 1) / 2;
        } else {
          return $pBetrag;
        }
        
      case 7:
        return floor($pBetrag); 
      case 8:
        return round($pBetrag + 0.001); 
      case 9:
        if (floor($pBetrag) != $pBetrag) { 
          return round($pBetrag + 0.5);
        } else {
          return $pBetrag;
        }
        
      default:
        
        return $pBetrag;
    }
  }

  
  public static function kuferSQLRound($pDouble)
  {
    
    

    $dtemp = round($pDouble * 100);
    if ($dtemp % 100 == 50) {
      $dtemp = floor($dtemp);
      if ($dtemp % 2 != 0) {
        $dtemp++;
      }

      return $dtemp;
    }

    return round($pDouble);
  }

  
  public static function auf50CentRunden($pGeb)
  {
    $dezimalTeil = StringOperations::substr(trim(sprintf('%.2F', $pGeb)), -2, 2);
    $ganzerTeil = StringOperations::substr(trim(sprintf('%.2F', $pGeb)), 0, -3);
    $tCent = '';
    if ('25' <= $dezimalTeil && '75' > $dezimalTeil) {
      $tCent = '50';
    } else {
      $tCent = '00';
    }
    if ($dezimalTeil >= '75') {
      $ganzerTeil += 1;
    }

    return (float)($ganzerTeil . '.' . $tCent);
  }

  
  public static function minProUeKnr($pKnr, $pMinProUe = false, $pMinProUeWert = 0, $pRefreshCache = false)
  {
    $cache = null;
    if ($cache == null) {
      $cache = array();
    }

    if (!$pRefreshCache && isset($cache[$pKnr])) {
      return $cache[$pKnr];
    }

    $tmpUeAbw = $pMinProUeWert;
    if (!$pMinProUe) {
      $dbwZusatztexte = new DBWorkKurszusatz(1, true);
      $tmp = $dbwZusatztexte->getZusatzTexte($pKnr);
      $tmpUeAbw = $tmp->ue_abw;
    }
    $minProUE = 0;
    if ($tmpUeAbw > 0) {
      $minProUE = $tmpUeAbw;
    } else {
      $basysSettings = BASysSettingsAPI::getBASysSettings();
      $ustdmin = $basysSettings->ustdmin;
      if (is_numeric($ustdmin) && $ustdmin > 0) {
        $minProUE = $ustdmin;
      } else {
        
        $minProUE = 90;
      }
    }
    $cache[$pKnr] = $minProUE;

    return $cache[$pKnr];
  }

  public static $_ue_cache;

  public static function calcMinProUeWithCustom(\Kurs $kurs)
  {
    if ($kurs->zusatztexte != null && $kurs->getVarValue('min_pro_ue') > 0) {
      return $kurs->getVarValue('min_pro_ue');
    } else {
      return MathOperations::minProUeKnr($kurs->id);
    }
  }

  
  public static function calculateUE(int $pMinuten, ?\Kurs $pKurs = null, string $pKnr = '', bool $pRefreshCache = false): float
  {
    if (is_null($pKurs) && $pKnr === '') {
      throw new \Exception('Fehler bei den Argumenten (MathOperations::calculateUE): weder Kurs noch Kursnummer angegeben');
    }

    if (is_null(self::$_ue_cache)) {
      self::$_ue_cache = array();
    }
    if (is_null($pKurs)) {
      $cacheKey = $pKnr . '|' . $pMinuten;
    } else {
      $cacheKey = $pKurs->id . '|' . $pMinuten;
    }

    if (!$pRefreshCache && isset(self::$_ue_cache[$cacheKey])) {
      return self::$_ue_cache[$cacheKey];
    }

    
    
    
    if ($pKurs != null && $pKurs->zusatztexte != null && $pKurs->getVarValue('min_pro_ue') > 0) {
      $minProUeKnr = $pKurs->getVarValue('min_pro_ue');
    } else {
      $minProUeKnr = \MathOperations::minProUeKnr($pKnr);
    }

    self::$_ue_cache[$cacheKey] = self::teileMinutenDurchMinutenProUe($pMinuten, $minProUeKnr);

    return self::$_ue_cache[$cacheKey];
  }

  public static function calculateUeByKurszusatztexte(int $pMinuten, \Kurszusatztexte $pKurszusatztexte, bool $pRefreshCache = false): float
  {
    static $cache = array();
    $key = $pKurszusatztexte->id . '|' . $pMinuten;
    if (!$pRefreshCache && isset($cache[$key])) {
      return $cache[$key];
    }

    $cache[$key] = self::teileMinutenDurchMinutenProUe($pMinuten, $pKurszusatztexte->ue_abw);

    return $cache[$key];
  }

  private static function teileMinutenDurchMinutenProUe(int $pMinuten, int $pMinProUeKnr): float
  {
    $dbwhl = new \DBWorkHLGrundSys(1, true);

    
    $g_musik_22halbue = $dbwhl->getFeldWert('MUSIK_22HALBUE', 'F');
    if ($g_musik_22halbue === 'W' && $pMinProUeKnr == 45) {
      $min45stel = floor($pMinuten) % 45;
      if ($min45stel == 11) {
        $result = ($pMinuten + 0.25) / $pMinProUeKnr;
      } elseif ($min45stel == 12) {
        $result = ($pMinuten - 0.75) / $pMinProUeKnr;
      } elseif ($min45stel == 22) {
        $result = ($pMinuten + 0.5) / $pMinProUeKnr;
      } elseif ($min45stel == 23) {
        $result = ($pMinuten - 0.5) / $pMinProUeKnr;
      } elseif ($min45stel == 33) {
        $result = ($pMinuten + 0.75) / $pMinProUeKnr;
      } elseif ($min45stel == 34) {
        $result = ($pMinuten - 0.25) / $pMinProUeKnr;
      } else {
        $result = $pMinuten / $pMinProUeKnr;
      }
    } else {
      $result = $pMinuten / $pMinProUeKnr;
    }

    return $result;
  }

  public static $_min_cache;

  
  public static function calculateMin($pUe, $pKurs = null, $pKnr = '', $pRefreshCache = false)
  {
    $ue = MathOperations::stringToFloat($pUe);

    if (is_null($pKurs) && $pKnr == '') {
      throw new Exception('Fehler bei den Argumenten (MathOperations::calculateMin): weder Kurs noch Kursnummer angegeben');
    }

    if (is_null(self::$_min_cache)) {
      self::$_min_cache = array();
    }
    if (is_null($pKurs)) {
      $cacheKey = $pKnr . '|' . $ue;
    } else {
      $cacheKey = $pKurs->id . '|' . $ue;
    }

    if (!$pRefreshCache && isset(self::$_min_cache[$cacheKey])) {
      return self::$_min_cache[$cacheKey];
    }

    if (null == $pKurs) {
      $minProUeKnr = MathOperations::minProUeKnr($pKnr);
    } else {
      $minProUeKnr = $pKurs->getVarValue('min_pro_ue');
    }


    $dbwhl = new DBWorkHLGrundSys(1, true);

    
    
    $g_musik_22halbue = $dbwhl->getFeldWert('MUSIK_22HALBUE', 'F');
    if ($g_musik_22halbue == 'W' && $minProUeKnr == 45) {
      $ganzeUe = floor($ue);
      $teilUe = round($ue - $ganzeUe, 2);
      if ($teilUe == 0.25) {
        $result = $ganzeUe * $minProUeKnr + 11;
      } elseif ($teilUe == 0.5) {
        $result = $ganzeUe * $minProUeKnr + 22;
      } elseif ($teilUe == 0.75) {
        $result = $ganzeUe * $minProUeKnr + 33;
      } else {
        $result = $ue * $minProUeKnr;
      }
    } else {
      $result = $ue * $minProUeKnr;
    }

    self::$_min_cache[$cacheKey] = MathOperations::kuferSQLRound($result);

    return self::$_min_cache[$cacheKey];
  }

  
  public static function aufbereitung($value)
  {
    if (!is_numeric($value)) {
      return $value;
    }
    $tmp = '';
    $counter = 0;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    $tmp .= ($aktzeichen + 4) % 10;
    if ($aktzeichen == '') {
      return $tmp;
    }
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= (MathOperations::check($aktzeichen - 3)) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= ($aktzeichen + 6) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= (MathOperations::check($aktzeichen - 5)) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= ($aktzeichen * 3) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= ($aktzeichen * 7) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= (MathOperations::check($aktzeichen - 1)) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= ($aktzeichen + 2) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= ($aktzeichen * 7) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= (MathOperations::check($aktzeichen - 4)) % 10;
    $counter++;
    $aktzeichen = StringOperations::substr($value, $counter, 1);
    if ($aktzeichen == '') {
      return $tmp;
    }
    $tmp .= ($aktzeichen + 5) % 10;
    $counter++;

    return $tmp;
  }

  public static function check($val)
  {
    if ($val < 0) {
      return $val + 10;
    } else {
      return $val;
    }
  }

  
  public static function powmod($base, $exponent, $modulus)
  {
    if (intval(PHP_VERSION) > 4) {
      return(bcpowmod($base, $exponent, $modulus));
    } else {
      $r = '';
      while ($exponent != '0') {
        $t = bcmod($exponent, '4096');
        $r = StringOperations::substr('000000000000' . decbin(intval($t)), -12) . $r;
        $exponent = bcdiv($exponent, '4096');
      }
      $r = preg_replace('!^0+!', '', $r);
      if ($r == '') {
        $r = '0';
      }
      $base = bcmod($base, $modulus);
      $erb = strrev($r);
      $q = '1';
      $a[0] = $base;
      for ($i = 1;$i < StringOperations::strlen($erb);$i++) {
        $a[$i] = bcmod(bcmul($a[$i - 1], $a[$i - 1]), $modulus);
      }
      for ($i = 0;$i < StringOperations::strlen($erb);$i++) {
        if ($erb[$i] == '1') {
          $q = bcmod(bcmul($q, $a[$i]), $modulus);
        }
      }

      return($q);
    }
  }

  
  public static function getInteger($str)
  {
    if (StringOperations::substr($str, -1) == 'M') {
      $num = ((int)StringOperations::substr($str, 0, -1)) * 1048576;
    } elseif (StringOperations::substr($str, -1) == 'K') {
      $num = ((int)StringOperations::substr($str, 0, -1)) * 1024;
    } elseif (StringOperations::substr($str, -1) == 'G') {
      $num = ((int)StringOperations::substr($str, 0, -1)) * 1024 * 1048576;
    } else {
      $num = (int)$str;
    }

    return $num;
  }

  
  public static function quersumme($pZahl)
  {
    $quersumme = 0;
    $strZahl = (string)$pZahl;

    for ($i = 0; $i < StringOperations::strlen($strZahl); $i++) {
      $quersumme += $strZahl[$i];
    }

    return $quersumme;
  }

  
  public static function gewichten($pZahl, array $pGewicht)
  {
    $result = array();

    for ($i = 0; $i < StringOperations::strlen($pZahl); $i++) {
      array_push($result, $pZahl[$i] * $pGewicht[$i]);
    }

    return $result;
  }

  
  public static function gewichtenQS($pZahl, array $pGewicht)
  {
    $result = array();

    $ary = self::gewichten($pZahl, $pGewicht);
    for ($i = 0; $i < count($ary); $i++) {
      $result[$i] = self::quersumme($ary[$i]);
    }

    return $result;
  }

  
  public static function getBytesUmrechnung($pFileSize)
  {
    $fileExtension = array('Bytes', 'KB', 'MB', 'GB');
    $i = 0;
    while ($pFileSize >= 1024) {
      $pFileSize /= 1024;
      $i++;
    }

    return (MathOperations::floatToString($pFileSize) . ' ' . $fileExtension[$i]);
  }

  
  public static function grammToKilo($pWeight, $pWithThousandSeparator = false)
  {
    $float = $pWeight / 1000;
    if ($pWeight % 10 !== 0) {
      $result = number_format((double)$float, 3, static::getDecimalSep(), static::getThousandSep());
    } else {
      $result = number_format((double)$float, 2, static::getDecimalSep(), static::getThousandSep());
    }
    if (!$pWithThousandSeparator) {
      $result = str_replace(static::getThousandSep(), '', $result);
    }

    return $result;
  }

  
  public static function gewichtAnzeige($pGewicht)
  {
    if ($pGewicht > 1000) {
      return MathOperations::grammToKilo($pGewicht) . ' kg';
    } else {
      return $pGewicht . ' g';
    }
  }

  public static function formatZweiNachkommastellen($betrag)
  {
    if (strpos($betrag, ',')) {
      $betrag = MathOperations::stringToFloat($betrag);
    }

    return number_format($betrag, 2, '.', '');
  }

  
  public static function isNumeric($str)
  {
    if (!is_numeric($str)) {
      return false;
    }
    if (preg_match('/\\d+[eE]-?\\d+/', $str)) {
      return false;
    }

    return true;
  }
}