<?php
/**
 * CONFIG - basic application preparation
 */
error_reporting(E_ALL);
ini_set('display_errors', 1);
define('VENDOR_AUTOLOADER', 'vendor/autoload.php');
session_cache_limiter(false);
session_start();

require_once __DIR__ . '/../'.VENDOR_AUTOLOADER;
require __DIR__ . '/../vendor/carbon/autoload.php';
require_once __DIR__ . '/../config.php';

\lib\Session::init(new lib\PhpSessionAdapter());

// Setup custom Twig view
$twigView = new \Slim\Views\Twig();

/**
 * VIEW CACHING - change app.cache in config to toggle
 */
$appCache = lib\Config::read('app.cache');
if ($appCache) {
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        $twigView->parserOptions['cache'] = '/windows/temp/twigcache';
    } else {
        $twigView->parserOptions['cache'] = '/tmp/twigcache';
    }
} else {
    $twigView->parserOptions['cache'] = false;
}

/**
 * DEBUGGING - change app.debug in config to toggle
 */
$appDebug = lib\Config::read('app.debug');
$twigView->parserOptions['debug'] = $appDebug;

if ($appDebug) {
    $twigView->parserExtensions[] = 'Twig_Extension_Debug';
}

$templatesPath = __DIR__ . '/../templates/';

$app = new \Slim\Slim(array(
    'debug' => $appDebug,
    'view' => $twigView,
    'templates.path' => $templatesPath,
));

$twig = $app->view()->getEnvironment();
$twigFriendlyDate = new Twig_SimpleFilter("friendlyDate", function ($timestamp) {
    $date = new \lib\Utils;
    return $date->friendlyDate($timestamp);

});
$twig->addFilter($twigFriendlyDate);

$twigHtmlEntityDecode = new Twig_SimpleFilter("htmlEntityDecode", function ($string) {
    return html_entity_decode($string);
});
$twig->addFilter($twigHtmlEntityDecode);

$twigIsImageUrl = new Twig_SimpleFilter("isImageUrl", function ($value) {
    if (!is_string($value) || empty($value)) {
        return false;
    }
    if (!filter_var($value, FILTER_VALIDATE_URL)) {
        return false;
    }
    $path = strtolower(parse_url($value, PHP_URL_PATH) ?: '');
    return (bool) preg_match('/\.(jpe?g|png|gif|bmp|webp|svg|ico|tiff?)$/i', $path);
});
$twig->addFilter($twigIsImageUrl);

// Set up time zone
$timezone = lib\Config::read('timezone');
date_default_timezone_set($timezone);
ini_set('date.timezone', $timezone);

// We don't want any browser caching going on
$app->response->headers->set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
$app->response->headers->set('Pragma', 'no-cache');
$app->response->headers->set('Expires', date("D, d M Y H:i:s T"));

// Cache instantiation
// same as in public/soap/index.php
$app->container->singleton('cache', function() {
    $cache = null;
    $isCacheEnabled = lib\Config::read('app.phpcache');

    if ($isCacheEnabled) {
        require_once(__DIR__ .'/../vendor/phpfastcache/phpfastcache.php');
        phpFastCache::setup('storage', 'files');
        $cache = phpFastCache();
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            $cache->option('path', '/windows/temp');
        } else {
            $cache->option('path', '/tmp');
        }
        $cache->option('securityKey', 'ttsentry.cache');
    }

    return $cache;
});

// db instantiation
// Push our DB obj to singleton container
$app->container->singleton('db', function() {

    $dsn = 'mysql:host=' . lib\Config::read('db.host') .
           ';dbname='    . lib\Config::read('db.basename') .
           ';port='      . lib\Config::read('db.port') .
           ';connect_timeout=15';
    // getting DB user from config                
    $user = lib\Config::read('db.user');
    // getting DB password from config                
    $password = lib\Config::read('db.password');

    $db = new PDO($dsn, $user, $password);
    $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    return $db;
});

// We use settings everywhere, so setting this up for quick access
$app->container->singleton('setting', function() use ($app) {
    $settings = new lib\Settings($app->db, $app->cache);
    return $settings;
});

// If user is logged in, instantiate logger
$app->container->singleton('logger', function() use ($app) {
    $userId = empty($_SESSION['user'])? 0: $_SESSION['user']['userId'];
    $logger = new models\Logger($app->db);
    $logger->setUserId($userId)
        ->setServerId($app->setting->get('serverId'));
    return $logger;
});

/**
 * HOOKS - add in any post-action hooks here
 * http://docs.slimframework.com/#Hooks-Overview
 */

// Check for undo transaction id, and pass to view if set
$undo = \lib\Session::get('undo');
if (!empty($undo)) {
    $app->view->appendData(array('undo' => $undo));
    \lib\Session::remove('undo');
}

// Check if vehicle mode is enabled, pass to view if set
$vehicleMode = \lib\Session::get('vehicleMode');
$app->view->appendData(array('vehicleMode' => $vehicleMode));

// Add a hook to check for user instantiation - if missing, redirect
// to login page
$app->hook('slim.before.router', function() use ($app) {
    $user = \lib\Session::get('user');
    $sysadmin = (isset($user['sysadmin']) && ((bool)$user['sysadmin']));
    $canteenMode = (bool)\lib\Config::read('canteenMode');

    if (
        ($canteenMode && !$sysadmin)
        && (strpos($app->request()->getPathInfo(), '/canteen') === false)
        && (strpos($app->request()->getPathInfo(), '/login') === false)
    ) {
        /**
         * Additional logic for the canteenMode configuration option. If it is
         * set to true, then restrict access to only login & canteen routes unless
         * the user is a sysadmin
         * 
         * Erm.... what about logout, or is that intentionally unavailable?
         */
        $app->redirect('/canteen');
    } else if (
        empty($user) 
        && strpos($app->request()->getPathInfo(), '/login') === false
        && strpos($app->request()->getPathInfo(), '/soap') === false
        && strpos($app->request()->getPathInfo(), '/entryapp/') === false
        && strpos($app->request()->getPathInfo(), '/api/') === false
        && strpos($app->request()->getPathInfo(), '/test') === false
        && $canteenMode === false
    ) {
        $app->redirect('/login');
    } elseif (!empty($user)) {
        $app->view->setData('user', $user);
        $isSectionManager = $user['type'] === \models\User::TYPE_SECTION_MANAGER_USER;
        $app->view->setData('isSectionManager', $isSectionManager);
        
        $pathRequest = $app->request()->getPathInfo();

        // acl for section managers - operates as whitelist
        if ($isSectionManager) {
            $sectionManagerPaths = ['/searchgroup', '/logout'];
            $found = false;
            foreach ($sectionManagerPaths as $path) {
                if (strpos($pathRequest, $path) !== FALSE) {
                    $found = true;
                    break;
                }
            }

            if (!$found) {
                $app->logger->log("Section manager {$user['username']} (uid {$user['userId']}) tried to access $pathRequest");
                $app->redirect('/searchgroup');
            }
        }

        // basic ACL layer for all other users
        $sysAdminPaths = array(
            '/setup',
            '/events',
            '/credentials',
            '/display',
            '/passouts',
            '/passoutupdate',
            '/gates',
            '/redemptionrelation',
            '/scheduledpassoutupdate'
        );
        foreach ($sysAdminPaths as $sysAdminPath) {
            if (strpos($pathRequest, $sysAdminPath) === 0 && empty($user['sysadmin'])) {
                $app->logger->log("Tried to access $pathRequest with insufficient privileges");
                $app->flash('error', 'You do not have permission to view this page');
                $app->redirect('/');
            }
        }
        
        $adminPaths = array(
            '/tickets',
            '/users',
        );
        foreach ($adminPaths as $adminPath) {
            if (strpos($pathRequest, $adminPath) === 0 && empty($user['admin']) && empty($user['sysadmin'])) {
                $app->logger->log("Tried to access $pathRequest with insufficient privileges");
                $app->flash('error', 'You do not have permission to view this page');
                $app->redirect('/');
            }
        }
        
        $reportsAvailable = lib\Config::read("reportsAvailable");
        $app->view->setData('reportsAvailable', $reportsAvailable);
        
        $inOutReportEnabled = lib\Config::read("inOutReport.enabled");
        $app->view->setData('inOutReportEnabled', $inOutReportEnabled);
        
        if (strpos($pathRequest, '/report') === 0 && empty($user['reports'])) {
            $allowed = false;
            if (!empty($reportsAvailable)) {
                foreach ($reportsAvailable as $report) {
                    if (strpos($pathRequest, $report) !== FALSE) {
                        $allowed = true;
                        break;
                    }
                }
            }
            
            if (!$allowed) {
                $app->logger->log("Tried to access $pathRequest with insufficient privileges");
                $app->flash('error', 'You do not have permission to view this page');
                $app->redirect('/');
            }
        }

        $canteenReportPaths = ['/canteen/transactions', '/canteen/history'];
        foreach ($canteenReportPaths as $canteenReportPath) {
            if (strpos($pathRequest, $canteenReportPath) === 0 && empty($user['reports'])) {
                $app->logger->log("Tried to access $pathRequest with insufficient privileges");
                $app->flash('error', 'You do not have permission to view this page');
                $app->redirect('/canteen');
            }
        }

        if (empty($user['admin']) && empty($user['reports'])) {
            if ($pathRequest == '/user-report/count' && !lib\Config::read("userReport.count")) {
                $app->logger->log("Tried to access $pathRequest with insufficient privileges");
                $app->flash('error', 'You do not have permission to view this page');
                $app->redirect('/');
            }
            $app->view->setData('userReportsAllowed', lib\Config::read("userReport.count"));
        }
    }
});

// Get gate details for appending to footer of each page
$eventIds = $app->setting->getEventIds();
$gates = new models\Gates($app->db);
$gateDetails = array();

$iosFastClick = false;
foreach ($eventIds as $eventId) {
    $gate = $gates->setGateId($app->setting->get('gateId', $eventId))
        ->setEventId($eventId)
        ->getGateById();
    
    if (!$iosFastClick && $app->setting->get('iosFastClick', $eventId)) {
        $iosFastClick = true;
    }

    $gateDetails[] = array(
        'gateName' => empty($gate['name']) ? '' : $gate['name'],
        'eventName' => $app->setting->get('eventName', $eventId),
    );
        
}
$serverId = $app->setting->get('serverId');
$app->view->setData('serverId', $serverId);
$app->view->setData('gateDetails', $gateDetails);
$app->view->setData('iosFastClick', $iosFastClick);

/**
 * ROUTING - setup any routing files and changes needed
 */
// Automatically load router files
// Get the request uri, and find the first request to detect which
// router file to load
$pathRequest = $app->request()->getPathInfo();

// path will always start with a /, so offset by 1
if (($offset = strpos($pathRequest, "/", 1)) !== false) {
    // preg_replace reduces request to alphabetical, so it's safe to use
    $router = preg_replace("/[^A-Za-z]/", "", substr($pathRequest, 1, $offset - 1));
} else {
    $router = preg_replace("/[^A-Za-z]/", "", substr($pathRequest, 1));
}

// Automatically load router files
//$routers = glob('../routers/*.router.php');
//foreach ($routers as $router) {
//    require $router;
//}

$app->view->setData('resourceuri', $app->request->getResourceUri());

$app->view->setData('displayCanteenButton', false);
foreach ($eventIds as $eventId) {
    if ($app->setting->get('entryMode', $eventId) == 'meal' || $app->setting->get('entryMode', $eventId) == 'all') {
        $app->view->setData('displayCanteenButton', true);
        break;
    }
}

//// Pick up router file if set correctly, otherwise get default
$app->view->setData('enableSingleScanScreen', (int) $app->setting->get('enableSingleScanScreen'));
if (!empty($router) && file_exists(__DIR__ . "/../routers/$router.router.php")) {
    $app->view->setData('currentpage', $router);
    require __DIR__ . "/../routers/$router.router.php";
} else {
    $app->view->setData('currentpage', 'index');
    require __DIR__ . "/../routers/index.router.php";
}

/**
 * RUN - go!
 */
$app->run();
