<?php

namespace Dalten\RuianToUir\Command;

use Dalten\RuianToUir\Command\Helper\FileResolver;
use Dalten\RuianToUir\Converter\ConverterInterface;
use Dalten\RuianToUir\Converter\ExplorerWrapper;
use Symfony\Component\Console\Command\Command as BaseCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
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 Explore extends BaseCommand
{
    /**
     * @var ConverterInterface[]
     */
    private $converters;
    /**
     * @var array
     */
    private $skippedTags;
    /**
     * @var FileResolver
     */
    private $fileResolver;

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

        $this->converters = array_map(
            function ($converter) {
                return new ExplorerWrapper($converter);
            }, $converters
        );
        $this->skippedTags = $skippedTags;
        $this->fileResolver = $fileResolver;
    }

    public function execute(InputInterface $input, OutputInterface $output)
    {
        /** @var string $inputFile */
        $inputFile = $input->getArgument('input-file');
        $files = $this->fileResolver->resolveFiles($inputFile);
        $io = new SymfonyStyle($input, $output);

        foreach ($files as $file) {
            try {
                $io->comment((string) $file);
                $this->handleFile((string) $file, $io);
            } catch (\Exception $e) {
                $io->error($e->getMessage());

                return 1;
            }
        }

        return 0;
    }

    /**
     * @param string       $filename
     * @param SymfonyStyle $io
     */
    private function handleFile($filename, SymfonyStyle $io)
    {
        $skippedTags = $this->skippedTags;

        $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, $skippedTags)) {
                $reader->next();
            }

            if (\XMLReader::ELEMENT === $reader->nodeType) {
                foreach ($this->converters as $converter) {
                    if ($converter->accepts($reader)) {
                        $skippedTags[] = $reader->name;
                        $data = $converter->convert($reader);
                        $io->title($converter->getName());

                        $depth = 1;
                        $io->writeln('');
                        $this->writeArray($io, $data, $depth);
                    }
                }
            }
        }

        $reader->close();
    }

    /**
     * @param SymfonyStyle $io
     * @param array        $data
     * @param int          $depth
     */
    private function writeArray(SymfonyStyle $io, array $data, $depth)
    {
        foreach ($data as $key => $value) {
            $expand = false;
            $row = [str_pad('', $depth, '.'), $key, ': '];
            if (null === $value) {
                $row[] = '~';
            } elseif (!is_array($value)) {
                $row[] = "'$value'";
            } else {
                $expand = true;
                $row[] = $value ? '' : '[]';
            }

            $io->text(implode('', $row));
            if ($expand) {
                $this->writeArray($io, $value, ($depth + 1));
            }
        }
    }

    protected function configure()
    {
        $this->setName('explore');
        $this->setDescription('Zobrazí přehledně strukturu jednotlivých prvků v XML souboru.');
        $this->addArgument(
            'input-file', InputArgument::REQUIRED, 'Zdroj dat (soubor, adresář, maska adresáře app/cache/*/).'
        );
    }
}
