<?php
namespace Braintree\Xml;

use DateTime;
use DateTimeZone;
use DOMDocument;
use DOMElement;
use DOMText;
use Braintree\Util;

/**
 * Braintree XML Parser
 */
class Parser
{
    /**
     * Converts an XML string into a multidimensional array
     *
     * @param string $xml
     * @return array
     */
    public static function arrayFromXml($xml)
    {
        $document = new DOMDocument('1.0', 'UTF-8');
        $document->loadXML($xml);

        $root = $document->documentElement->nodeName;

        return Util::delimiterToCamelCaseArray([
            $root => self::_nodeToValue($document->childNodes->item(0)),
        ]);
    }

    /**
     * Converts a node to an array of values or nodes
     *
     * @param DOMNode @node
     * @return mixed
     */
    private static function _nodeToArray($node)
    {
        $type = null;
        if ($node instanceof DOMElement) {
            $type = $node->getAttribute('type');
        }

        switch($type) {
        case 'array':
            $array = [];
            foreach ($node->childNodes as $child) {
                $value = self::_nodeToValue($child);
                if ($value !== null) {
                    $array[] = $value;
                }
            }
            return $array;
        case 'collection':
            $collection = [];
            foreach ($node->childNodes as $child) {
                $value = self::_nodetoValue($child);
                if ($value !== null) {
                    if (!isset($collection[$child->nodeName])) {
                        $collection[$child->nodeName] = [];
                    }
                    $collection[$child->nodeName][] = self::_nodeToValue($child);
                }
            }
            return $collection;
        default:
            $values = [];
            if ($node->childNodes->length === 1 && $node->childNodes->item(0) instanceof DOMText) {
                return $node->childNodes->item(0)->nodeValue;
            } else {
                foreach ($node->childNodes as $child) {
                    if (!$child instanceof DOMText) {
                        $values[$child->nodeName] = self::_nodeToValue($child);
                    }
                }
                return $values;
            }
        }
    }

    /**
     * Converts a node to a PHP value
     *
     * @param DOMNode $node
     * @return mixed
     */
    private static function _nodeToValue($node)
    {
        $type = null;
        if ($node instanceof DOMElement) {
            $type = $node->getAttribute('type');
        }

        switch($type) {
        case 'datetime':
            return self::_timestampToUTC((string) $node->nodeValue);
        case 'date':
            return new DateTime((string) $node->nodeValue);
        case 'integer':
            return (int) $node->nodeValue;
        case 'boolean':
            $value =  (string) $node->nodeValue;
            if(is_numeric($value)) {
                return (bool) $value;
            } else {
                return ($value !== "true") ? false : true;
            }
        case 'array':
        case 'collection':
            return self::_nodeToArray($node);
        default:
            if ($node->hasChildNodes()) {
                return self::_nodeToArray($node);
            } elseif (trim($node->nodeValue) === '') {
                return null;
            } else {
                return $node->nodeValue;
            }
        }
    }


    /**
     * Converts XML timestamps into DateTime instances
     *
     * @param string $timestamp
     * @return DateTime
     */
    private static function _timestampToUTC($timestamp)
    {
        $tz = new DateTimeZone('UTC');
        $dateTime = new DateTime($timestamp, $tz);
        $dateTime->setTimezone($tz);
        return $dateTime;
    }
}
