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

// Composer Autoload

// If we have cloned the repository this should work!
if (file_exists(__DIR__.'/vendor/autoload.php')) {
	include __DIR__.'/vendor/autoload.php';
}
// If we have installed this globally this should work!
elseif (file_exists(getenv('HOME').'/.composer/vendor/autoload.php')) {
	include getenv('HOME').'/.composer/vendor/autoload.php';
}
// User *MOST* likely hasn't run `composer install`
else {
	$str = <<<'EOF'
Oops! We can't find the Composer autoload file!

If you have cloned the repository make sure you
run `composer install` before trying to run this
program.

If you are still having issues ask for help with
GitHub issues on the repository.

If you would like to contact me directly feel free
to email me on david@team-reflex.com.
EOF;
	echo $str.PHP_EOL;
	die(1);
}

@list(, $dir, $port, $hostname, $loglevel) = $_SERVER['argv'];

if (is_null($dir)) {
	echo 'The <dir> field is required.'.PHP_EOL;
	echo 'Usage: `./react-laravel <laravel-dir> [port] [hostname]'.PHP_EOL;
	die(1);
}

require $dir.'/bootstrap/autoload.php';

$port = (is_null($port)) ? 8080 : $port;
$hostname = (is_null($hostname)) ? '127.0.0.1' : $hostname;
$loglevel = (is_null($loglevel)) ? 'INFO' : strtoupper($loglevel);

$levels = \Monolog\Logger::getLevels();

if (!isset($levels[$loglevel])) {
	$loglevel = \Monolog\Logger::INFO;
} else {
	$loglevel = $levels[$loglevel];
}

$log = new \Monolog\Logger('react-laravel');
$log->pushHandler(new \Monolog\Handler\StreamHandler('php://stdout', $loglevel));

$loop = \React\EventLoop\Factory::create();
$socket = new \React\Socket\Server($loop);
$http = new \React\Http\Server($socket);

// Init Laravel
$app = require_once $dir.'/bootstrap/app.php';
$kernel = $app->make('Illuminate\Contracts\Http\Kernel');

$http->on('request', function ($request, $response) use ($kernel, $dir, $log) {
	$st = microtime(true);
	$ua = isset($request->getHeaders()['User-Agent']) ? $request->getHeaders()['User-Agent'] : 'Unknown User-Agent';
	$log->addInfo("{$request->getMethod()} {$request->getPath()} - {$ua}");

	$headers = $request->getHeaders();

	if (file_exists($dir.'/public/'.$request->getPath()) && false === array_search($request->getPath(), ['/', '/index.php'])) {
		$date = \DateTime::createFromFormat('U', filemtime($dir.'/public/'.$request->getPath()));
		$date->setTimezone(new \DateTimeZone('UTC'));

		if (isset($headers['If-None-Match'])) {
			$etag = $request->getPath().$request->getRemoteAddress();
			$etag = md5($etag);
			$etag = "\"{$etag}\"";

			if (isset($headers['If-Modified-Since'])) {
				$modified = \DateTime::createFromFormat('D, d M Y H:i:s T', $headers['If-Modified-Since']);
				
				if (!($date->format('U') > $modified->format('U'))) {
					if ($headers['If-None-Match'] == $etag) {
						$response->writeHead(304, ['ETag' => $etag, 'Last-Modified' => $date->format('D, d M Y H:i:s').' GMT']);
						$response->end();
						return;
					}
				}
			}
		}

		$rHeaders = [];
		
		try {
			$type = new \Hoa\Mime\Mime(new \Hoa\File\Read($dir.'/public/'.$request->getPath()));
			$rHeaders['Content-Type'] = $type->getMime();
		} catch (\Hoa\Mime\Exception\MimeIsNotFound $e) {} // Do nothing, we will just send with no Content-Type

		$etag = $request->getPath().$request->getRemoteAddress();
		$etag = md5($etag);

		$rHeaders['ETag'] = "\"{$etag}\"";
		$rHeaders['Last-Modified'] = $date->format('D, d M Y H:i:s').' GMT';

		$response->writeHead(200, $rHeaders);
		$response->end(file_get_contents($dir.'/public/'.$request->getPath()));
		return;
	}

	$body = $request->getPost();
	$cookies = [];
	$files = [];

	if (isset($headers['Cookie'])) {
		$hCookies = explode(';', $headers['Cookie']);

		foreach ($hCookies as $cookie) {
			$cookie = explode('=', trim($cookie));

			if (!isset($cookie[1])) {
				$cookies[] = $cookie;
				continue;
			}

			list($name, $value) = $cookie;
			$cookies[$name] = $value;
		}
	}

	$lRequest = new \Illuminate\Http\Request(
		$request->getQuery(), // GET Parameters
		$body, // POST Parameters
		[], // Request Attributes
		$cookies, // Cookies
		$files, // Files
		[], // Server Parameters
		$request->getBody() // Raw Request body
	);
	$lRequest->setMethod($request->getMethod());
	$lRequest->headers->replace($headers);
	$lRequest->server->set('REQUEST_URI', $request->getPath());
	$lRequest->server->set('SERVER_NAME', explode(':', $headers['Host'])[0]);

	$lResponse = $kernel->handle(
		$lRequest
	);

	$resHeaders = [];

	foreach ($lResponse->headers->allPreserveCase() as $key => $values) {
		$resHeaders[$key] = $values[0];
	}

	foreach ($lResponse->headers->getCookies() as $cookie) {
		$resHeaders['Set-Cookie'][] = (string) $cookie;
	}

	if (!isset($resHeaders['Date'])) {
		$date = \DateTime::createFromFormat('U', time());
		$date->setTimezone(new \DateTimeZone('UTC'));
		$resHeaders['Date'] = $date->format('D, d M Y H:i:s').' GMT';
	}

	$response->writeHead($lResponse->getStatusCode(), $resHeaders);
	$response->end($lResponse->getContent());

	$kernel->terminate($lRequest, $lResponse);

	$et = microtime(true);
	$diff = $et - $st;
	$log->addDebug("{$request->getMethod()} {$request->getPath()}: Completed in {$diff} second(s).");
});

$socket->listen($port, $hostname);
$log->addInfo("Server will listen on {$hostname}:{$port}");
$loop->run();
