<?php

namespace IZON\Admin\MVC\Views\Resolvers;

use \Exception;

use \IZON\MVC\Views\Resolvers\ViewResolver;
use \IZON\Logs\Logger;

use \IZON\Admin\MVC\Views\AdminPHPLayoutView;

/**
 * resolves what view to use for rendering of admin
 * all admin views must start with admin/ prefix to be processed by this view resolver
 * final view uses layout 
 * 
 * @author IZON s.r.o. <info@izon.cz>
 * @package IZON\Admin\MVC\Views
 */
class AdminPHPLayoutViewResolver extends AdminPHPViewResolver implements ViewResolver {

    /**
     * relative to appDir 
     */
    const DEFAULT_ADMIN_LAYOUTS_DIR = "vendor/izon/admin/layouts";
    
    /**
     * @var string directory containing layouts for admin  
     */
    protected $adminLayoutsDir;
    
    /**
     * @var string name of default layout to use 
     */
    protected $defaultLayoutName = "basic-layout";
    
    /**
     * @var array contains info about layouts for different views
     */
    protected $viewsLayoutNames = [
        "admin/login" => "login-layout"
    ];
    
    function __construct($appDir, array $modulesDirs, array $config = []) {
        parent::__construct($appDir, $modulesDirs, $config);

        $this->adminLayoutsDir = $appDir ."/". self::DEFAULT_ADMIN_LAYOUTS_DIR;
        
        // redefine default layout if passed in config
        if( array_key_exists("defaultLayoutName", $config) ) { 
            $this->defaultLayoutName = $config["defaultLayoutName"];
        }
        
        // redefine other views if passed in config
        if( array_key_exists("viewsLayoutNames", $config) ) {
            $this->viewsLayoutNames = $config["viewsLayoutNames"];
        }
        
        $this->log = Logger::getLogger(self::class);
    }

    public function buildView($viewName) {
        if( !\IZON\String\startsWith($viewName, self::ADMIN_VIEWS_PREFIX) ) {
            $this->log->info(self::class ." not handling view ". $viewName ." it doesn't contain admin/ prefix");
            return NULL;
        }
        $this->log->info("bulding view with name ". $viewName);
        
        // find out if view even exists
        if( !$this->viewExists($viewName) ) {
            $this->log->info(self::class ." not handling view ". $viewName ." view doesn't exist");
            return NULL;
        }
        
        // layout file to use
        $layoutFilePath = $this->getViewLayoutFilePath($viewName);
        
        // where to search for layout parts
        $dirsToSearchLayoutParts = $this->getDirsToSearchLayoutParts($viewName);
        
        // folders to load libraries
        $librariesDirs = $this->getLibrariesDirs($viewName);
        
        // create view
        $view = new AdminPHPLayoutView($layoutFilePath, $dirsToSearchLayoutParts);
        $view->setLibrariesDirs($librariesDirs);
        $view->setStaticContentDirs($this->staticContentDirs);
        $view->setStaticContentPrefixes($this->staticContentPrefixes);
        $view->setStylesAutoloadURLs($this->getProjectStylesAutoloadURLs());
        $view->setScriptsAutoloadURLs($this->getProjectScriptsAutoloadURLs());
        
        return $view;
    }
    
    /**
     * reurns layout for specified view
     * @param string $viewName
     * @return string
     * @throws Exception
     */
    protected function getViewLayoutFilePath($viewName) {
        $layoutFileName = NULL;
        if( isset($this->viewsLayoutNames[$viewName]) ) {
            $layoutFileName = $this->viewsLayoutNames[$viewName];
        }
        if( $layoutFileName == NULL ) { // neni zmeneno nastavit defaultni
            $layoutFileName = $this->defaultLayoutName;
        }
        // which file to use for rendering
        $layoutFilePath = $this->adminLayoutsDir ."/". $layoutFileName . \IZON\Admin\MVC\Views\AdminPHPLayoutView::LAYOUT_SUFFIX;
        $this->log->info("Using layout $layoutFileName");
        
        // test jestli layout existuje
        if( !file_exists($layoutFilePath) ) {
            $message = "Layout file $layoutFilePath doesn't exist";
            $e = new Exception($message);
            $this->log->error($message, ['exception' => $e]);
            throw $e;
        }
        
        return $layoutFilePath;
    }
    
    /**
     * tests if set view exists
     * @param string $viewName
     * @return boolean
     */
    protected function viewExists($viewName) {
        // search for view in project specific dir
        $stripedName = mb_substr($viewName, mb_strlen("admin/"));
        
        $projectSpecificViewsPath = $this->projectSpecificViewsDir ."/". $stripedName;
        if( file_exists($projectSpecificViewsPath)
            && is_dir($projectSpecificViewsPath) ) {
            // is project specific view
            return true;
        }
        
        $adminViewsPath = $this->adminViewsDir ."/". $stripedName;
        if( file_exists($adminViewsPath)
            && is_dir($adminViewsPath) ) {
            // is admin view
            return true;
        }
        
        $moduleNameMatches = null;
        if( preg_match("#^admin/([a-zA-Z0-9\-]+/[a-zA-Z0-9\-]+)/.*$#", $viewName, $moduleNameMatches) ) {
            $moduleType = $moduleNameMatches[1]; // typ modulu ve kterem se ma hledat
            if( isset( $this->modulesViewDirs[$moduleType] ) ) {
                return true;
            }
        }
        
        return false;
    }
    
    protected function getDirsToSearchLayoutParts($viewName) {
        /// find directores where to search for layout parts
        // sub dirs for viewName
        $viewNameSubDirs = $this->splitSubDirs($viewName);
        // directories where to search for layout part files
        $dirsToSearchLayoutParts = [];
        for($prefixLength = count($viewNameSubDirs); $prefixLength > 0; $prefixLength--) {
            $viewNamePart = $this->createSubDir($viewNameSubDirs, $prefixLength);
            $stripedNamePart = mb_substr($viewNamePart, mb_strlen("admin/")); // name without admin start
            
            $projectSpecificViewsPath = $this->projectSpecificViewsDir ."/". $stripedNamePart;
            if( file_exists($projectSpecificViewsPath)
                && is_dir($projectSpecificViewsPath) ) {
                $dirsToSearchLayoutParts[] = $projectSpecificViewsPath;
            }

            $moduleNameMatches = null;
            if( preg_match("#^admin/([a-zA-Z0-9\-]+/[a-zA-Z0-9\-]+)/.*$#", $viewNamePart, $moduleNameMatches) ) {
                $moduleType = $moduleNameMatches[1]; // typ modulu ve kterem se ma hledat
                if( isset($this->modulesViewDirs[$moduleType]) ) {
                    $moduleViewDir = $this->modulesViewDirs[$moduleType];
                    
                    // nove kratsi cesty
                    $viewDir = $moduleViewDir ."/". mb_substr($stripedNamePart, mb_strlen($moduleType));
                    if(file_exists($viewDir)
                        && is_dir($viewDir) ) {
                        $dirsToSearchLayoutParts[] = $viewDir;
                    }
                }
            }
            
            $adminViewsPath = $this->adminViewsDir ."/". $stripedNamePart;
            if( file_exists($adminViewsPath)
                && is_dir($adminViewsPath) ) {
                 $dirsToSearchLayoutParts[] = $adminViewsPath;
            }
        }
        return $dirsToSearchLayoutParts;
    }
    
    protected function splitSubDirs($path) {
        return explode("/", $path);
    }

    /**
     * vytvoti cestu k podadresari podle polozek v poli a poctu polozek, ktere se maji\
     * pozit
     */
    protected function createSubDir($subDirsArray, $count) {
        return implode('/', array_slice($subDirsArray, 0, $count));
    }
}