<?php
/**
 * This file is part of the DATAtourisme project.
 *
 *  @author Conjecto <contact@conjecto.com>
 *
 * For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
 */

namespace Datatourisme\Api\Resolver\SparqlResolver;

use Datatourisme\Api\Resolver\SparqlResolver\Utils\SparqlUtils;
use Youshido\GraphQL\Parser\Ast\ArgumentValue\InputList;
use EasyRdf\Graph;
use EasyRdf\Resource;
use EasyRdf\Literal;

class QueryHydrator
{
    /**
     * @param array $args
     * @param $astFields
     * @param string $sparqlResult
     *
     * @return array
     *
     */
    public function generate(array $args, $astFields, $sparqlResult = '')
    {
        /** @var resource[] $listRes */
        $listRes = [];
        if ($sparqlResult) {
            $graph = new Graph();
            $graph->parse($sparqlResult);

            //$type = SparqlUtils::expandUri($entryPoint);
            $listRes = $graph->allOfType('urn:resource');
        }

        // first sort by uri
        usort($listRes, function ($a, $b) {
            return strcmp($a->getUri(), $b->getUri());
        });

        // hydrate results
        $tabResult = [];
        foreach ($listRes as $res) {
            $tabResult[] = $this->consume($astFields, $res, $args);
        }

        // re-apply sorts
        $sortList = (isset($args['sort'])) ? $args['sort'] : [];
        $tabResult = $this->sortResult($sortList, $tabResult);

        return $tabResult;
    }

    /**
     * @param array    $fields
     * @param resource $res
     *
     * @return mixed
     */
    private function consume(array $fields, $res, $args)
    {
        $tabResult = [];
        if ($res) {
            foreach ($fields as $field) {
                $fieldName = $field->getName();

                if ('_uri' == $fieldName) {
                    $tabResult['_uri'] = $res->getUri();
                    continue;
                }

                $property = new Resource(SparqlUtils::expandUri($fieldName));

                if ($field->hasFields()) {
                    $tabResult[$fieldName] = [];
                    $listRes = $res->all($property);
                    foreach ($listRes as $resourceValue) {
                        if (isset($args['lang']) && $resourceValue instanceof Literal && $resourceValue->getLang() && $resourceValue->getLang() != $args['lang']) {
                            continue;
                        }
                        $tabResult[$fieldName][] = $this->consume($field->getFields(), $resourceValue, $args);
                    }
                } elseif ($res instanceof Literal) {
                    switch ($fieldName) {
                        case 'value':  $tabResult[$fieldName] = $res->getValue(); break;
                        case 'lang':  $tabResult[$fieldName] = $res->getLang(); break;
                    }
                } elseif ($res instanceof Resource && $res->hasProperty($property)) {
                    $tabResult[$fieldName] = [];
                    /** @var Resource $res */
                    $propertyValues = $res->all($property);
                    foreach ($propertyValues as $value) {
                        if ($value instanceof Literal) {
                            if (isset($args['lang']) && $value->getLang() && $value->getLang() != $args['lang']) {
                                continue;
                            }
                            $tabResult[$fieldName][] = $value->getValue();
                        } elseif ($value instanceof Resource) {
                            if (!$value->isBNode()) {
                                $tabResult[$fieldName][] = $value->getUri();
                            }
                        }
                    }
                }
            }
        }

        return $tabResult;
    }

    /**
     * @param array $sortList
     * @param $tab
     *
     * @return mixed
     */
    private function sortResult($sortList, $tab)
    {
        if (null == $sortList) {
            return $tab;
        }
        /* @var InputList $pup */
        foreach ($sortList as $sort) {
            uasort($tab, function ($a, $b) use ($sort) {
                $key = key($sort);
                $order = reset($sort[$key]);

                if (!isset($a[$key])) {
                    return -1;
                }
                if (!isset($b[$key])) {
                    return 0;
                }
                if (is_array($a[$key])) {
                    $a = reset($a[$key]);
                    $b = reset($b[$key]);
                } else {
                    $a = $a[$key];
                    $b = $b[$key];
                }
                if ($a == $b) {
                    return 0;
                }
                if ('asc' == $order) {
                    return ($a < $b) ? -1 : 1;
                } else {
                    return ($a > $b) ? -1 : 1;
                }
            });
        }

        return $tab;
    }
}
