<?php
//@*--


class Theme_Designer_Adminhtml_ThemeController extends Theme_Designer_Controller_AdminAction
{
    protected static $APP_DIR;
    protected static $SKIN_DIR;

    protected static $PREVIEW_DIRNAME;
    protected static $THEME_DIRNAME;

    /**
     * @var Theme_Designer_Model_Config
     */
    protected $cfg;
    protected $fso;
    protected $preview;

    /**
     * Overrides base constructor
     */
    protected function _construct() {
        parent::_construct();
        $this->cfg = Mage::getSingleton('designer/config');
        $this->fso = Mage::helper('designer/filesystem');
        $this->preview = Mage::getSingleton('designer/export_preview', array('theme' => $this->currentTheme));

        self::$APP_DIR = MAGENTO_ROOT . $this->cfg->get('paths/app_base');
        self::$SKIN_DIR = MAGENTO_ROOT . $this->cfg->get('paths/skin_base');

        self::$PREVIEW_DIRNAME = $this->cfg->get('paths/preview/dirname', array('theme' => $this->currentTheme));
        self::$THEME_DIRNAME = $this->cfg->get('paths/theme/dirname', array('theme' => $this->currentTheme));
    }

    /**
     * Loads Designer Admin Page
     */
    public function indexAction()
    {
        //$project = Theme_Designer_Model_Export_Project::open($this->currentTheme);
        //$project->saveSession();

        $this->loadLayout();
        $this->_title($this->__('Designer'));
        $this->renderLayout();
    }

    /**
     *  Starts Export
     */
    public function exportAction()
    {
        $info = json_decode($this->getRequest()->getParam('info', '{}'), true);
        $chunk = Mage::getModel('designer/export_chunk');
        $chunk->save($info);

        if ($chunk->last()) {
            $content = json_decode($chunk->complete(), true);

            $this->_buildExport($content);

            if (array_key_exists('projectData', $content)) {
                $project = Mage::getSingleton('designer/export_project', array($this->currentTheme, $content));
                $project->save();

                /** @var Theme_Designer_Model_Export_Storage_Abstract $diff */
                $diff = Mage::getSingleton('designer/export_storage_diff', array('theme' => $this->currentTheme));
                $this->_updateTheme($diff->load()->toArray(), true);
                $diff->remove();
            }

            if (array_key_exists('positions', $content)) {
                $this->_updatePositions($content['positions']);
            }

            if (array_key_exists('cssJsSources', $content)) {
                $this->_updateCache($content['cssJsSources']);
            }

            if (array_key_exists('md5Hashes', $content)) {
                $this->_updateHashes($content['md5Hashes']);
            }

            $this->setAjaxResponse(array('result' => 'done'));
        } else {
            $this->setAjaxResponse(array('result' => 'processed'));
        }
    }

    /**
     * Makes theme active
     */
    public function publishAction() {
        Mage::getConfig()->saveConfig('design/theme/default', $this->currentTheme);
        Mage::getConfig()->reinit();
        Mage::app()->reinitStores();

        $this->adminRedirect('designer/adminhtml_list/index');
    }

    /**
     * Clears chunks
     */
    public function clearAction() {
        $uploadPath = $this->cfg->get('paths/export/upload');
        if (($id = $this->getRequest()->getParam('id')) && $id && is_dir($uploadPath . DS . $id)) {
            Mage::helper('designer/filesystem')->remove($uploadPath . DS . $id, true);
            $this->setAjaxResponse(array('result' => 'ok'));
        } else {
            $this->setAjaxResponse(array('result' => 'fail'));
        }
    }

    /**
     * Update preview theme
     */
    public function updatePreviewAction()
    {
        $previewThemeAppDir = self::$APP_DIR . self::$PREVIEW_DIRNAME;
        $previewThemeSkinDir = self::$SKIN_DIR . self::$PREVIEW_DIRNAME;

        $themeAppDir = self::$APP_DIR . self::$THEME_DIRNAME;
        $themeSkinDir = self::$SKIN_DIR . self::$THEME_DIRNAME;

        $baseThemeDir = $this->cfg->get('paths/base/dirname');
        $baseApp = self::$APP_DIR . $baseThemeDir;
        $baseSkin = self::$SKIN_DIR . $baseThemeDir;

        // checks main theme
        if (!is_dir($themeAppDir)) {
            $this->_copyWithLocking($baseApp, $themeAppDir);
        }
        if (!is_dir($themeSkinDir)) {
            $this->_copyWithLocking($baseSkin, $themeSkinDir);
        }

        // checks preview theme
        if (!is_dir($previewThemeAppDir)) {
            $this->_copyWithLocking($themeAppDir, $previewThemeAppDir);
        }
        if (!is_dir($previewThemeSkinDir)) {
            $this->_copyWithLocking($themeSkinDir, $previewThemeSkinDir);
        }

        /** @var Theme_Designer_Model_Export_Storage_Abstract $diff */
        $diff = Mage::getSingleton('designer/export_storage_diff', array('theme' => $this->currentTheme));
        $this->_updateTheme($diff->load()->toArray(), false); // restores preview theme state to base
        $diff->remove();

        $this->setAjaxResponse();
    }

    /**
     * Returns theme package
     */
    public function downloadAction()
    {
        $themeName = $this->getRequest()->getParam('themeName', $this->currentTheme);
        $themeName = trim($themeName) ? $themeName : $this->currentTheme;
        $installPath = MAGENTO_ROOT . $this->cfg->get('paths/theme/install', array('theme' => $this->currentTheme));

        $this->fso->copy(MAGENTO_ROOT . '/app/code/local/Theme', $installPath . '/app/code/local/Theme');
        $this->fso->copy(MAGENTO_ROOT . '/app/design/adminhtml/default/default/template/designer', $installPath . '/app/design/adminhtml/default/default/template/designer');
        $this->fso->copy(MAGENTO_ROOT . '/skin/adminhtml/default/default/designer', $installPath . '/skin/adminhtml/default/default/designer');
        $this->fso->copy(MAGENTO_ROOT . '/app/etc/modules/Theme_Ajax.xml', $installPath . '/app/etc/modules/Theme_Ajax.xml');
        $this->fso->copy(MAGENTO_ROOT . '/app/etc/modules/Theme_Designer.xml', $installPath . '/app/etc/modules/Theme_Designer.xml');
        $this->fso->copy(MAGENTO_ROOT . '/app/design/adminhtml/default/default/layout/designer.xml', $installPath . '/app/design/adminhtml/default/default/layout/designer.xml');

        //Theme_Designer_Model_Export_Project::open($this->currentTheme)->removeSession();

        $info = Mage::getModel('designer/package_info');
        $info->setName($themeName);
        $info->addContent('magedesign', 'frontend/default' . self::$THEME_DIRNAME); // base theme
        $info->addContent('mageskin', 'frontend/default' . self::$THEME_DIRNAME); // base theme
        $info->addContent('magedesign', 'frontend/default' . self::$PREVIEW_DIRNAME); // preview theme
        $info->addContent('mageskin', 'frontend/default' . self::$PREVIEW_DIRNAME); // preview theme

        $ext = Mage::getModel('designer/package_extension', array('themeName' => $themeName));
        $ext->setData($info->getData());
        $ext->createPackage();

        //Theme_Designer_Model_Export_Project::open($this->currentTheme)->saveSession();

        $this->fso->remove($installPath, true);

        $packageFilePath = $info->getArchivePath();
        if (is_readable($packageFilePath)) {
            $ajax = $this->getRequest()->getParam('ajax', false);
            if (!$ajax) {
                $this->getResponse()
                    ->setHttpResponseCode(200)
                    ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true)
                    ->setHeader('Pragma', 'public', true)
                    ->setHeader('Content-type', 'application/octet-stream')
                    ->setHeader('Content-Length', filesize($packageFilePath))
                    ->setHeader('Content-Disposition', 'attachment' . '; filename=' . basename($packageFilePath));
            }
            $this->getResponse()->clearBody();
            $this->getResponse()->sendHeaders();
            // todo: possible outofmemory exception
            if ($ajax) {
                $pathInfo = pathinfo($packageFilePath);
                $this->setAjaxResponse(array(
                    'ext' => array_key_exists('extension', $pathInfo) ? $pathInfo['extension'] : 'tgz',
                    'content' => base64_encode(file_get_contents($packageFilePath))
                ));
            } else {
                readfile($packageFilePath);
            }
        }
    }

    public function renameAction()
    {
        $newThemeName = $this->getRequest()->getPost('newName', '');

        if (trim($newThemeName)) {
            $newPreview = $this->cfg->get('paths/preview/dirname', array('theme' => $newThemeName));
            $newBase = $this->cfg->get('paths/theme/dirname', array('theme' => $newThemeName));
            if (is_dir(self::$APP_DIR . $newPreview) || is_dir(self::$APP_DIR . $newBase)
                    || is_dir(self::$SKIN_DIR . $newPreview) || is_dir(self::$SKIN_DIR . $newBase)) {
                $this->setAjaxResponse(array('result' => 'fail', 'error' => 'theme already exists'));
            } else {
                $this->fso->copy(self::$APP_DIR . self::$PREVIEW_DIRNAME, self::$APP_DIR . $newPreview);
                $this->fso->copy(self::$APP_DIR . self::$THEME_DIRNAME, self::$APP_DIR . $newBase);

                $this->fso->copy(self::$SKIN_DIR . self::$PREVIEW_DIRNAME, self::$SKIN_DIR . $newPreview);
                $this->fso->copy(self::$SKIN_DIR . self::$THEME_DIRNAME, self::$SKIN_DIR . $newBase);

                $this->fso->remove(self::$APP_DIR . self::$PREVIEW_DIRNAME, true);
                $this->fso->remove(self::$APP_DIR . self::$THEME_DIRNAME, true);
                $this->fso->remove(self::$SKIN_DIR . self::$PREVIEW_DIRNAME, true);
                $this->fso->remove(self::$SKIN_DIR . self::$THEME_DIRNAME, true);

                $this->setAjaxResponse(array('result' => 'OK'));
            }
        } else {
            $this->setAjaxResponse(array('result' => 'fail', 'error' => 'invalid name param'));
        }
    }

    private function _copyWithLocking($from, $to)
    {
        $lockFile = self::$APP_DIR . '/lock';
        $ff = fopen($lockFile, 'c');
        if (flock($ff, LOCK_EX)) {
            if (!is_dir($to)) {
                mkdir($to, 0777, true);
            }
            $this->fso->copy($from, $to);
            file_put_contents($to . '/complete', '');
            flock($ff, LOCK_UN);
        } else {
            throw new Mage_Adminhtml_Exception('Coping with locking fails');
        }
    }

    /**
     * @param Array $updateList
     * @param bool $saveAction Update base theme if true, otherwise update preview (copy from or copy to list)
     * @throws Mage_Adminhtml_Exception
     */
    private function _updateTheme($updateList, $saveAction)
    {
        if (!is_array($updateList) || !count($updateList)) return;

        if (!$saveAction) {
            $this->fso->remove(Mage::helper('designer/path')->previewAppDir($this->currentTheme) . '/complete');
        }

        $lock = fopen(self::$APP_DIR . '/lock', 'c');
        if (flock($lock, LOCK_EX)) {

            foreach ($updateList as $previewFile) {
                $previewFile = str_replace('\\', '/', $previewFile);
                $themeFile = $this->_getThemeFileName($previewFile);

                if ($saveAction) {
                    // update base theme from preview
                    $this->_updateThemeFile($themeFile, $previewFile);
                } else {
                    // restore preview theme from base
                    $this->_restorePreviewFile($previewFile, $themeFile);
                }
            }

            $this->preview->save();

            flock($lock, LOCK_UN);
        } else {
            throw new Mage_Adminhtml_Exception('Update with locking fails');
        }

        if (!$saveAction) {
            $this->fso->write(Mage::helper('designer/path')->previewAppDir($this->currentTheme) . '/complete', '');
        }
    }

    private function _updateThemeFile($themeFile, $sourceFile)
    {
        if (file_exists($sourceFile)) {
            @mkdir(dirname($themeFile), 0777, true);

            if (strpos($themeFile, Mage::helper('designer/path')->appDir()) !== false) {
                $content = $this->preview->removeDataId($sourceFile);
                file_put_contents($themeFile, $content);
            } else {
                copy($sourceFile, $themeFile);
            }

        } else if (file_exists($themeFile)) {
            unlink($themeFile);
            $this->preview->removeKey($themeFile);
        }

    }

    private function _restorePreviewFile($previewFile, $sourceFile)
    {
        if (file_exists($sourceFile)) {
            @mkdir(dirname($previewFile), 0777, true);

            if (strpos($previewFile, Mage::helper('designer/path')->appDir()) !== false) {
                $content = $this->preview->restoreDataId($sourceFile);
                file_put_contents($previewFile, $content);
            } else {
                copy($sourceFile, $previewFile);
            }

        } else if (file_exists($previewFile)) {
            unlink($previewFile);
        }

    }

    private function _getThemeFileName($diffFile)
    {
        /** @var Theme_Designer_Helper_Path $pathHelper */
        $pathHelper = Mage::helper('designer/path');
        $previewAppDir = $pathHelper->previewAppDir($this->currentTheme);
        $previewSkinDir = $pathHelper->previewSkinDir($this->currentTheme);

        $themeAppDir = $pathHelper->themeAppDir($this->currentTheme);
        $themeSkinDir = $pathHelper->themeSkinDir($this->currentTheme);

        $themeFile = '';

        if (strpos($diffFile, $previewAppDir) !== false) {
            $themeFile = str_replace($previewAppDir, $themeAppDir, $diffFile);
        } else if (strpos($diffFile, $previewSkinDir) !== false) {
            $themeFile = str_replace($previewSkinDir, $themeSkinDir, $diffFile);
        }

        return $themeFile;
    }

    /**
     * Builds export from controls collection
     * @param $content
     * @param string $themeSuffix
     */
    private function _buildExport($content, $params = array())
    {
        $exportParams = array(
            'name' => $this->currentTheme,
            'content' => $content,
            'dirname' => self::$PREVIEW_DIRNAME
        );

        $exportParams = array_merge($exportParams, $params);

        $export = Mage::getModel('designer/export_builder', $exportParams);
        $export->build();
        $export->saveTo(MAGENTO_ROOT);

        if (!$this->getRequest()->getParam('debug', false)) {
            $export->clear();
        }
    }

    private function _updateHashes($hashes)
    {
        /** @var Theme_Designer_Model_Export_Storage_Abstract $hashStorage */
        $hashStorage = Mage::getSingleton('designer/export_storage_hashes', array(
            'theme' => $this->currentTheme
        ));
        $hashStorage->load()->update($hashes);
        $hashStorage->save();
    }

    /**
     * @param array $cache Cache data
     */
    private function _updateCache($cache)
    {
        /** @var Theme_Designer_Model_Export_Storage_Abstract $cacheStorage */
        $cacheStorage = Mage::getSingleton('designer/export_storage_cache', array(
            'theme' => $this->currentTheme
        ));
        $cacheStorage->load()->update($cache);
        $cacheStorage->save();
    }

    /**
     * @param array $positions Positions data
     */
    private function _updatePositions($positions)
    {
        $data = Mage::helper('designer')->getBlocksData();
        $exportPositions = Mage::getModel('designer/export_position');
        $exportPositions->update($data, $positions);
    }

}

//--*@