<?php #ajax/print.php

class Printer
{
  private $page;
  private $responseCode = 500;
  private $prints = [];
  private $html = "";
  
  public function __construct() {
    $this->page = $this->man->Route();
    
    $qry = "SELECT l.ID,l.Bezeichnung,l.Hauptblock,b.Css,b.Breite,b.Höhe FROM sys_print_layouts l "
          ."LEFT JOIN sys_print_blocks b ON l.Hauptblock=b.ID WHERE Seite=? ORDER BY l.Bevorzugt DESC, l.Bezeichnung ASC";
    if ($stmt = Db::Get()->prepare($qry)) {
      $stmt->bind_param("s", $this->page);
      $stmt->execute();
      $res = $stmt->get_result();
      while ($row = $res->fetch_assoc()) {
        $this->prints[] = [
          "ID" => strval($row["ID"]), 
          "Bezeichnung" => $row["Bezeichnung"],
          "Hauptblock" => $row["Hauptblock"],
          "Css" => $row["Css"],
          "Breite" => $row["Breite"],
          "Höhe" => $row["Höhe"],
        ];
  } } }
  
  public function GetPrints() {
    return $this->prints;
  }
  
  public static function Print($printID, $json) {
  	return "<div>Not yet implemented</div>";
  }
  
  public function CreateHtml($printID) {
    // Find requested layout
    $layout = null;
    foreach ($this->prints as $key => $print) {
      if (strval($printID)==$print["ID"]) {
        $layout = $print;
        break;
    } }
    
    // Breakoff if layout doesn't exist
    if (null == $layout) {
      $this->responseCode = 400;
      return;
    }
    
    // Get content
    $content = $this->man->Output()["content"];
    
    $block = [
      "ID" => $layout["Hauptblock"],
      "Bezeichnung" => $layout["Bezeichnung"],
      "Css" => $layout["Css"],
      "Breite" => $layout["Breite"],
      "Höhe" => $layout["Höhe"],
      "Links" => 0,
      "Oben" => 0
    ];
    $entries = $this->prepareEntries($content);
    $this->printBlock($block, $entries);
    $this->html.= $this->joinHtml($entries);
    
    $this->responseCode = 200;
  }
  
  public function GetHtml() {
    return $this->html;
  }
  
  public function ResponseCode() {
    return $this->responseCode;
  }
  
  /******************** Private functions *************************/
  
  private function prepareEntries($content) {
    $ret = [];
    $selectionExists = sizeof($this->man->selected)>0;
    foreach ($content as $group) {
      foreach ($group["ENTRIES"] as $entry) {
        if (!$selectionExists || in_array($entry["ID"], $this->man->selected)) {
          $ret[$entry["ID"]] = [
            "html" => "",
            "floating" => [],
            "content" => $entry,
          ];
    } } }
    return $ret;
  }
  
  private function joinHtml($entries) {
    $ret = "";
    foreach ($entries as $entry) {
      $ret.= $entry["html"];
    }
    return $ret;
  }
  
  private function printBlock($block, &$entries) {
    // Initialize block
    $div = "<div name='".$block["Bezeichnung"]."' style='".$block["Css"]
          ."width:".($block["Breite"]/10)."mm;height:".($block["Höhe"]/10)."mm;left:".($block["Links"]/10)."mm;top:".($block["Oben"]/10)."mm;'>";
          
    foreach ($entries as &$entry) {
      $entry["html"].= $div;
    }
    unset($entry);  // $entry was used as a reference, so it needs to be unset
    
    // Fetch sub-blocks
    $qry = "SELECT spb.*,sph.Links,sph.Oben FROM sys_print_hierarchy sph LEFT JOIN sys_print_blocks spb ON spb.ID=sph.Kindblock WHERE sph.Block=".$block["ID"];
    $res = Db::Get()->query($qry);
    while ($subblock = $res->fetch_assoc()) {
      $this->printBlock($subblock, $entries);
    }
    
    // Fetch block elements
    $elements = [];
    if ($res = Db::Get()->query("SELECT * FROM sys_print_elements WHERE Block=".$block["ID"]." ORDER BY Reihenfolge ASC")) {
      while ($element = $res->fetch_assoc()) {
        $this->printEntryElement($element, $entries);
        //$elements[] = $row;
    } }
    
    // Finalize block
    foreach ($entries as &$entry) {
      $entry["html"].= "</div>";
    }
    unset($entry);
  }
  
  private function printEntryElement($element, &$entries) {
    foreach ($entries as &$entry) {
      $do_print = false;
      $cond = explode("|", $element["Bedingung"]);
    	if ($element["Parameter"]=="") {
        $element["Parameter"] = "{}";
      }
      $params = json_decode($element["Parameter"], true);
      switch ($cond[0]) {
        case null:
        case "":
        case "0": //Print always
          $do_print = true;
          //echo " - Print always";
          break;
        case "1": //Field has to be NOT null, not false and not empty text
          //echo " - Print if NOT null/false/empty";
          if (sizeof($cond)>1 && ($this->getContent($entry["content"], $cond[1])!==null && (
                  $this->getContent($entry["content"], $cond[1])!==false || $this->getContent($entry["content"], $cond[1], true)!=""
          ) ) ) {
            $do_print = true;
          }
          break;
        case "2": //Field has to be null, false or empty text
          //echo " - Print if null/false/empty";
          if (sizeof($cond)>1 && ($this->getContent($entry["content"], $cond[1])===null || $this->getContent($entry["content"], $cond[1])===false || $this->getContent($entry["content"], $cond[1], true)==""))
            $do_print = true;
          break;
        case "3": //Field has to be value (second parameter) or include value if is link
          if (sizeof($cond)>2) {
            // UNTESTED
            //echo " - Print if has/includes value ".$cond[2];
            $result = $this->getContent($entry["content"], $cond[1]);
            $arr = is_array($result);
            $do_print = ($arr && in_array($cond[2], $result)) || (!$arr && $cond[2]==$result);
          }
          break;
        case "4": //Field has to be NOT value (second parameter) or DONT include value if is link
          if (sizeof($cond)>2) {
            // UNTESTED
            //echo " - Print if has/includes NOT value ".$cond[2];
            $result = $this->getContent($entry["content"], $cond[1]);
            $arr = is_array($result);
            $do_print = ($arr && !in_array($cond[2], $result)) || (!$arr && $cond[2]!=$result);
          }
          break;
        case "5": //Field has to be text (second parameter) or include text if it is link
          if (sizeof($cond)>2) {
            //echo " - Print if has/includes text ".$cond[2];
            $result = $this->getContent($entry["content"], $cond[1], 1);
            $testtexts = explode(",", $cond[2]);
            if (is_array($result)) {
              $do_print = true;
              foreach ($testtexts as $testtext) {
                if (!in_array($testtext, $result)) {
                  $do_print = false;
                  break;
              } }
            } else {
              $do_print = $cond[2]==$result;
          } }
          break;
        case "6": //Field has to be NOT text (second parameter) or DONT include text if it is link
          if (sizeof($cond)>2) {
            // UNTESTED
            //echo " - Print if has/includes NOT text ".$cond[2];
            $result = $this->getContent($entry["content"], $cond[1], 1);
            $testtexts = explode(",", $cond[2]);
            if (is_array($result)) {
              $do_print = true;
              foreach ($testtexts as $testtext) {
                if (in_array($testtext, $result)) {
                  $do_print = false;
                  break;
              } }
            } else {
              $do_print = $cond[2]!=$result;
          } }
          break;
      }
      $print_count = 0;
      if ($do_print) {
        $print_count = $params["repeat"] ?? 1;
      }
  		
      while ($print_count>0) {
        //echo " - Printing";
        $print_count--;
        $border = array();
        $border["top"] = isset($params["border-top"]) ? $params["border-top"] : (isset($params["border"]) ? $params["border"] : "0");
        $border["bottom"] = isset($params["border-bottom"]) ? $params["border-bottom"] : (isset($params["border"]) ? $params["border"] : "0");
        $border["left"] = isset($params["border-left"]) ? $params["border-left"] : (isset($params["border"]) ? $params["border"] : "0");
        $border["right"] = isset($params["border-right"]) ? $params["border-right"] : (isset($params["border"]) ? $params["border"] : "0");
        $border["color"] = isset($params["border-color"]) ? $params["border-color"] : "#000000";
        $border["string"] = "";
        if ($border["left"]!="0") {
          $border["string"].= "border-left: 1px solid ".$border["color"]."; ";
        }
        if ($border["right"]!="0") {
          $border["string"].= "border-right: 1px solid ".$border["color"]."; ";
        }
        if ($border["top"]!="0") {
          $border["string"].= "border-top: 1px solid ".$border["color"]."; ";
        }
        if ($border["bottom"]!="0") {
          $border["string"].= "border-bottom: 1px solid ".$border["color"]."; ";
        }
        /*if (isset($params["border"])) {
          $border["string"] = "border: 1px solid ".$border["color"].";";
        }*/
        
        $cnt = $element["Inhalt"];
        $fields = array();
        if (preg_match_all("/#([^#\[\]]+)(?:\[(\d*):(\d*)\])?#/", $cnt, $matches, PREG_SET_ORDER)) {
          foreach ($matches as $match) {
            $replace = "";
            $fields[] = $match[1];
            $replace = $this->getContent($entry["content"], $match[1], 1);
            if (sizeof($match)>3) {
            	if ($match[3]!="") {
            		$replace = substr($replace, 0, intval($match[3]));
            	}
            	if ($match[2]!="") {
            		$replace = substr($replace, intval($match[2]));
            	}
            }
            $cnt = str_replace($match[0], $replace, $cnt);
          }
        }
        
        $left = $element["Links"]/10;
        $top = $element["Oben"]/10;
        $width = $element["Breite"]/10;
        if ($width == 0) {
          $width = 10;
        }
        $height = $element["Höhe"]/10;
        if ($height == 0) {
          $height = 10;
        }
        $this->moveIfOverlap($entry["floating"], $left, $top, $width, $height, isset($params["float"]) ? $params["float"] : "none");
        $position = "position:absolute;";
        if (isset($params["position"])) {
          $position = "position:".$params["position"].";";
        }
        $position.= "z-index:".$element["Reihenfolge"].";";

        switch ($element["Art"]) {
          case "img":
            if (isset($params["aspect"]) && $params["aspect"]=="keep") {
              $imgsize = "max-width: ".$width."mm; max-height: ".$height."mm; width: auto; height: auto; ";
            }
            if (isset($params["aspect"]) && $params["aspect"]=="height") {
              $imgsize = "height: ".$height."mm; width: auto; ";
            } else {
              $imgsize = "width: ".$width."mm; height: ".$height."mm; ";
            }
            $path = $cnt;
            if (count($fields)) {
              $path = $cnt;
            }
            //if (file_exists($path)) {
              $entry["html"].= "<img src='".$path."' style='".$position." left: ".$left."mm; top: ".$top."mm; ".$imgsize."' />";
            //}
            break;
          case "text":
            $rotate = "";
            if (isset($params["rotate"]) && $params["rotate"]=="1") {
              $rotate = "transform: rotate(-90deg); ";
              $left = "calc(".($left + ($height - $width)/2)."mm - ".$border["left"]."px); ";
              $top = "calc(".($top + ($width - $height)/2)."mm - ".$border["top"]."px); ";
            } else {
              $left.= "mm; ";
              $top.= "mm; ";
            }
            $font = "font-family: ".(isset($params["font"]) && $params["font"]=="narrow" ? "Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; " : "Arial, Liberation Sans; ");
            $font.= "font-size: ".(isset($params["size"]) ? $params["size"] : $height)."mm; ";
            if (isset($params["weight"])) {
              $font.= "font-weight: ".$params["weight"]."; ";
            }
            if (isset($params["align"])) {
              $font.= "text-align: ".$params["align"]."; ";
            }
            if (isset($params["color"])) {
            	$font.= "color: ".$params["color"]."; ";
            }
            $entry["html"].= "<span style='".$position."overflow:hidden;white-space:nowrap;left:".$left."top:".$top."width:".$width."mm;height:"
                            .$height."mm;".$font.$border["string"].$rotate."'>".$cnt."</span>";
            break;
          case "circle":
            $left.= "mm; ";
            $top.= "mm; ";
            $display = "display: inline-flex; align-items: center; justify-content: space-around; ";
            $font = "font-family: ".(isset($params["font"]) && $params["font"]=="narrow" ? "Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; " : "Arial, Liberation Sans; ");
            $font.= "font-size: ".(isset($params["size"]) ? $params["size"] : $height)."mm; ";
            if (isset($params["weight"])) {
              $font.= "font-weight: ".$params["weight"]."; ";
            }
            if (isset($params["color"])) {
            	$font.= "color: ".$params["color"]."; ";
            }
            $font.= "text-align: center; ";
            $background = isset($params["background"]) ? "background-color: ".$params["background"].";" : "";
            $entry["html"].= "<span style='".$position."left:".$left."top:".$top."width:".$width."mm;height:".$height."mm;".$display.$background.$font
                            .$border["string"]."border-radius:50%;'>".$cnt."</span>";
            break;
          case "quartercircle":
            $left.= "mm; ";
            $top.= "mm; ";
            $background = isset($params["background"]) ? "background-color: ".$params["background"].";" : "";
            $border_radius = isset($params["corner"]) ? "border-".$params["corner"]."-radius:100%;" : "border-top-left-radius:100%;";
            $entry["html"].= "<span style='".$position."left:".$left."top:".$top."width:".$width."mm;height:".$height."mm;"/*.$display*/.$background/*.$font*/
                            .$border_radius."'>".$cnt."</span>";
            break;
    } } }
    unset($entry);
  }
  
  private function getTextFromReference($ref, $getText) {
    if (1==$getText && array_key_exists("KÜRZEL", $ref)) {
      return $ref["KÜRZEL"];
    } elseif (1<=$getText && array_key_exists("NAME", $ref)) {
      return $ref["NAME"];
    } elseif (array_key_exists("ID", $ref)) {
      return $ref["ID"];
    }
    return "";
  }
  
  // $entry   Content-array of entry ([ID, MAIN, SUB])
  // $key     Requested field
  // $getText 0 = ID is sufficient, 1 = Kürzel (prio.) or Name desired, 2 = Name desired
  private function getContent($entry, $key, $getText = 0) {
    // Check if requested field are actually nested keys separated by dots
    $dotPos = strpos($key, ".");
    if ($dotPos!==false) {
      $subkey = substr($key, 0, $dotPos);
      if (array_key_exists($subkey, $entry)) {
        return $this->getContent($entry[$subkey], substr($key, $dotPos+1, strlen($key)-$dotPos-1));
    } }
    
    // Check if requested field exists
    if (array_key_exists($key, $entry)) {
      // Check if requested field is an array (which means it was selected through a foreign reference or link table)
      if (is_array($entry[$key])) {
        // Check if array is size 0 -> This means an unset reference, i.e. NULL
        if (0==sizeof($entry[$key])) {
          return null;
        } elseif (array_key_exists("ID", $entry[$key])) { // Check if requested field is a normal foreign reference -> It will have the ID field then.
          // Foreign reference with ID, NAME and maybe KÜRZEL
          return $this->getTextFromReference($entry[$key], $getText);
        } else { // If not, we assume to have an sequential array which means a n:m-link-table
          $ret = [];
          foreach ($entry[$key] as $i => $ref) {
            $ret[] = $this->getTextFromReference($ref, $getText);
          }
          return $ret;
        }
      } else { // Requested field is a normal value
        return $entry[$key];
      }
    } elseif (array_key_exists("MAIN", $entry)) { // If we are in the root structure of a content object, we will have sections MAIN and SUB and check those recursively
      // Search the MAIN-array for field
      $ret = $this->getContent($entry["MAIN"], $key, $getText);
      // If only the field name was returned check SUB-array if exists
      if ($key==$ret && array_key_exists("SUB", $entry)) {
        $ret = $this->getContent($entry["SUB"], $key, $getText);
      }
      return $ret;
    } else {
      // Nothing was found, we return the field name
      return $key;
  } }
  
  private function moveIfOverlap(&$floating, &$left, &$top, $width, $height, $dir = "none", $lastdir = "none") {
    $moved = false;
    foreach ($floating as $key => $float) {
      if (
        $left < $float["right"] && 
        $left+$width > $float["left"] && 
        $top < $float["bottom"] &&        
        $top+$height > $float["top"]
      ) {
        if ($float["dir"]=="left" && $lastdir!="right") {
          $left = $float["right"];
        } else if ($float["dir"]=="right" && $lastdir!="left") {
          $left = $float["left"] - $width;
        } else if ($float["dir"]=="up" && $lastdir!="down") {
          $top = $float["bottom"];
        } else if ($float["dir"]=="down" && $lastdir!="up") {
          $top = $float["top"] - $height;
        }
        $lastdir = $float["dir"];
        $moved = true;
      }
    }
    if ($moved) {
      $this->moveIfOverlap($floating, $left, $top, $width, $height, $dir, $lastdir);
    } else if ($dir!="none") {
      $floating[] = array(
        "left" => $left,
        "right" => $left + $width,
        "top" => $top,
        "bottom" => $top + $height,
        "dir" => $dir
      );
  } }
}
