#!/usr/bin/env php
<?php

use Illuminate\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\View\Compilers\BladeCompiler;
use Illuminate\View\Engines\CompilerEngine;
use Illuminate\View\Engines\EngineResolver;
use Illuminate\View\Engines\PhpEngine;
use Illuminate\View\Factory;
use Illuminate\View\FileViewFinder;
use Mni\FrontYAML\Bridge\Parsedown\ParsedownParser;
use Mni\FrontYAML\Bridge\Symfony\SymfonyYAMLParser;
use Mni\FrontYAML\Markdown\MarkdownParser;
use Mni\FrontYAML\Parser;
use Mni\FrontYAML\YAML\YAMLParser;
use TightenCo\Jigsaw\BladeDirectivesFile;
use TightenCo\Jigsaw\CollectionDataLoader;
use TightenCo\Jigsaw\CollectionItemHandlers\BladeCollectionItemHandler;
use TightenCo\Jigsaw\CollectionItemHandlers\MarkdownCollectionItemHandler;
use TightenCo\Jigsaw\Collection\CollectionPaginator;
use TightenCo\Jigsaw\ConfigFile;
use TightenCo\Jigsaw\Console\BuildCommand;
use TightenCo\Jigsaw\Console\InitCommand;
use TightenCo\Jigsaw\Console\ServeCommand;
use TightenCo\Jigsaw\DataLoader;
use TightenCo\Jigsaw\File\Filesystem;
use TightenCo\Jigsaw\File\TemporaryFilesystem;
use TightenCo\Jigsaw\Handlers\BladeHandler;
use TightenCo\Jigsaw\Handlers\CollectionItemHandler;
use TightenCo\Jigsaw\Handlers\DefaultHandler;
use TightenCo\Jigsaw\Handlers\IgnoredHandler;
use TightenCo\Jigsaw\Handlers\MarkdownHandler;
use TightenCo\Jigsaw\Handlers\PaginatedPageHandler;
use TightenCo\Jigsaw\Jigsaw;
use TightenCo\Jigsaw\Parsers\FrontMatterParser;
use TightenCo\Jigsaw\PathResolvers\BasicOutputPathResolver;
use TightenCo\Jigsaw\PathResolvers\CollectionPathResolver;
use TightenCo\Jigsaw\SiteBuilder;
use TightenCo\Jigsaw\View\BladeMarkdownEngine;
use TightenCo\Jigsaw\View\MarkdownEngine;
use TightenCo\Jigsaw\View\ViewRenderer;

if (file_exists(__DIR__.'/vendor/autoload.php')) {
    require __DIR__.'/vendor/autoload.php';
} else {
    require __DIR__.'/../../autoload.php';
}

$cachePath = getcwd() . '/_tmp';
$bootstrapFile = getcwd() . '/bootstrap.php';

$container = new Container;
$container->instance('cwd', getcwd());
$container->instance('buildPath', [
    'source' => getcwd() . '/source',
    'destination' => getcwd() . '/build_{env}',
]);

$container->bind('config', function () {
    return (new ConfigFile(getcwd() . '/config.php'))->config;
});

$container->bind('outputPathResolver', function ($c) {
    return new BasicOutputPathResolver;
});

$container->bind(YAMLParser::class, SymfonyYAMLParser::class);

$container->bind(MarkdownParser::class, ParsedownParser::class);

$container->bind(Parser::class, function ($c) {
    return new Parser($c[YAMLParser::class], $c[MarkdownParser::class]);
});

$container->bind(FrontMatterParser::class, function ($c) {
    return new FrontMatterParser($c[Parser::class]);
});

$container->bind(Factory::class, function ($c) use ($cachePath) {
    $resolver = new EngineResolver;

    $bladeCompiler = new BladeCompiler(new Filesystem, $cachePath);
    $compilerEngine = new CompilerEngine($bladeCompiler, new Filesystem);

    $resolver->register('blade', function () use ($compilerEngine) {
        return $compilerEngine;
    });

    $resolver->register('php', function () {
        return new PhpEngine();
    });

    $resolver->register('markdown', function () use ($c) {
        return new MarkdownEngine($c[FrontMatterParser::class], new Filesystem, $c['buildPath']['source']);
    });

    $resolver->register('blade-markdown', function () use ($c, $compilerEngine) {
        return new BladeMarkdownEngine($compilerEngine, $c[FrontMatterParser::class]);
    });

    (new BladeDirectivesFile(getcwd() . '/blade.php'))->register($bladeCompiler);

    $finder = new FileViewFinder(new Filesystem, [$c['buildPath']['source']]);

    return new Factory($resolver, $finder, Mockery::mock(Dispatcher::class)->shouldIgnoreMissing());
});

$container->bind(ViewRenderer::class, function ($c) {
    return new ViewRenderer($c[Factory::class]);
});

$container->bind(BladeHandler::class, function ($c) {
    return new BladeHandler($c[TemporaryFilesystem::class], $c[FrontMatterParser::class], $c[ViewRenderer::class]);
});

$container->bind(TemporaryFilesystem::class, function ($c) use ($cachePath) {
    return new TemporaryFilesystem($cachePath);
});

$container->bind(MarkdownHandler::class, function ($c) {
    return new MarkdownHandler($c[TemporaryFilesystem::class], $c[FrontMatterParser::class], $c[ViewRenderer::class]);
});

$container->bind(CollectionPathResolver::class, function ($c ) {
    return new CollectionPathResolver($c['outputPathResolver'], $c[ViewRenderer::class]);
});

$container->bind(CollectionDataLoader::class, function ($c) {
    return new CollectionDataLoader(new Filesystem, $c[CollectionPathResolver::class], [
        $c[MarkdownCollectionItemHandler::class],
        $c[BladeCollectionItemHandler::class],
    ]);
});

$container->bind(DataLoader::class, function ($c) {
    return new DataLoader($c[CollectionDataLoader::class]);
});

$container->bind(CollectionItemHandler::class, function ($c) {
    return new CollectionItemHandler($c['config'], [
        $c[MarkdownHandler::class],
        $c[BladeHandler::class],
    ]);
});

$container->bind(CollectionPaginator::class, function ($c) {
    return new CollectionPaginator($c['outputPathResolver']);
});

$container->bind(PaginatedPageHandler::class, function ($c) {
    return new PaginatedPageHandler($c[CollectionPaginator::class], $c[FrontMatterParser::class], $c[TemporaryFilesystem::class], $c[ViewRenderer::class]);
});

$container->bind(SiteBuilder::class, function ($c) use ($cachePath) {
    return new SiteBuilder(new Filesystem, $cachePath, $c['outputPathResolver'], [
        $c[CollectionItemHandler::class],
        new IgnoredHandler,
        $c[PaginatedPageHandler::class],
        $c[MarkdownHandler::class],
        $c[BladeHandler::class],
        $c[DefaultHandler::class],
    ]);
});

if (file_exists($bootstrapFile)) {
    include $bootstrapFile;
}

$container->bind(Jigsaw::class, function ($c) {
    return new Jigsaw($c, $c[DataLoader::class], $c[SiteBuilder::class]);
});

$buildCommand = new BuildCommand($container);
$serveCommand = new ServeCommand($container);

$app = new Symfony\Component\Console\Application('Jigsaw', '1.0.4');
$app->add($container[InitCommand::class]);
$app->add($buildCommand);
$app->add($serveCommand);
$app->run();
