<?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;

class TypeCompiler extends AbstractCompiler
{
    /**
     * @return string
     */
    public function compile(): string
    {
        $this->content = '';
        foreach ($this->schema['types'] as $type => $def) {
            $this->consumeType($type, $def);
            $this->consumeFilterType($type, $def);
            $this->consumeSortType($type, $def);
        }

        // generate union types
        foreach ($this->schema['fields'] as $field => $def) {
            if (is_array($def['type'])) {
                $fields = [];
                foreach ($def['type'] as $type) {
                    if (isset($this->schema['types'][$type])) {
                        $fields = array_merge($fields, $this->schema['types'][$type]['fields']);
                    }
                }
                $this->consumeType($def['type'], ['fields' => $fields]);
                $this->consumeFilterType($def['type'], ['fields' => $fields]);
                $this->consumeSortType($def['type'], ['fields' => $fields]);
            }
        }

        return $this->content;
    }

    /**
     * @param $name
     * @param $def
     */
    protected function consumeType($name, $def)
    {
        $fields = ['new Field(["name" => "_uri", "type" => new StringType()])'];

        $hasRdfType = false;
        foreach ($def['fields'] as $field) {
            if ($field == "rdf:type") {
                $hasRdfType = true;
            }
            $fields[SchemaCompiler::normalize($field)] = 'new Field_'.SchemaCompiler::normalize($field).'()';
        }

        if (!$hasRdfType) {
            $fields[] = 'new Field_RdfType()';
        }

        $className = 'Type_'.SchemaCompiler::normalize($name);
        $this->content .= $this->render('type.twig', $className, [
            'parentClass' => 'AbstractObjectType',
            'name' => SchemaCompiler::normalize($name),
            'uri' => !is_array($name) ? $this->prefixMap->expandUri($name) : null,
            'fields' => $fields,
            'description' => @$def['description'],
        ]);
    }

    /**
     * @param $name
     * @param $def
     */
    protected function consumeFilterType($name, $def)
    {
        $className = 'FilterType_'.SchemaCompiler::normalize($name);
        $hasRdfType = false;

        $fields = [
            'new Field(["name" => "_or", "type" => new ListType(new '.$className.'())])',
            'new Field(["name" => "_and", "type" => new ListType(new '.$className.'())])',
            'new Field(["name" => "_not", "type" => new ListType(new '.$className.'())])',
            'new Field(["name" => "_eq", "type" => new StringType()])',
            'new Field(["name" => "_in", "type" => new ListType(new StringType())])',
            'new Field(["name" => "_nin", "type" => new ListType(new StringType())])',
        ];

        $fields[] = 'new GeoDistanceField()';
        $fields[] = 'new GeoBoundingField()';

        foreach ($def['fields'] as $field) {
            if ($field == "rdf:type") {
                $hasRdfType = true;
            }
            $fields[SchemaCompiler::normalize($field)] = 'new FilterField_'.SchemaCompiler::normalize($field).'()';
        }

        if (!$hasRdfType) {
            $fields[] = 'new FilterField_RdfType()';
        }
        
        $this->content .= $this->render('type.twig', $className, [
            'parentClass' => 'AbstractInputObjectType',
            'name' => SchemaCompiler::normalize($name).'_Filter',
            'uri' => !is_array($name) ? $this->prefixMap->expandUri($name) : null,
            'fields' => $fields,
            'description' => @$def['description'],
        ]);
    }

    /**
     * @param $name
     * @param $def
     */
    protected function consumeSortType($name, $def)
    {
        $fields = [];

        foreach ($def['fields'] as $field) {
            $fields[SchemaCompiler::normalize($field)] = 'new SortField_'.SchemaCompiler::normalize($field).'()';
        }

        $className = 'SortType_'.SchemaCompiler::normalize($name);
        $this->content .= $this->render('type.twig', $className, [
            'parentClass' => 'AbstractInputObjectType',
            'name' => SchemaCompiler::normalize($name).'_Sort',
            'uri' => !is_array($name) ? $this->prefixMap->expandUri($name) : null,
            'fields' => $fields,
            'description' => @$def['description'],
        ]);
    }
}
