<?php
require_once('PlaceHoldersStorage.php');
require_once('Preview.php');
require_once('StylesBuilder.php');

class ThemeBuilder
{
    public $themeDir;
    public $editorDir;

    private $_result = array();

    private $_themeName;
    private $_placeholders;
    private $_customImagesInfo = array();
    private $_preview;

    public function __construct($templateName, $media)
    {
        $this->themeDir     = realpath(dirname(dirname(__FILE__)));
        $this->editorDir    = $this->themeDir . '/editor';
        
        $this->preview = new Preview($templateName);

        // save icon fonts to result
        $this->addIconFonts($media['icons']);
        // save thumbnails to result
        $this->addThumbnails($media['thumbnails']);
        // save custom images to result
        $this->addCustomImages($media['images']);

        $this->_publish = false;

        $this->_templateName = $templateName;

        // create placeholders collection
        $this->_placeholders = new PlaceHoldersStorage();
        //load fragments
        $fragments = Helper::enumerate($this->themeDir . '/app/fragments');
        $this->_placeholders->set($this->loadFragments($fragments));
        //preview theme
        $this->_placeholders->set('theme', $this->_templateName);
        $content    = $this->_placeholders->get('preview_logic');
        $content    = str_replace('[[joomla_user_id]]', Project::getProperty('joomla_user_id'), $content);
        $this->_placeholders->set('preview_logic', $content);
    }

    public function export($storage)
    {
        $this->buildTemplateFiles($storage);
        $this->updatePreviewTheme();
    }

    public function addThumbnails($thumbnails)
    {
        if (!$thumbnails) return;

        // template_preview.png
        $preview_base64 = str_replace('data:image/png;base64,', '', $thumbnails[0]['data']);
        $this->_result['/' . $thumbnails[0]['name']] = base64_decode($preview_base64);
        // template_thumbnail.png
        $thumbnail_base64 = str_replace('data:image/png;base64,', '', $thumbnails[1]['data']);
        $this->_result['/' . $thumbnails[1]['name']] = base64_decode($thumbnail_base64);
    }

    private function addIconFonts($icons)
    {
        if (!$icons) return;

        foreach ($icons as $name => $content) {
            $this->_result['/css/' . $name] = base64_decode($content);
        }
    }

    private function addCustomImages($images)
    {
        if (!$images) return;

        foreach ($images as $name => $data) {
            $validName = preg_replace('/[^a-z0-9_\.]/i', '', $name);

            $path = '/images/designer/' . $validName;
            $this->_customImagesInfo[$name] = $path;
            if ($data)
                $this->_result[$path] = base64_decode($data);
        }
    }

    private function buildTemplateFiles($themeStorage)
    {
        // copy page control files to result
        $excludedFiles = array('/options.json');
        foreach($themeStorage as $name => $content) {
            $info = pathinfo($name);
            $dirnameParts = explode('/', $info['dirname']);
            $type = '';
            if (count($dirnameParts) > 2 && 'html' == $dirnameParts[1]) {
                $componentParts = explode('_', $dirnameParts[2]);
                $type = array_shift($componentParts);
            }
            $parts      = explode('.', $info['basename']);
            $fname      = $parts[0];
            $extension  = array_pop($parts);
            // change extension for templates files
            if (('\\' === $info['dirname'] || '/' === $info['dirname']) && '/index.html' !== $name && 'html' === $extension) {
                $extension = 'php';
                $name = '/templates/' . $fname . '.' . $extension;
            }
            // exclude some files from result
            if (in_array($name, $excludedFiles, true) || preg_match('/^\/fragments\//', $name)) {
                continue;
            }
            // rename translation file to theme name
            if (preg_match('/^\/language\/en-GB\/en-GB.tpl_template.ini/', $name)) {
                $this->_result['/language/en-GB/en-GB.tpl_' .$this->_templateName . '.ini'] = $content;
                continue;
            }
            switch ($name) {
                case '/style.css':
                    $name = '/css/template.css';
                    foreach($this->_customImagesInfo as $guid => $path) {
                        $content = str_replace($guid, '..' . $path, $content);
                    }
                    break;
                case '/bootstrap.css':
                    $name = '/css/bootstrap.css';
                    break;
                default:
                    break;
            }

            if ('php' === $extension) {
                // change guids for images
                foreach($this->_customImagesInfo as $guid => $path) {
                    $content = str_replace('url(' . $guid . ')', '{templateUrl}' . $path, $content);
                }
                // add condition for the editor
                if ('[DELETED]' !== $content && ('com' === $type || 'mod' === $type || 'pagination' === $fname
                    || 'error' === $fname || 'modules' === $fname || '/index.php' === $name)) {
                    if ($value = $this->_placeholders->get('include_editor_prepend') )
                        $content = $value . $content;
                    if ($value = $this->_placeholders->get('include_editor_append'))
                        $content = $content . $value;
                }
            }
            if (!preg_match('/^\/app/', $name))
                $content = $this->_placeholders->replace($content);
            if (isset($this->_result[$name])) {
                $this->_result[$name] .= $content;
            } else {
                $this->_result[$name] = $content;
            }
        }
    }

    private function updatePreviewTheme()
    {
        // build print.css and editor.css
        $content = '';
        if (isset($this->_result['/css/template.css'])) {
            $content .= $this->_result['/css/template.css'];
        }
        if (isset($this->_result['/css/bootstrap.css'])) {
            $content .= $this->_result['/css/bootstrap.css'];
        }
        if ('' !== $content) {
            //$files = StylesBuilder::buildCssFiles($content);
            //$this->_result['/css/print.css'] = $files['print'];
            //$this->_result['/css/editor.css'] = $files['editor'];
        }
        
        foreach($this->_result as $path => $content) {
            if ('/css/template.css' === $path)  { // to do DR-6333
                if (file_exists($this->editorDir . '/css/print.css')) {
                    file_put_contents($this->editorDir . '/css/print.css', $content, FILE_APPEND);
                }
                if (file_exists($this->editorDir . '/css/editor.css')) {
                    file_put_contents($this->editorDir . '/css/editor.css', $content, FILE_APPEND);
                }
            }
            $pos = strrpos($path, "/"); // find position last slash
            //check exists new added control file
            $mainPath = $this->themeDir . $path;
            if (preg_match('/^\/html/', $path) && !file_exists($mainPath)) {
                $currentDir = $this->themeDir . substr($path, 0, $pos + 1);
                Helper::createDir($currentDir);
                file_put_contents($mainPath, $content);
            }
            $fullPath = $this->editorDir . $path;
            if ('[DELETED]' === $content) {
                $this->removeDeletedFile($fullPath);
                $this->preview->removeKey($path);
                continue;
            } else {
                if (false !== $pos) {
                    $currentDir = $this->editorDir . substr($path, 0, $pos + 1);
                    Helper::createDir($currentDir);
                }
                file_put_contents($fullPath, $content);
            }
        }
        // build manifest
        $this->buildManifest($this->editorDir);
        
        // removal of junk image files
        $dir = $this->editorDir . '/images/designer';
        if (file_exists($dir)) {
            $it = new RecursiveDirectoryIterator($dir);
            $files = new RecursiveIteratorIterator($it,
                         RecursiveIteratorIterator::CHILD_FIRST);
            foreach($files as $file) {
                if ($file->getFilename() === '.' || $file->getFilename() === '..')
                    continue;
                if (!in_array('/images/designer/' .  $file->getFilename(), $this->_customImagesInfo))
                    unlink($file->getRealPath());
            }
        }
    }

    public function updateOriginalTheme()
    {
        $includesDir = $this->themeDir . '/includes';
        if (file_exists($includesDir))
            Helper::removeDir($includesDir, false);
        
        $imagesDir = $this->themeDir . '/images';
        $designerImagesDir = $imagesDir .  '/designer';
        if (file_exists($designerImagesDir))
            Helper::removeDir($designerImagesDir, false);

        $files = Helper::enumerateRecursive($this->editorDir);
        foreach($files as $path => $content) {
            if (preg_match('/functions\.php$/', $path))
                $content = str_replace('\'is_preview\' => true', '\'is_preview\' => false', $content);
            if (preg_match('/script\.js$/', $path))
                $content = str_replace('var PREVIEW = true', 'var PREVIEW = false', $content);
            $pos = strrpos($path, "/");
            $fullPath = $this->themeDir . '/' . $path;
            if (false !== $pos) {
                $currentDir = $this->themeDir . '/' . substr($path, 0, $pos + 1);
                Helper::createDir($currentDir);
            }
            if (!preg_match('/^app\//', $path))
                $content = $this->preview->removeDataId($path, $content);
            file_put_contents($fullPath, $content);
        }
        if (Helper::isEmptyDir($designerImagesDir)) {
            Helper::removeDir($designerImagesDir);
            if (Helper::isEmptyDir($imagesDir))
                Helper::removeDir($imagesDir);
        }
        $this->preview->save();
    }


    public function getTemplatePositions($content)
    {
        $result = array();
        preg_match_all('/\$view->position\(\'([^\']+)\'/', $content, $matches);
        foreach($matches[1] as $value){
            if (!in_array($value, $result))
                array_push($result, $value);
        }
        sort($result);
        return $result;
    }

    public function buildManifest($themeDir)
    {
        $manifest = file_get_contents($themeDir . '/templateDetails.xml');
        //Build manifest, which contains set of files
        $placeholders = new PlaceHoldersStorage(array( 'theme' => $this->_templateName));
        $manifest = $placeholders->replace($manifest);

        $xml = simplexml_load_string($manifest);
        if ($xml) {
            // Set theme metadata info
            $xml->name = $this->_templateName;
            $xml->creationDate = date('Y-m-d');
            $xml->version = '1.0';
            $xml->author = '';
            $xml->authorUrl = '';
            $xml->description = '';
            // Build list of folders
            $folders = JFolder::folders($themeDir, '.', false, false, array('editor', 'tmp'));
            sort($folders);
            unset($xml->files->folder);
            foreach ($folders as $value) {
                $xml->files->addChild('folder', $value);
            }
            // Build list of files
            $files = JFolder::files($themeDir);
            sort($files);
            unset($xml->files->file);
            foreach ($files as $value) {
                $xml->files->addChild('file', $value);
            }
            // Build list of positions
            unset($xml->positions->position);
            $files = JFolder::files($themeDir, '.php', true, true, array('editor', 'tmp', 'app'));
            $pageContent = '';
            foreach ($files as $value) {
                $pageContent .= JFile::read($value);
            }
            $elements  = $this->getTemplatePositions($pageContent);
            foreach ($elements as $element) {
                $xml->positions->addChild('position', $element);
            }
        } else {
            $error_msg = "File Open Error: file " . $xmlfile;
            $this->error('error: ' . $error_msg);
        }

        // Save dom to xml file
        $dom = new DOMDocument('1.0', 'utf-8');
        $dom->preserveWhiteSpace = false;
        $dom->formatOutput = true;
        $dom->loadXML($xml->asXML());
        file_put_contents($themeDir . '/templateDetails.xml', $dom->saveXML());
    }

    public function loadFragments($files)
    {
        $pieces = array();
        foreach($files as $path => $value) {
            $pathParts = explode('/', $path);
            $name  = array_pop($pathParts);
            $nameParts  = explode('.', $name);
            $key   = array_shift($nameParts);
            preg_match_all('/([\w\W]*)<!--\s*CONTAINER\s*-->([\w\W]*)<!--\s*\/CONTAINER\s*-->([\w\W]*)/', $value, $primary);
            $secondary;
            if(empty($primary[1])) {
                $pieces[$key] = $value;
            } else {
                $pieces[$key . '_before'] = $primary[1][0];
                $pieces[$key . '_after'] = $primary[3][0];
                preg_match_all('/^([\w\W]*)<!--\s*CONTENT\s*-->([\w\W]*)$/', $primary[2][0], $secondary);
                if (empty($secondary[1])) {
                    $pieces[$key . '_prepend'] = $primary[2][0];
                    $pieces[$key . '_append'] = '';
                } else {
                    $pieces[$key . '_prepend'] = $secondary[1][0];
                    $pieces[$key . '_append'] = $secondary[2][0];
                }
            }
        }
        return $pieces;
    }

    private function removeDeletedFile($filePath)
    {
        $info  = pathinfo($filePath);
        $dirName = $info['dirname'];
        if (file_exists($filePath))
            unlink($filePath);
        if (Helper::isEmptyDir($dirName))
            Helper::removeDir($dirName);
    }

    private function error($msg)
    {
        $response = json_encode(array('response' => $msg));
        exit($response);
    }
}