<?php

include_once('designer.php');
include_once(dirname(__FILE__).'/Export/PositionController.php');
include_once(dirname(__FILE__).'/Export/HookController.php');
include_once(dirname(__FILE__).'/Export/Chunk.php');
include_once(dirname(__FILE__).'/Export/PreviewHelper.php');

class DesignerTabSelf
{
    private $_themeName;
    private $_previewThemeName;
    private $_themeDir;
    private $_previewThemeDir;
        
    public function __construct($themeName)
    {
        $this->_themeName = $themeName;
        $this->_previewThemeName = $themeName . _PREVIEW_SUFFIX_;
        $this->_themeDir = FilesHelper::normalize_path(getThemeDir($this->_themeName));
        $this->_previewThemeDir = FilesHelper::normalize_path(getThemeDir($this->_previewThemeName));
    }
    
    /**
     * Saves designer project to current theme
     */
    public function save($data, $domain, $isNew)
    {
        $info = json_decode($data, true);
        $chunk = new Chunk();
        $chunk->save($info);

        if ($chunk->last())
        {
            // merge and decode content
            $result = json_decode($chunk->complete(), true);
            $modules_dir = FilesHelper::normalize_path(_PS_MODULE_DIR_);
            
            $thumbnails = array_key_exists('thumbnails', $result) > 0 ? $result['thumbnails'] : '';
            $cssJsSources = array_key_exists('cssJsSources', $result) > 0 ? $result['cssJsSources'] : '';
            $md5Hashes = array_key_exists('md5Hashes', $result) > 0 ? $result['md5Hashes'] : '';
            $projectData = array_key_exists('projectData', $result) > 0 ? $result['projectData'] : '';

            $helper = new PreviewHelper($this->_themeDir);
            $changed_files = getPreviewChangedFiles($this->_themeDir);
            foreach($changed_files as $file) {
                if (file_exists($this->_previewThemeDir . $file)) {
                    if (strpos($file, 'designer/')) { // update plugin folder but do not save it to base theme
                        FilesHelper::copy_recursive($this->_previewThemeDir . $file, $modules_dir . $file);
                    } else {
                        FilesHelper::copy_recursive($this->_previewThemeDir . $file, $this->_themeDir . $file, array($helper, "removeDataId"));
                    }
                } else {
                    $helper->removeKey($file);
                    if (is_file($this->_themeDir . $file)) {
                        unlink($this->_themeDir . $file);
                    }
                }
            }
            setPreviewChangedFiles($this->_themeDir, $this->_previewThemeDir, array());
            $helper->save();

            $this->_saveThumbnails($thumbnails);
            if (!empty($projectData))
                saveProject($this->_themeName, $projectData, $domain);

            if ($isNew) // save and publish new theme
                Designer::setActiveThemeForShop($this->_themeName);

            if (!empty($cssJsSources))
                setThemeCache($this->_themeDir, $cssJsSources);
            
            if (!empty($md5Hashes))
                setThemeHashes($this->_themeDir, $md5Hashes);
            
            return array('result' => 'done');
        }
        return array('result' => 'processed');
    }
    
    /**
     * Exports designer project
     */
    public function export($data)
    {
        $info = json_decode($data, true);
        $chunk = new Chunk();
        $chunk->save($info);

        if ($chunk->last())
        {
            // merge and decode content
            $content = json_decode($chunk->complete(), true);
            $this->_exportTheme($content, false);
            return array('result' => 'done');
        }
        return array('result' => 'processed');
    }
    
    /**
     * Updates Designer theme
     */
    public function update()
    {
        $helper = new PreviewHelper($this->_themeDir);
        $changedFiles = getPreviewChangedFiles($this->_themeDir);
        if ($changedFiles === null || !file_exists($this->_previewThemeDir)) {
            // full copy
            FilesHelper::copy_recursive($this->_themeDir, $this->_previewThemeDir, array($helper, "restoreDataId"));
            FilesHelper::empty_dir($this->_previewThemeDir . '/export', true);
        } else {
            // copy only changed files
            foreach($changedFiles as $file){
                if (file_exists($this->_themeDir . $file)) {
                    FilesHelper::copy_recursive($this->_themeDir . $file, $this->_previewThemeDir . $file, array($helper, "restoreDataId"));
                } else {
                    if (is_file($this->_previewThemeDir . $file)) {
                        unlink($this->_previewThemeDir . $file);
                    }

                }
            }
            setPreviewChangedFiles($this->_themeDir, $this->_previewThemeDir, array());
        }
        return array('result' => 'done');
    }
    
    /**
     * Set designer theme as active in PrestaShop
     */
    public function publish()
    {
        // if it is new theme Designer copies current preview state
        if (!file_exists($this->_themeDir))
        {
            mkdir($this->_themeDir, 0777, true);
            FilesHelper::copy_recursive($this->_previewThemeDir, $this->_themeDir);
        }
        
        return Designer::setActiveThemeForShop($this->_themeName);
    }
    
    /**
     * Renames Designer theme
     */
    public function rename($newThemeName)
    {
        $newThemeName = preg_replace('|[^a-z0-9_./-]|i', '', $newThemeName);
        $newThemePreview = $newThemeName . _PREVIEW_SUFFIX_;

        $newThemeDir = FilesHelper::normalize_path(getThemeDir($newThemeName));
        $newThemePreviewDir = FilesHelper::normalize_path(getThemeDir($newThemePreview));
        
        if (file_exists($this->_themeDir) && !file_exists($newThemeDir))
        {
            if (file_exists($this->_previewThemeDir))
                rename($this->_previewThemeDir, $newThemePreviewDir);
            
            rename($this->_themeDir, $newThemeDir);
            Designer::deleteTheme($this->_themeName);
            Designer::deleteTheme($this->_previewThemeName);
            Designer::setActiveThemeForShop($newThemeName);

            return array('result' => 'done');
        }

        if (file_exists($newThemeDir))
            die('You have already such theme');

        die;
    }
    
    /**
     * Returns theme to Designer
     */
    public function getTheme($userThemeName)
    {
        $archive_file = $this->getThemeArchive($userThemeName);

        if (is_readable($archive_file)) {
            $archive_content = array(
                'ext' => 'zip',
                'content' => base64_encode(file_get_contents($archive_file))
            );
            return $archive_content;
        }

        die('Archive file: ' . $archive_file. ' is not readable');
    }
    
    public function getThemeArchive($userThemeName = '')
    {
        $module_dir = _PS_MODULE_DIR_ . 'designer';
        $base_template_dir = $this->_themeDir;
        if (!file_exists($base_template_dir)){
            die('Error : No Theme Folder');
        }

        $useBaseTheme = empty($userThemeName) || ($userThemeName == $this->_themeName);
        if ($useBaseTheme)
        {
            $userThemeName = $this->_themeName;
            $user_template_dir = $base_template_dir;
        }
        else
        {
            $user_template_dir = FilesHelper::normalize_path(getThemeDir($userThemeName));
            FilesHelper::copy_recursive($base_template_dir, $user_template_dir); // user theme
        }
        
        $config_dir = $module_dir . '/Export';
        
        $base_upload_dir = _PS_DOWNLOAD_DIR_;
        $base_upload_uri = Designer::getDownloadUrl();

        $user_template_dir = FilesHelper::modifyWinDirectory($user_template_dir);
        $base_upload_dir = FilesHelper::modifyWinDirectory($base_upload_dir);
        $module_dir = FilesHelper::modifyWinDirectory($module_dir);
        $config_dir = FilesHelper::modifyWinDirectory($config_dir);
        
        $defines_name = 'defines.inc.php';
        $defines_path = $module_dir . '/' .$defines_name;
        $new_defines_path = $base_upload_dir . '/modules/designer/' . $defines_name;
        $config_path = $config_dir . '/Config.xml';
        $new_config_path = $base_upload_dir . '/Config.xml';

        $this->_replaceDefaultThemeName($config_path, $new_config_path, $userThemeName);
        $this->_replaceDefaultThemeName($defines_path, $new_defines_path, $userThemeName);
        
        $v_remove = FilesHelper::modifyWinDirectory(_PS_ROOT_DIR_);
        $u_remove = FilesHelper::modifyWinDirectory($base_upload_dir);
        
        $archive_name = 'theme_' . uniqid(time()) .  '.zip';
        
        require_once(dirname(__FILE__) . '/../../tools/pclzip/pclzip.lib.php');			
        $archive_file = $base_upload_dir . '/' . $archive_name;
        $archive = new PclZip($archive_file);
        $this->_addToArchive($archive, $user_template_dir, $v_remove);
        $this->_addToArchive($archive, $module_dir, $v_remove);
        $this->_addToArchive($archive, $new_config_path, $u_remove);
        $this->_deleteFromArchive($archive, $defines_name);
        $this->_addToArchive($archive, $new_defines_path, $u_remove);
        
        if (!$useBaseTheme)
            FilesHelper::remove($user_template_dir, true);

        return $archive_file;
    }
    
    private function _replaceDefaultThemeName($path, $pathToSave, $userThemeName)
    {
        $content = file_get_contents($path);
        $result = str_replace(DEFAULT_THEME_NAME, $userThemeName, $content);
        FilesHelper::write($pathToSave, $result);
    }
    
    private function _deleteFromArchive(&$archive, $fileName)
    {
        if (($list = $archive->listContent()) == 0) {
            die("Error : ".$zip->errorInfo(true));
        }

        for ($i = 0; $i < sizeof($list); $i++) {
            if (strpos($list[$i]['filename'], $fileName) !== FALSE) {
                $v_list = $archive->delete(PCLZIP_OPT_BY_INDEX, "$i");
                if ($v_list == 0) {
                    die("Error : ".$archive->errorInfo(true));
                }
            }
        }
    }
    
    private function _addToArchive(&$archive, $dir, $remove_path)
    {
        $v_list = $archive->add($dir, PCLZIP_OPT_ADD_PATH, '', PCLZIP_OPT_REMOVE_PATH, $remove_path);
        if ($v_list == 0) {
            die('Error to add ' . $dir . ': ' . $archive->errorInfo(true));
        }
    }
    
    /**
     * Exports designer project
     */
    private function _exportTheme($content, $copyWithExportPlugin = true)
    {
        $changed_files = getPreviewChangedFiles($this->_themeDir);
        if (!is_array($changed_files))
            $changed_files = array();

        $positions      = isset($content['positions'])    ? $content['positions']    : null;
        $theme          = isset($content['themeFso'])     ? $content['themeFso']     : null;
        $images         = isset($content['images'])       ? $content['images']       : null;
        $fonts          = isset($content['iconSetFiles']) ? $content['iconSetFiles'] : null;
        
        $replaceInfo = array();
        $replaceInfo = array_merge_recursive($replaceInfo, (array)$this->_processFonts($this->_previewThemeDir, $fonts, $changed_files));
        $replaceInfo = array_merge_recursive($replaceInfo, (array)$this->_processImages($this->_previewThemeDir, $images, $changed_files));
        $hasReplace = isset($replaceInfo) && isset($replaceInfo['ids']) && isset($replaceInfo['paths']);
        $this->_fillThemeStorage($theme, $this->_previewThemeDir, $changed_files, $replaceInfo, $hasReplace);

        // we have to rebuild template files if layout position changed or block moved for example
        $this->_buildThemeLayoutFilesFromHtml($changed_files, $replaceInfo, $hasReplace);
        
        if (isset($positions))
            PositionController::savePositions($positions);
            
        if ($copyWithExportPlugin)
            FilesHelper::copy_recursive($this->_themeDir . '/export', $this->_previewThemeDir . '/export');
            
        setPreviewChangedFiles($this->_themeDir, $this->_previewThemeDir, $changed_files);
    }
    
    private function _processImages($themeDir, $images, &$changed_files)
    {
        if (!isset($images) || !is_array($images)) return null;
        
        $imageDir = $themeDir . '/img';
        if (!is_dir($imageDir)) {
            if (!mkdir($imageDir, 0777, true))
                die('Failed to create folder!');
        }

        $ids = array();
        $paths = array();
        foreach ($images as $id => $content)
        {
            /* STORAGE */
            $imageFileName = preg_replace('/[^a-z0-9_\.]/i', '', $id);
            $imagePath = $imageDir . '/' . $imageFileName;

            if ($content) {
                $changed_files[] = $imagePath;
                if ($content === '[DELETED]') {
                    if (is_file($imagePath)) {
                        unlink($imagePath);
                    }
                    continue;
                }
                file_put_contents($imagePath,  base64_decode($content));
            }

            $ids[] = $id;
            $paths[] = IMG_PATH_TOKEN . '/' . $imageFileName; // css path
        }
        return array('ids' => $ids, 'paths' => $paths);
    }
    
    /**
     * Saves theme fonts
     */
    private function _processFonts($themeDir, $fonts, &$changed_files)
    {
        if (!isset($fonts)) return null;
        
        $fontsDir = $themeDir . '/fonts';
        if (!is_dir($fontsDir))
            if (!mkdir($fontsDir, 0777, true))
                die('Failed to create folder!');

        foreach ($fonts as $name => $content) {
            $path = $fontsDir . '/' . $name;
            file_put_contents($path, base64_decode($content));
            $changed_files[] = $path;
        }

        $existingFonts = scandir($fontsDir);
        $ids = array();
        $paths = array();
        foreach($existingFonts as $filename)
            if ($filename !== '.' && $filename !== '..') {
                $ids[] = $filename;
                $paths[] = '../fonts/' . $filename;
            }

        return array('ids' => $ids, 'paths' => $paths);
    }
    
    private function _saveThumbnails($thumbnails) 
    {
        if(is_array($thumbnails)) {
            foreach($thumbnails as $thumbnail) {
                $img = base64_decode(str_replace('data:image/jpeg;base64,', '', $thumbnail['data']));
                file_put_contents($this->_themeDir . '/' . $thumbnail['name'], $img);
            }
        }
    }
    
    private function _fillThemeStorage($fso, $path, &$changed_files, &$replaceInfo, $hasReplace)
    {
        if (!isset($fso) || !is_array($fso) || !is_array($fso['items'])) {
            return;
        }
        if (!file_exists($path)) {
            if (!mkdir($path, 0777, true))
                die('Failed to create folder!');
        }
        foreach ($fso['items'] as $name => $file) {
            if (isset($file['content']) && isset($file['type'])) {
                $to = $path . '/' . $name;
                $content = $file['type'] == 'text' ? $file['content'] : base64_decode($file['content']);
                $this->_processFile($to, $content, $changed_files, $replaceInfo, $hasReplace);
            } elseif (isset($file['items']) && isset($file['type'])) {
                $this->_fillThemeStorage($file, $path . '/' . $name, $changed_files, $replaceInfo, $hasReplace);
            }
        }
    }

    private function _processFile($path, $content, &$changed_files, &$replaceInfo, $hasReplace)
    {
        $themeDir = $this->_previewThemeDir;
        $info = pathinfo($path);
        $fileName = $info['basename'];
        $fileExt = isset($info['extension']) && $info['extension'] ? $info['extension'] : '';
        if ($content === '[DELETED]')
        {
            if (is_file($path))
                unlink($path);
        }
        elseif (in_array($fileExt, array('js')) && !strpos($path, 'designer/')) // move js file from theme to inner /js/ folder apart of CloudZoom.js
        {
            $path = strpos($path, '/js/') || strpos($path, 'CloudZoom.js') ? $path : $themeDir . '/js/'. $fileName;
            file_put_contents($path, $content);
        } 
        elseif (in_array($fileExt, array('css')) && !strpos($path, 'designer/')) // move css file from theme to inner /css/ folder
        {
            if ($fileName === 'style.css') $fileName = 'global.css';
            $path = strpos($path, '/css/') ? $path : $themeDir . '/css/'. $fileName;
            if ($hasReplace) $content = str_replace($replaceInfo['ids'], $replaceInfo['paths'], $content);
            file_put_contents($path, $content);
        }
        elseif (in_array($fileExt, array('tpl')))
        {
            $result = $this->_replaceImageIdToUrl($content, $replaceInfo, $hasReplace);
            file_put_contents($path, $result);
        }
        else // other files 
        {
            file_put_contents($path, $content);
        }
        $changed_files[] = $path; // save changed file - source or modified
    }
    
    /**
     * Build theme layout files header.tpl, footer.tpl
     * @@param string $templatePath
     */
    private function _buildThemeLayoutFilesFromHtml(&$changed_files, $replaceInfo, $hasReplace)
    {
        $dir = $this->_previewThemeDir;
        foreach (FilesHelper::enumerate_files($dir, false) as $file)
        {
            $path = $file['path'];
            $info = pathinfo($path);
            $name = $info['filename'];
            $fileExt = isset($info['extension']) && $info['extension'] ? $info['extension'] : '';
            if (!in_array($fileExt, array('html'))) {
                continue;
            }
            $content = file_get_contents($path);
            $content = $this->_buildTemplateContent($content, $dir);
            $content = $this->_replaceImageIdToUrl($content, $replaceInfo, $hasReplace);

            $content_before = $content;
            $content_after = '';
            $id = 'center_column';
            $searchable_content = '<div id="'.$id.'">{$HOOK_HOME}</div>';
            $position = strpos($content, $searchable_content);
            if ($position !== FALSE)
            {
                $content_before = substr($content, 0, $position) . '<div id="'.$id.'">';
                $content_after = '</div>'.substr($content, $position + strlen($searchable_content));
            }
            
            $suffix = $name === 'default' ? '' : '_'.$name;
            $header_tpl_path = $dir . '/header' . $suffix . '.tpl';
            $footer_tpl_path = $dir . '/footer' . $suffix . '.tpl';
            FilesHelper::write($header_tpl_path, $content_before);
            FilesHelper::write($footer_tpl_path, $content_after);
            $changed_files[] = $header_tpl_path;
            $changed_files[] = $footer_tpl_path;
        }
    }
    
    private function _replaceImageIdToUrl($content, $replaceInfo, $hasReplace)
    {
        $result = $content;
        if ($hasReplace)
        {
            $urls = str_replace(IMG_PATH_TOKEN, '{$img_dir}', $replaceInfo['paths']);
            foreach ($replaceInfo['ids'] as $key => $value) // replace <img src="url(id)"> to <img src="$url">
                $result = str_replace('url('.$value.')', $urls[$key], $result);
        }
        return $result;
    }
    
    private function _buildTemplateContent($content, $themeDir)
    {
        $controls = array('sheet', 'layoutcontainer', 'hookhome');
        foreach ($controls as $control)
            $content = HookController::getHookFromControl($themeDir, $content, $control);

        return $content;
    }
    
    public function chunkClear()
    {
        $chunk = new Chunk();
        $chunk->clear_chunk_directory();
    }
}