<?php
/**
 * @copyright (c) Kufer Software GmbH, Loisachstrasse 4, D-84453 Mühldorf
 */


class XMLNode
{
  
  public $parentnode;
  
  public $childnodes = array();
  
  public $opentagchar = '<';
  
  public $closetagchar = '>';
  
  public $tag = '';
  
  public $data = '';

  
  public $attributes = '';
  
  public $dataType;

  
  public $dmzLeer;

  
  public function __construct($pTag = '', $pData = '', $pAttributes = '', $pDataType = '', $pDmzLeer = false)
  {
    $this->setDataType($pDataType);
    $this->setTag($pTag);
    $this->setData($pData);
    $this->setAttributes($pAttributes);
    $this->dmzLeer = $pDmzLeer;
  }

  
  public function parseXML($pInput)
  {
    $str = $pInput;
    if ($this->getDataType() == 'HTML') {
      $str = trim($str, ' '); 
      $str = '<![CDATA[' . $str . ']]>';
    } else {
      $str = trim($str ?? '', ' ');
      $str = str_replace('&', '&amp;', $str);
      $str = str_replace('<', '&lt;', $str);
      $str = str_replace('>', '&gt;', $str);
      $str = str_replace("'", '&apos;', $str);
      $str = str_replace('"', '&quot;', $str);
    }

    return $str;
  }

  
  public function isNodeOK($pNode)
  {
    if (!$pNode instanceof XMLNode) {
      return false;
    }
    $ret = true;
    if (isset($pNode) && $pNode->tag == '') {
      $ret = false;
    }
    if (isset($pNode) && $pNode->data == '30 12 1899  0:00') {
      $ret = false;
    }

    return $ret;
  }

  
  public function setTag($pTag)
  {
    $this->tag = $pTag;
  }

  
  public function setDataType($pValue)
  {
    $this->dataType = $pValue;
  }

  
  public function getDataType()
  {
    return $this->dataType;
  }

  
  public function setData($pData)
  {
    $this->data = $this->parseXML($pData);
  }

  
  public function setAttributes($pAttributes)
  {
    $this->attributes = $pAttributes;
  }

  
  public function addChild($pNode)
  {
    if ($this->isNodeOK($pNode)) {
      $pNode->parentnode = $this;
      array_push($this->childnodes, $pNode);

      return $pNode;
    } else {
      return false;
    }
  }

  
  public function addChilds($pNodes)
  {
    if (count($pNodes) > 0) {
      for ($i = 0; $i < count($pNodes); $i++) {
        $this->addChild($pNodes[$i]);
      }
    }
  }

  
  public function hasChildren()
  {
    return count($this->childnodes) > 0;
  }

  
  public function getXMLstring($pNode = '')
  {
    if ($pNode == '') {
      $pNode = $this;
    }
    $str = '';
    if ((isset($pNode->data) && ($pNode->data == ''))
      && (isset($pNode->childnodes) && count($pNode->childnodes) == 0)
      && !$pNode->dmzLeer) {
      
    } else {
      $str = $this->opentagchar . (isset($pNode->tag) ? $pNode->tag : '')
        . (empty($pNode->attributes) ? '' : ' ' . $pNode->attributes)
        . $this->closetagchar;
      if (isset($pNode->childnodes) && count($pNode->childnodes) > 0) {
        $str .= "\r\n";
        for ($i = 0; $i < count($pNode->childnodes); $i++) {
          $str .= $this->getXMLstring($pNode->childnodes[$i]);
        }
      }
      $str .= isset($pNode->data) ? $pNode->data : '';
      $str .= $this->opentagchar . '/' . (isset($pNode->tag) ? $pNode->tag : '') . $this->closetagchar . "\r\n";
    }

    return $str;
  }

  
  public static function getXMLNode($pDatamodul, $pNodeTag, $pExceptions = array(), $pForDMZ = false, $pForceVars = array())
  {
    $rootNode = new XMLNode($pNodeTag);
    for ($i = 0; $i < $pDatamodul->getVarCount(); $i++) {
      $varname = $pDatamodul->getVarname($i);
      if (in_array($varname, $pExceptions)) {
        continue;
      }
      if ($pForDMZ) {
        $value = $pDatamodul->getDMZValue($varname);
      } else {
        $value = $pDatamodul->getVarValue($varname);
      }
      if (is_array($value)) { 
        continue;
      }
      if (!empty($value) || in_array($varname, $pForceVars)) {
        if ($pForDMZ) {
          if (in_array($varname, $pForceVars)) {
            $rootNode->addChild(new XMLNode(StringOperations::strtolower($pDatamodul->getDmzVarName($varname)), $value, '', '', true));
          } else {
            $rootNode->addChild(new XMLNode(StringOperations::strtolower($pDatamodul->getDmzVarName($varname)), $value));
          }
        } else {
          $rootNode->addChild(new XMLNode(StringOperations::strtolower($varname), $value));
        }
      }
    }

    return $rootNode;
  }

  
  public static function getXmlNodeFromObj($pObject, $pNodeTag, $pExceptions = array(), $pForceVars = array())
  {
    $rootNode = new XMLNode($pNodeTag);
    $objVars = get_object_vars($pObject);

    foreach ($objVars as $varname => $value) {
      if (in_array($varname, $pExceptions)) {
        continue;
      }
      if ($value == '' && !in_array($varname, $pForceVars)) {
        continue;
      }
      $rootNode->addChild(new XMLNode(StringOperations::strtolower($varname), $value, '', '', true));
    }

    return $rootNode;
  }

  
  public static function getXmlNodeOnlySpecificFields($pDatamodul, $pNodeTag, $pVars = array(), $pForDMZ = false)
  {
    $rootNode = new XMLNode($pNodeTag);
    foreach ($pVars as $varname) {
      if ($pForDMZ) {
        $value = $pDatamodul->getDMZValue($varname);
        $attribut = '';
        
        
        
        if (!isset($value) || $value === '') {
          $attribut = 'leer="true"';
          $value = '-';
        }
        $rootNode->addChild(new XMLNode(StringOperations::strtolower($pDatamodul->getDmzVarName($varname)), $value, $attribut));
      } else {
        $value = $pDatamodul->getVarValue($varname);
        $rootNode->addChild(new XMLNode(StringOperations::strtolower($varname), $value));
      }
    }

    return $rootNode;
  }

  
  public function getOneDirectChildByTag(string $tag, bool $ignorecase = false): ?XMLNode
  {
    foreach ($this->childnodes as $childnode) {
      if (!$ignorecase
        ? $childnode->tag === $tag
        : StringOperations::strtolower($childnode->tag) === StringOperations::strtolower($tag)) {
        return $childnode;
      }
    }

    return null;
  }

  
  public function getOneByTag(string $tag, bool $ignorecase = false): ?XMLNode
  {
    if (!$ignorecase
      ? $this->tag === $tag
      : StringOperations::strtolower($this->tag) === StringOperations::strtolower($tag)) {
      return $this;
    }

    foreach ($this->childnodes as $childnode) {
      if (!$ignorecase
        ? $childnode->tag === $tag
        : StringOperations::strtolower($childnode->tag) === StringOperations::strtolower($tag)) {
        return $childnode;
      }
    }

    foreach ($this->childnodes as $childnode) {
      $result = $childnode->getOneByTag($tag, $ignorecase);
      if (!is_null($result)) {
        return $result;
      }
    }

    return null;
  }

  
  public function getAllByTag(string $tag, bool $ignorecase = false, bool $first = true): array
  {
    $result = array();
    if ($first && (!$ignorecase ? $this->tag === $tag
        : StringOperations::strtolower($this->tag) === StringOperations::strtolower($tag))) {
      $result[] = $this;
    }

    array_push($result, ...array_filter(
      $this->childnodes,
      static function(XMLNode $childnode) use ($tag, $ignorecase): bool {
        return !$ignorecase ? $childnode->tag === $tag
          : StringOperations::strtolower($childnode->tag) === StringOperations::strtolower($tag);
      }
    ));

    foreach ($this->childnodes as $childnode) {
      array_push($result, ...$childnode->getAllByTag($tag, $ignorecase, false));
    }

    return $result;
  }

  
  public function getAllDirectChildByTag(string $tag, bool $ignorecase = false): array
  {
    return array_filter($this->childnodes, static function(XMLNode $childnode) use ($tag, $ignorecase): bool {
      return !$ignorecase
        ? $childnode->tag === $tag
        : StringOperations::strtolower($childnode->tag) === StringOperations::strtolower($tag);
    });
  }

  public static function toXMLNode(\SimpleXMLElement $element, bool $dmzLeer = false)
  {
    $attrs = '';
    foreach ($element->attributes() as $key => $attr) {
      $attrs .= ' ' . $key . '="' . str_replace('"', '\\"', (string)$attr) . '"';
    }

    $result = new XMLNode($element->getName(), trim((string)$element), trim($attrs), '', $dmzLeer);

    foreach ($element->children() as $child) {
      $result->addChild(static::toXMLNode($child, $dmzLeer));
    }

    return $result;
  }

  
  public static function fromXml(string $xml, bool $dmzLeer = false): ?XMLNode
  {
    $parsed = simplexml_load_string($xml);
    if ($parsed === false) {
      return null;
    }

    return static::toXMLNode($parsed, $dmzLeer);
  }
}