<?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\Schema\Compiler;

use Datatourisme\Api\Schema\Type\Object\LangStringType;
use Datatourisme\Api\Schema\Type\Scalar\BooleanType;
use Datatourisme\Api\Schema\Type\Scalar\FloatType;
use Datatourisme\Api\Schema\Type\Scalar\IntType;
use Symfony\Component\Yaml\Yaml;
use Youshido\GraphQL\Type\Scalar\DateTimeType;
use Youshido\GraphQL\Type\Scalar\StringType;

class SchemaCompiler
{
    private $twig;

    private $typesMap = array();

    public static $defaultTypesMap = [
        'http://www.w3.org/2001/XMLSchema#string' => StringType::class,
        'http://www.w3.org/2001/XMLSchema#boolean' => BooleanType::class,
        'http://www.w3.org/2001/XMLSchema#bool' => BooleanType::class,
        'http://www.w3.org/2001/XMLSchema#int' => IntType::class,
        'http://www.w3.org/2001/XMLSchema#integer' => IntType::class,
        'http://www.w3.org/2001/XMLSchema#decimal' => FloatType::class,
        'http://www.w3.org/2001/XMLSchema#float' => FloatType::class,
        'http://www.w3.org/2001/XMLSchema#date' => DateTimeType::class,
        'http://www.w3.org/2001/XMLSchema#dateTime' => DateTimeType::class,
        'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString' => LangStringType::class,
    ];

    /**
     * @param array $typesMap
     */
    public function setTypesMap(array $typesMap)
    {
        $this->typesMap = $typesMap;
    }

    /**
     * @return array
     */
    public function getTypesMap()
    {
        return array_merge(self::$defaultTypesMap, $this->typesMap);
    }

    /**
     * @param $name
     *
     * @return string
     */
    public function resolveType($name)
    {
        if (isset($this->getTypesMap()[$name])) {
            return $this->getTypesMap()[$name];
        }

        return StringType::class;
    }

    /**
     * Compiles a file.
     *
     * @param string $schemaFile
     * @param string $namespace
     *
     * @return string
     *
     * @throws \Twig_Error_Loader
     * @throws \Twig_Error_Runtime
     * @throws \Twig_Error_Syntax
     */
    public function compile(string $schemaFile, string $namespace): string
    {
        $schema = Yaml::parse(file_get_contents($schemaFile));
        $content = $this->render('schema.twig', [
            'namespace' => $namespace,
            'prefixes' => @$schema['prefixes'] ?: [],
            'name' => self::normalize(pathinfo($schemaFile)['filename']),
        ]);

        $content .= (new TypeCompiler($this, $schema))->compile();
        $content .= (new FieldCompiler($this, $schema))->compile();

        return $content;
    }

    /**
     * @param $template
     * @param array $params
     *
     * @return string
     *
     * @throws \Twig_Error_Loader
     * @throws \Twig_Error_Runtime
     * @throws \Twig_Error_Syntax
     */
    public function render($template, $params = [])
    {
        if (!$this->twig) {
            $loader = new \Twig_Loader_Filesystem(dirname(__FILE__).'/templates');
            $this->twig = new \Twig_Environment($loader, array(
                'cache' => false,
                'debug' => false,
            ));
        }

        return $this->twig->render($template, $params);
    }

    /**
     * @param $string
     *
     * @return string
     */
    public static function normalize($string)
    {
        if (is_array($string)) {
            return implode('_', array_map(function ($s) { return self::normalize($s); }, $string));
        }
        $parts = preg_split('/[^_a-zA-Z0-9]/', $string);
        $parts = array_filter($parts);

        return implode('_', $parts);
    }
}
