<?php

namespace Dalten\RuianToUir\Command;

use Dalten\RuianToUir\Command\Helper\FileResolver;
use Dalten\RuianToUir\Converter\ConverterInterface;
use Dalten\RuianToUir\Formatter\PlainText;
use Dalten\RuianToUir\Formatter\Sql;
use Dalten\RuianToUir\Writer\MultipleFiles;
use Dalten\RuianToUir\Writer\PrintOut;
use Dalten\RuianToUir\Writer\WriterInterface;
use Symfony\Component\Console\Command\Command as BaseCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * Created by PhpStorm.
 * User: karel
 * Date: 20.02.2017
 * Time: 16:56.
 */
class Convert extends BaseCommand
{
    /**
     * @var ConverterInterface[]
     */
    private $converters;
    /**
     * @var array
     */
    private $skippedTags;
    /**
     * @var array
     */
    private $formatterNameToIdentifierMap;
    /**
     * @var array
     */
    private $formatterNameToTableNameMap;
    /**
     * @var FileResolver
     */
    private $fileResolver;

    /**
     * Convert constructor.
     *
     * @param FileResolver         $fileResolver
     * @param ConverterInterface[] $converters
     * @param string[]             $skippedTags
     * @param array                $formatterNameToTableNameMap
     * @param array                $formatterNameToIdentifierMap
     */
    public function __construct(FileResolver $fileResolver, array $converters, array $skippedTags,
        array $formatterNameToTableNameMap, array $formatterNameToIdentifierMap)
    {
        parent::__construct();

        $this->converters = $converters;
        $this->skippedTags = $skippedTags;
        $this->formatterNameToIdentifierMap = $formatterNameToIdentifierMap;
        $this->formatterNameToTableNameMap = $formatterNameToTableNameMap;
        $this->fileResolver = $fileResolver;
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return int|null
     *
     * @throws \Exception
     */
    public function execute(InputInterface $input, OutputInterface $output)
    {
        if ('sql' === $input->getOption('data-format')) {
            $formatter = new Sql($this->formatterNameToTableNameMap, $this->formatterNameToIdentifierMap);
        } else {
            $formatter = new PlainText();
        }

        switch ($input->getOption('output-format')) {
            case 'directory':
                $extension = $formatter instanceof Sql ? 'sql' : 'txt';
                /** @var string $directory */
                $directory = $input->getOption('output-directory');
                $writer = new MultipleFiles($formatter, (string) $directory, $extension);

                break;
            case 'print':
                $writer = new PrintOut($formatter);

                break;
            default:
                // TODO Lepší Exception
                throw new \Exception('Output formát "%s" nebyl rozpoznán. Známé formáty: ["print", "directory"].');

                break;
        }

        /** @var string $inputFile */
        $inputFile = $input->getArgument('input-file');
        $files = $this->fileResolver->resolveFiles($inputFile);

        $io = new SymfonyStyle($input, $output);

        if ($output->getVerbosity() > OutputInterface::VERBOSITY_QUIET) {
            $progress = $io->createProgressBar(count($files) - 1);
            $progress->start();
        }

        foreach ($files as $file) {
            try {
                $filename = (string) $file;
                $io->writeln(['# '.$filename, '']);

                $this->handleFile($filename, $writer);

                if (isset($progress)) {
                    $progress->advance();
                }
            } catch (\Exception $e) {
                $io->error($e->getMessage());

                return 1;
            }
        }

        if (isset($progress)) {
            $progress->finish();
        }

        return 0;
    }

    protected function configure()
    {
        $this->setName('convert');
        $this->setDescription('Převede XML soubor ze vstupu do formátu UIR.');
        $this->addArgument(
            'input-file', InputArgument::REQUIRED, 'Zdroj dat (soubor, adresář, maska adresáře app/cache/*/).'
        );
        $this->addOption('data-format', null, InputOption::VALUE_REQUIRED, 'Formát dat (text, sql)', 'text');
        $this->addOption(
            'output-format', null, InputOption::VALUE_REQUIRED, 'Formát výstupu (print, directory)', 'print'
        );
        $this->addOption(
            'output-directory', null, InputOption::VALUE_REQUIRED, 'Cílový adresář (pro output-format=directory)',
            './output'
        );
    }

    /**
     * @param string          $filename
     * @param WriterInterface $writer
     */
    private function handleFile($filename, WriterInterface $writer)
    {
        $reader = new \XMLReader();
        if (false === $reader->open($filename)) {
            throw new \InvalidArgumentException(sprintf('Soubor %s nelze otevřít!', $filename));
        }

        while ($reader->read()) {
            while (in_array($reader->name, $this->skippedTags)) {
                $reader->next();
            }

            if (\XMLReader::ELEMENT === $reader->nodeType) {
                foreach ($this->converters as $converter) {
                    if ($converter->accepts($reader)) {
                        $writer->write($converter->convert($reader), $converter->getName());
                    }
                }
            }
        }

        $reader->close();
    }
}
