<?php

/*
 * SchemaConverter
 * Copyright 2019 TopWebStudio. All Rights Reserved.
 * Distribution allowed only with licensing permission
 * contact@topwebstudio.com  
 */

namespace Topwebstudio\Parsers;

class SchemaConverter {

    private $functions;

    public function __construct($input) {
        $this->tree = $this->buildTree($input);
    }

    public function setFunctions($functions) {
        $this->functions = $functions;
    }

    protected function getFunctions() {
        return $this->functions;
    }

    public function getTree() {
        return $this->tree;
    }

    public function getOutput() {
        $this->output = $this->convertArrayData($this->tree);

        return $this->output;
    }

    protected function buildTree($flat, $pidKey = 'parent_id', $idKey = 'id') {
        $grouped = array();

        foreach ($flat as $sub) {
            $grouped[$sub[$pidKey]][] = $sub;
        }

        $fnBuilder = function($siblings) use (&$fnBuilder, $grouped, $idKey) {
            foreach ($siblings as $k => $sibling) {
                $id = $sibling[$idKey];

                if (isset($grouped[$id])) {
                    $sibling['children'] = $fnBuilder($grouped[$id]);
                }
                $siblings[$k] = $sibling;
            }

            return $siblings;
        };

        $tree = $fnBuilder($grouped[0]);

        return $tree;
    }

    protected function convertArrayData( $arrayData) {
       if(!is_array($arrayData) || empty($arrayData)) {
            return array();   
        }
        
        
        $result = array();

        foreach ($arrayData as $arrayElem) {
            if (empty($arrayElem['key']) && empty($arrayElem['value']) && isset($arrayElem['children'])) {
                $result[] = $this->convertArrayData($arrayElem['children']);

                continue;
            }

            $value = $arrayElem['value'];

            if (empty($arrayElem['key']) && empty($value) && !isset($arrayElem['children'])) {
                continue;
            }

            if (empty($arrayElem['key'])) {

                $value = $this->applyFunctions($value);
                if ($this->is_serialized($value) || is_array($value)) {
                    if ($this->is_serialized($value)) {
                        $value = unserialize($value);
                    }

                    if (is_array($value) && !empty($value)) {
                        foreach ($value as $_value) {
                            $result[] = $_value;
                        }
                    }
                } else {
                    $result[] = $value;
                }

                continue;
            }

            if (isset($arrayElem['children'])) {
                $result[$arrayElem['key']] = $this->convertArrayData($arrayElem['children']);
            } else {

                $value = $this->applyFunctions($value);
                if ($this->is_serialized($value) || is_array($value)) {
                    if ($this->is_serialized($value)) {
                        $value = unserialize($value);
                    }
                    if (is_array($value) && !empty($value)) {
                        foreach ($value as $_value) {
                            $result[$arrayElem['key']][] = $_value;
                        }
                    } else {
                        $result[$arrayElem['key']] = $value;
                    }
                } else {
                    $result[$arrayElem['key']] = $this->applyFunctions($value);
                }
            }
        }

        return $result;
    }

    protected function applyFunctions($value) {
        if ($this->getFunctions()) {
            foreach ($this->getFunctions() as $function => $parameter) {
                $value = call_user_func($function, $value, $parameter);
            }
        }
        return $value;
    }

    protected function is_serialized($data, $strict = true) {
        // if it isn't a string, it isn't serialized.
        if (!is_string($data)) {
            return false;
        }
        $data = trim($data);
        if ('N;' == $data) {
            return true;
        }
        if (strlen($data) < 4) {
            return false;
        }
        if (':' !== $data[1]) {
            return false;
        }
        if ($strict) {
            $lastc = substr($data, -1);
            if (';' !== $lastc && '}' !== $lastc) {
                return false;
            }
        } else {
            $semicolon = strpos($data, ';');
            $brace = strpos($data, '}');
            // Either ; or } must exist.
            if (false === $semicolon && false === $brace) {
                return false;
            }
            // But neither must be in the first X characters.
            if (false !== $semicolon && $semicolon < 3) {
                return false;
            }
            if (false !== $brace && $brace < 4) {
                return false;
            }
        }
        $token = $data[0];
        switch ($token) {
            case 's':
                if ($strict) {
                    if ('"' !== substr($data, -2, 1)) {
                        return false;
                    }
                } elseif (false === strpos($data, '"')) {
                    return false;
                }
            // or else fall through
            case 'a':
            case 'O':
                return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);
            case 'b':
            case 'i':
            case 'd':
                $end = $strict ? '$' : '';
                return (bool) preg_match("/^{$token}:[0-9.E-]+;$end/", $data);
        }
        return false;
    }

}
