<?php
/**
 * YANA library
 *
 * @package yana
 * @licence http://creativecommons.org/licenses/by-nc-sa/2.5/
 *
 * @ignore
 */

/**
 * «Singleton» Skin
 *
 * @access      public
 * @package     yana
 * @subpackage  core
 *
 * @ignore
 */
class Skin extends Singleton
{

    /**#@+
     * @ignore
     * @access  private
     */

    /** @var string    */ var $directory  = "";
    /** @var dirStream */ var $dirStream  = null;
    /** @var smlfile[] */ var $files      = array();
    /** @var array     */ var $value      = array();

    /**#@-*/

    /**
     * «Singleton» Constructor
     *
     * @param string $directory
     * @param dirStream $dirStream
     * @param dirStream $skinDir
     * @param dirStream $defaultDir
     *
     * @ignore
     */
    function Skin($directory, $dirStream, $skinDir, $defaultDir)
    {
        $this->checkSingletonConstraint();
        assert('is_string($directory) && is_dir($directory);');
        settype($directory, "String");

        $this->directory = $directory;
        $this->dirStream = $dirStream;
        $this->dirStream->read();

        /* Load Defaults first */
        if ($defaultDir->getFilename() != $skinDir->getFilename()){
            $content = $defaultDir->get();
            foreach ($content as $a => $filename)
            {
                $this->files[$filename] = new smlfile($defaultDir->getFilename().$filename);
                $this->files[$filename]->init();
                $REF = $this->files[$filename]->get("*");
                foreach ($REF as $key => $element)
                {
                    if (!empty($element['FILE'])) {
                        $this->value[$key]['FILE'] = "../../".$defaultDir->getFilename().$element['FILE'];
                    }
                    if (!empty($element['STYLE'])) {
                        skin::_fallback($this->value[$key]['STYLE'], $element['STYLE'], $defaultDir->getFilename());
                    }
                    if (!empty($element['SCRIPT'])) {
                        skin::_fallback($this->value[$key]['SCRIPT'], $element['SCRIPT'], $defaultDir->getFilename());
                    }
                    if (!empty($element['LANGUAGE'])) {
                        skin::_fallback($this->value[$key]['LANGUAGE'], $element['LANGUAGE'], false);
                    }
                }
            }
        }

        /* Overwrite Defaults where needed */
        $content = $skinDir->get();
        foreach ($content as $a => $filename)
        {
            $this->files[$filename] = new smlfile($skinDir->getFilename().$filename);
            $this->files[$filename]->init();
            $REF = $this->files[$filename]->get("*");
            foreach ($REF as $key => $element)
            {
                if (!empty($element['FILE'])) {
                    $this->value[$key]['FILE'] = $element['FILE'];
                }
                if (!empty($element['STYLE'])) {
                    skin::_fallback($this->value[$key]['STYLE'], $element['STYLE'], $skinDir->getFilename());
                }
                if (!empty($element['SCRIPT'])) {
                    skin::_fallback($this->value[$key]['SCRIPT'], $element['SCRIPT'], $skinDir->getFilename());
                }
                if (!empty($element['LANGUAGE'])) {
                    skin::_fallback($this->value[$key]['LANGUAGE'], $element['LANGUAGE'], false);
                }
            }
        }
    }

    /**
     * get
     *
     * @access public
     * @param string $reference (optional)
     * @param string $key (optional)
     * @return array
     */
    function get($reference = "*", $key = "*")
    {
        settype($reference, "String");
        settype($key,       "String");

        $REF = array();
        if (empty($key)) {
            $key = "*";
        }
        if (empty($reference)) {
            $reference = "*";
        }
        $key       = strtoupper($key);
        $reference = strtoupper($reference);

        if ($reference == "*") {
            foreach ($this->files as $a => $element) 
            {
                $REF = mergeArrays($REF, $element->get($key));
            }
            return $REF;
        } else if (!isset($this->files[$reference])) {
            return false;
        } else {
            return $this->files[$reference]->get($key);
        }

    }

    /**
     * getFile
     *
     * @access public
     * @param string $key
     * @return string|boolean
     */
    function getFile($key)
    {
        assert('is_string($key);');
        settype($key, "String");

        if (empty($key)) {
            return false;
        } else {
            $key = strtoupper($key);
            $file_definition =& $this->value[$key];

            if (!is_null($file_definition)) {
                global $YANA;
                $language_list = $this->getLanguage($key);
                foreach ($language_list as $filename)
                {
                      $YANA->language->read($filename);
                }
                $style_list = $this->getStyle($key);
                if (is_array($style_list)) {
                    $old_style_list = $YANA->view->document->get('SYSTEM_STYLE');
                    if (is_array($old_style_list)) {
                        $style_list = array_merge($old_style_list, $style_list);
                    }
                    $YANA->view->document->insert('SYSTEM_STYLE', $style_list);
                }
                $script_list = $this->getScript($key);
                if (is_array($script_list)) {
                    $old_script_list = $YANA->view->document->get('SYSTEM_SCRIPT');
                    if (is_array($old_script_list)) {
                        $script_list = array_merge($old_script_list, $script_list);
                    }
                    $YANA->view->document->insert('SYSTEM_SCRIPT', $script_list);
                }
                return $this->getDirectory().$file_definition['FILE'];
            } else {
                return false;
            }
        }

    }

    /**
     * getLanguage
     *
     * @access public
     * @param string $key
     * @return array
     */
    function &getLanguage($key)
    {
        assert('is_string($key);');
        settype($key, "String");

        if (empty($key)) {
            return false;
        } else {
            $key = strtoupper($key);
    
            $file_definition =& $this->value[$key];
            $language_list   =& $file_definition['LANGUAGE'];
    
            if (!is_null($language_list)) {
                if (is_array($language_list)) {
                    return $language_list;
                } else if (is_string($language_list)) {
                    $result = array(&$language_list);
                    return $result;
                }
            } else {
                $result = array();
                return $result;
            }
        }
    }

    /**
     * getStyle
     *
     * @access public
     * @param string $key
     * @return array
     */
    function getStyle($key)
    {
        assert('is_string($key);');
        settype($key, "String");
        $key = strtoupper($key);

        $file_definition =& $this->value[$key];
        $style_list      = @$file_definition['STYLE'];

        if (empty($key)) {
            return false;
        } else if (!is_array($style_list)) {
            return array();
        } else {
            return $style_list;
        }
    }

    /**
     * getScript
     *
     * @access public
     * @param string $key
     * @return array
     */
    function getScript($key)
    {
        assert('is_string($key);');
        settype($key, "String");
        $key = strtoupper($key);

        $file_definition =& $this->value[$key];
        if (isset($file_definition['SCRIPT'])) {
            $script_list = $file_definition['SCRIPT'];
        } else {
            $script_list = "";
        }

        if (empty($key)) {
            return false;
        } else if (!is_array($script_list)) {
            return array();
        } else {
            return $script_list;
        }
    }

    /**
     * getContent
     *
     * @access public
     * @return array (numeric array of strings)
     */
    function getContent()
    {
        $content = $this->dirStream->getContent();
        assert('is_array($content);');
        if (is_array($content)) {
            return $content;
        } else {
            return array();
        }
    }

    /**
     * getDirectory
     *
     * @access public
     * @return string
     */
    function getDirectory()
    {
        assert('is_string($this->directory);');
        return $this->directory;
    }

    /**
     * getInfo
     *
     * @access public
     * @param string $filename
     * @return array
     */
    function getInfo($filename)
    {
        assert('is_string($filename);');
        settype($filename, "String");

        $file = ($this->dirStream->getFilename()).$filename;
        if (is_file($file)) {
            $REF = getConfigFile($file);
            assert('is_array($REF["SKIN_INFO"]);');
            if (is_array($REF['SKIN_INFO'])) {
                $REF['SKIN_INFO']['LAST_CHANGE'] = filemtime($file);
                clearstatcache();
                return $REF["SKIN_INFO"];
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    
    /**
     *  _fallback
     *
     * implements fallback behaviour
     *
     * @access private
     * @param array|string &$new
     * @param array|null &$list
     * @param string|boolean $prefix (optional)
     * @return &array
     * @ignore
     */
    function _fallback(&$list, &$new, $prefix = "")
    {
        if (!empty($new)) {

            settype($new, 'array');
            settype($list, 'array');

            /* overwrite defaults where required */
            foreach (array_keys($new) as $i)
            {
                $var =& $new[$i];
                if (is_string($prefix)) {
                    $var = $prefix.$new[$i];
                }
                /* NOTE: Collisions are treated as follows.
                 * Numeric indices will always be appended, no matter what, while textual indices will get replaced.
                 *
                 * This allows the user to decide wether to create an 'anonymous' association (numbered index)
                 * or a named association (textual index) with a stylesheet.
                 *
                 * So in a derived template the user may decide for himself what element to take and what to drop.
                 *
                 * Also note, that you can drop a named association in a derived template by entering string('null') or boolean('false') as source.
                 */
                if (is_int($i) && ($prefix === false || is_file($var))) {
                    array_push($list, $var);
                } else if ($var == 'null' || empty($var)) {
                    $list[$i] = false;
                } else if ($prefix === false || is_file($var)) {
                    $list[$i] = $var;
                } else {
                    trigger_error("The provided value '${i}' => '${var}' is not a valid file resource.", E_USER_WARNING);
                }
            }
        } else {
            /* intentionally left blank */
        }
    }

    /**
     * check
     *
     * @access public
     * @return string
     */
    function check()
    {
        /* String */ $txt = "";
        global $YANA;

        foreach ($this->value as $key => $element)
        {
            $filename = $this->getFile($key);
            if (!empty($filename) && !file_exists($filename)) {
                $txt .= "<p>ERROR: Skin entry \"$key\" references to file \"$filename\". But this file does not exist.<br>Please make sure this path and filename is correct and you have all files installed. Reinstall this skin if necessary.</p>\n";
            }
            if (!empty($element['LANGUAGE']) && is_array($element['LANGUAGE']) && count($element['LANGUAGE']) > 0) {
                foreach ($element['LANGUAGE'] as $key2 => $value)
                {
                    if (!empty($value)) {
                        if (!$YANA->language->read($value)) {
                            $txt .= "<p>ERROR: A required language file \"".$value.".config\" \" for template \"$key\" is not available.<br>This template may not be displayed correctly. Please check if the chosen language file is correct and update your language pack if needed.</p>\n";
                        }
                    }
                }
            }
        }
        return $txt;
    }

}

?>