<?php
namespace Newland\Toubiz\Sync\Neos\Command;

use CrEOF\Spatial\PHP\Types\AbstractGeometry;
use CrEOF\Spatial\PHP\Types\Geometry\Polygon;
use Doctrine\ORM\AbstractQuery;
use Newland\Toubiz\Sync\Neos\Domain\Model\Geometry;
use Neos\Flow\Annotations as Flow;
use Newland\Toubiz\Sync\Neos\Domain\Repository\GeometryRepository;
use Newland\Toubiz\Sync\Neos\Geometry\CreofDoctrineSpatialFactory;
use Newland\Toubiz\Sync\Neos\Geometry\GeoJsonParser;
use function Safe\file_get_contents;
use function Safe\json_decode;

/**
 * @Flow\Scope("singleton")
 */
class GeometryCommandController extends AbstractCommandController
{
    const IMPORT_FROM = 'resource://Newland.Toubiz.Sync.Neos/Private/GeoJson/' .
        '2019-03-13__suche-postleitzahl-org__5-digit.geojson';

    /**
     * @var GeometryRepository
     * @Flow\Inject()
     */
    protected $geometryRepository;

    public function seedZipPolygonsCommand()
    {
        $this->outputLine('Remove previosly imported zips');
        $this->geometryRepository->removeByType(Geometry::TYPE_ZIP);

        $this->outputLine('Loading GeoJson file from ' . static::IMPORT_FROM);
        $geoJson = json_decode(file_get_contents(static::IMPORT_FROM), true);

        $this->outputLine('Parsing GeoJson');
        $polygons = $this->extractZipsAndCoordinatesFromGeoJson($geoJson);

        $this->outputLine('Importing GeoJson as Geometry');
        $this->output->progressStart(\count($polygons));
        foreach ($polygons as $polygon) {
            $geometry = new Geometry();
            $geometry->setType(Geometry::TYPE_ZIP);
            $geometry->setLabel($polygon['zip']);
            $geometry->setGeometry($polygon['geometry']);

            $this->geometryRepository->add($geometry);
            $this->output->progressAdvance();
        }
        $this->output->progressFinish();
    }

    public function zipForCoordinatesCommand(float $longitude, float $latitude)
    {
        $geometry = $this->geometryRepository->labelContainingCoordinates(Geometry::TYPE_ZIP, $longitude, $latitude);
        if (!$geometry) {
            $this->outputLine('No geometry found');
        } else {
            $this->outputLine($geometry);
        }
    }

    /**
     * Removes geometries from the system according to the given clause.
     * If no WHERE clause is given then all geometries will be deleted.
     *
     * # Remove all geometries
     * $ php flow geometry:remove
     *
     * # Remove geometries according to WHERE clause
     * $ php flow geometry:remove --where='geometry.label LIKE "77%"'
     *
     * # Remove single geometry
     * $ php flow geometry:remove \
     *      --where="geometry.Persistence_Object_Identifier='a4625eb4-6e84-4834-969d-c9f1d447408b'"
     *
     *
     * @param string|null $where DQL WHERE clause selecting the geometries to delete.
     * @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException
     */
    public function removeCommand(string $where = null)
    {
        $query = $this->geometryRepository->createQueryBuilder('geometry');
        if ($where) {
            $query->where($where);
            $this->outputLine('Deleting geometries WHERE ' . $where);
        } else {
            $this->outputLine('Deleting all geometries');
        }

        $count = (clone $query)->select('COUNT(geometry) AS count')
                ->getQuery()
                ->execute([], AbstractQuery::HYDRATE_ARRAY)[0]['count'] ?? 0;
        $this->askForConfirmationAndAbortIfNoneGiven(sprintf('Do you really want to remove %d geometries?', $count));

        $this->output->progressStart($count);
        foreach ($query->getQuery()->execute() as $geometry) {
            $this->geometryRepository->remove($geometry);
            $this->output->progressAdvance();
        }

        $this->output->progressFinish();
    }


    private function extractZipsAndCoordinatesFromGeoJson(array $geoJson)
    {
        $polygons = [];
        $factory = new CreofDoctrineSpatialFactory();

        foreach ($geoJson['features'] as $feature) {
            $type = $feature['geometry']['type'];
            $zip = $feature['properties']['plz'] ?? null;
            $coordinates = $feature['geometry']['coordinates'] ?? null;

            if ($zip && $coordinates) {
                $polygons[] = [
                    'zip' => $zip,
                    'geometry' => $factory->create($type, $coordinates),
                ];
            }
        }
        return $polygons;
    }
}
