<?php

require_once 'Vitis.class.inc';
require_once __DIR__ . '/../../class/vitis_lib/Connection.class.inc';
//require_once 'Domain.class.inc';
require_once(__DIR__ . '/../../class/vmlib/BdDataAccess.inc');

/**
 * \file properties.class.inc
 * \class Properties
 *
 * \author Yoann Perollet <yoann.perollet@veremes.com>.
 *
 * 	\brief This file contains the Properties php class
 *
 * This class defines the rest api for properties
 *
 */
class Properties extends Vitis {
    /**
     * @SWG\Definition(
     *   definition="/properties",
     *   allOf={
     *     @SWG\Schema(ref="#/definitions/properties")
     *   }
     * )
     * * @SWG\Tag(
     *   name="Properties",
     *   description="Operations about properties"
     * )
     */

    /**
     * construct
     * @param type $aPath url of the request
     * @param type $aValues parameters of the request
     * @param type $properties properties
     * @param type $bShortcut false to reinit variables
     * @param type $oConnection connection object
     */
    function __construct($aPath, $aValues, $properties, $bShortcut = false, $oConnection = false) {
        parent::__construct($aPath, $aValues, $properties, $bShortcut, $oConnection);

        $this->aSelectedFields = Array("services_alias", 'language', 'domain.*', 'vas_home', 'database', 'sign_up', 'password_forgotten', 'login_remember_me', 'login_remember_me_default');

        // Properties accessibles
        if (in_array('vitis_admin', $this->oConnection->aPrivileges)) {
            $this->aSelectedFields = array_merge($this->aSelectedFields, $properties['aAdminFields']);
        }
        if (in_array('vitis_user', $this->oConnection->aPrivileges)) {
            $this->aSelectedFields = array_merge($this->aSelectedFields, $properties['aUserFields']);
        }
    }

    /**
     * @SWG\Get(path="/properties",
     *   tags={"Properties"},
     *   summary="Get properties",
     *   description="Request to get properties",
     *   operationId="GET",
     *   produces={"application/xml", "application/json", "application/x-vm-json"},
     *  @SWG\Parameter(
     *     name="token",
     *     in="query",
     *     description="user token",
     *     required=false,
     *     type="string"
     *   ),
     *  @SWG\Response(
     *         response=200,
     *         description="Poprerties Response",
     *         @SWG\Schema(ref="#/definitions/properties")
     *     )
     *  )
     */

    /**
     *
     * @return properties
     */
    function GET() {

        if (isset($this->aSelectedFields) && !empty($this->aSelectedFields)) {
            $this->aFields = $this->getSelectedFields($this->aProperties);
        } else {
            $this->aFields = $this->aProperties;
        }
        require_once dirname($_SERVER['SCRIPT_FILENAME']) . "/conf/version.inc";

        if (VM_STATUS != "STABLE") {
            $this->aFields['VM_STATUS'] = "UNSTABLE";
        } else {
            $this->aFields['VM_STATUS'] = "STABLE";
            if ($pointeur = opendir(dirname($_SERVER['SCRIPT_FILENAME']) . "/conf/")) {
                while (($fichier = readdir($pointeur)) !== false) {

                    if (($fichier != '.') && ($fichier != '..')) {

                        if (is_dir(dirname($_SERVER['SCRIPT_FILENAME']) . "/conf/" . $fichier)) {
                            $sTexte = file_get_contents(dirname($_SERVER['SCRIPT_FILENAME']) . "/conf/" . $fichier . "/version.inc");

                            if (strpos($sTexte, "define (\"VM_STATUS\", \"STABLE\");") == false) {
                                $this->aFields['VM_STATUS'] = "UNSTABLE";
                            }
                        }
                    }
                }
                //fermeture du pointeur
                closedir($pointeur);
            }
            //pour chaque fichier et dossier
        }
        $aXmlRacineAttribute['status'] = 1;
        $sMessage = $this->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']);
        return $sMessage;
    }

    /**
     * Returns the properties to display
     * @param arrya $aProperties
     * @return array
     */
    function getSelectedFields($aProperties) {
        $aFields = Array();
        foreach ($aProperties as $key => $value) {
            if (is_array($aProperties[$key])) {
                foreach ($this->aSelectedFields as $sSelectedField) {
                    if (strpos($sSelectedField, '.')) {
                        $aSubSelectedFields = explode('.', $sSelectedField);
                        if ($aSubSelectedFields[0] === $key) {
                            if ($aSubSelectedFields[1] === '*') {
                                $aFields[$key] = $aProperties[$key];
                            } else {
                                if (!array_key_exists($key, $aFields)) {
                                    $aFields[$key] = array();
                                }
                                if (!array_key_exists($aSubSelectedFields[1], $aFields[$key])) {
                                    $aFields[$key][$aSubSelectedFields[1]] = array();
                                }
                                $aFields[$key][$aSubSelectedFields[1]] = $this->recursiveGetSubSelectedFields($aProperties[$key], $aSubSelectedFields, $aFields[$key], 1);
                            }
                        }
                    }
                }
            } else {
                if (in_array($key, $this->aSelectedFields)) {
                    $aFields[$key] = $aProperties[$key];
                }
            }
        }
        return $aFields;
    }

    /**
     * Recursive function called by getSelectedFields that returns the sub-selected fields
     * @param array $aSubProperties
     * @param array $aSubSelectedFields
     * @param array $aField
     * @param number $index
     * @return array|string
     */
    function recursiveGetSubSelectedFields($aSubProperties, $aSubSelectedFields, $aField, $index) {
        if (isset($aSubProperties[$aSubSelectedFields[$index]]) && !empty($aSubProperties[$aSubSelectedFields[$index]])) {
            if (empty($aSubSelectedFields[$index + 1])) {
                $aSubSelectedFields[$index + 1] = "";
            }
            if ($aSubSelectedFields[$index + 1] === '*') {
                $aField[$aSubSelectedFields[$index]] = $aSubProperties[$aSubSelectedFields[$index]];
            } else {
                if (is_array($aSubProperties[$aSubSelectedFields[$index]])) {
                    foreach ($aSubProperties[$aSubSelectedFields[$index]] as $key => $value) {
                        if ($key === $aSubSelectedFields[$index + 1]) {
                            if (!empty($aSubProperties[$aSubSelectedFields[$index]][$aSubSelectedFields[$index + 1]])) {
                                if (!array_key_exists($aSubSelectedFields[$index], $aField)) {
                                    $aField[$aSubSelectedFields[$index]] = array();
                                }
                                $aField[$aSubSelectedFields[$index]][$key] = $this->recursiveGetSubSelectedFields($aSubProperties[$aSubSelectedFields[$index]], $aSubSelectedFields, $aField[$aSubSelectedFields[$index]], $index + 1);
                            }
                        }
                    }
                    if (empty($aField[$aSubSelectedFields[$index]])) {
                        $aField[$aSubSelectedFields[$index]] = null;
                    }
                } else {
                    $aField[$aSubSelectedFields[$index]] = $aSubProperties[$aSubSelectedFields[$index]];
                }
            }
        }

        return $aField[$aSubSelectedFields[$index]];
    }

    /**
     * @SWG\Put(path="/properties/{module_name}",
     *   tags={"Properties"},
     *   summary="Update properties",
     *   description="Request to update the properties",
     *   operationId="PUT",
     *   produces={"application/xml", "application/json"},
     *   @SWG\Parameter(
     *     name="token",
     *     in="formData",
     *     description="user token",
     *     required=true,
     *     type="string"
     *   ),
     *   @SWG\Parameter(
     *     name="module_name",
     *     in="path",
     *     description="module name",
     *     required=true,
     *     type="string"
     *   ),
     *   @SWG\Response(
     *         response=200,
     *         description="Poprerties Response",
     *         @SWG\Schema(ref="#/definitions/properties")
     *     ),
     *
     *  )
     */

    /**
     * Update properties
     */
    function PUT() {
        // Verify connection.
        if (!empty($this->oConnection->oError)) {
            $oError = $this->oConnection->oError;
            $aXmlRacineAttribute['status'] = 0;
            $sMessage = $oError->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']);
            return $sMessage;
        }
        // Privilège "vitis_admin" requis.
        if (!in_array('vitis_admin', $this->oConnection->aPrivileges)) {
            $oError = new VitisError(1, "Rights problem : you don't have right to delete log files");
            $aXmlRacineAttribute['status'] = 0;
            $sMessage = $oError->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']);
            return $sMessage;
        }
        if (!empty($this->aValues["properties"])) {
            // Répertoire partagé par défaut si non spécifié.
            if (empty($this->aValues["properties"]->shared_dir))
                $this->aValues["properties"]->shared_dir = $this->aProperties['vas_home'] . '/shared';
            // Paramètres pour le fichier de 'properties'
            define('PROPERTIES_HEADER', 'require "constants.inc";' . PHP_EOL . 'require "properties_server.inc";' . PHP_EOL . 'require "properties_domain.inc";' . PHP_EOL . 'require "selected_properties.inc";');
            define('PROPERTIES_FOOTER', PHP_EOL . PHP_EOL
                    . '// Require tous les fichiers de tous les dossiers contenus sans properties' . PHP_EOL
                    . '$aDir = scandir(__DIR__);' . PHP_EOL
                    . 'foreach ($aDir as $dir) {' . PHP_EOL
                    . '	if($dir != \'.\' && $dir != \'..\'){' . PHP_EOL
                    . '		if(is_dir(__DIR__.\'/\'.$dir)){' . PHP_EOL
                    . '			$aFiles = scandir(__DIR__.\'/\'.$dir);' . PHP_EOL
                    . '			foreach ($aFiles as $file) {' . PHP_EOL
                    . '				if((filetype(__DIR__.\'/\'.$dir.\'/\'.$file) == \'file\') && (pathinfo(__DIR__.\'/\'.$dir.\'/\'.$file)[\'extension\']) == \'inc\' && $file != "version.inc"){' . PHP_EOL
                    . '					require_once __DIR__.\'/\'.$dir.\'/\'.$file;' . PHP_EOL
                    . '				}' . PHP_EOL
                    . '			}' . PHP_EOL
                    . '		}' . PHP_EOL
                    . '	}' . PHP_EOL
                    . '}' . PHP_EOL
                    . PHP_EOL . 'require "properties_post.inc";' . PHP_EOL . 'include "php_conf.inc";');
            define('PROPERTIES_TEMPLATE', '$properties[\'[key]\'] = [value];'); // Modèle pour chaque ligne du fichier
            define('PHP_START', '<?php'); // Début du php
            define('PHP_END', '?>'); // Fin du php
            define('ARRAY_DECLARATION_TEMPLATE', 'array([value])'); // Modèle de la déclaration d'un tableau
            define('ARRAY_CONTENT_TEMPLATE', '[key] => [value]'); // Modèle du contenu d'un tableau
            define('ARRAY_VALUE_SEPARATOR', ', '); // Caractère séparant les valeurs d'un tableau
            define('STR_CH_ENCLOSE', '\''); // Caractère entourant les valeurs de type 'string'
            define('NUMERIC_ENCLOSE', false); // Entoure avec des simples/doubles quotes les valeurs numérique comme les chaines de caractères ?
            define('REQUEST_SUCCESS', 0);
            define('REQUEST_ERROR', 1);

            $aReturn = $this->writeProperties($this->aValues["properties"]);
        }
        //
        $aXmlRacineAttribute['status'] = $aReturn['status'];
        if ($aReturn['status'] == 1)
            $sMessage = $this->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']);
        else {
            $oError = new VitisError($aReturn['error_code'], $aReturn['message']);
            $sMessage = $oError->asDocument('', 'vitis', $this->aValues['sEncoding'], True, $aXmlRacineAttribute, $this->aValues['sSourceEncoding'], $this->aValues['output']);
        }
        return $sMessage;
    }

    /*     * ********************************
      Ecrase le fichier des properties avec le
      contenu des paramètres du formulaire
      $aPropertiesForm : tableau contenant la liste des clés/valeurs à écrire dans le fichier de properties
      $aPropertiesExclude : tableau contenant des champs du formulaire à exclure pour l'écriture du fichier de properties
     * ********************************* */

    function writeProperties($aPropertiesForm, $aPropertiesExclude = array()) {
        $aReturn = array('status' => 1, 'message' => '');
        // Vérifie que le formulaire de properties n'est pas vide
        if (!empty($aPropertiesForm)) {

            // Message de retour
            $aMessage = array('status' => REQUEST_SUCCESS);

            // Ouverture de php
            $sPropertiesFile = PHP_START . PHP_EOL;

            // Texte à ajouter avant le tableau des properties
            if ($this->aValues['module_name'] == 'vitis')
                $sPropertiesFile .= PROPERTIES_HEADER . PHP_EOL;

            // Convertit chaque valeur du tableau 'properties' en ligne de code php
            foreach ($aPropertiesForm as $sKey => $v) {
                // La valeur n'est pas à exclure ?
                if (!in_array($sKey, $aPropertiesExclude)) {
                    // Formate la valeur suivant le type
                    if (is_array($v))
                        $v = $this->exportArray($v);
                    else {
                        // Booléen ?
                        if ($v == 'true')
                            $v = true;
                        else if ($v == 'false')
                            $v = false;

                        $v = $this->formatValue($v);
                    }

                    // Exception pour 'max_upload_file_size' : conversion en octets
                    if ($sKey == 'max_upload_file_size')
                        $v .= ' * 1024 * 1024';

                    // Remplit la clé, la valeur et le retour à la ligne
                    $sKey = str_replace('.', "']['", $sKey);
                    $sPropertiesLine = str_replace('[key]', $sKey, PROPERTIES_TEMPLATE);
                    $sPropertiesLine = str_replace('[value]', $v, $sPropertiesLine);
                    $sPropertiesLine .= PHP_EOL;

                    // Ajoute la nouvelle ligne
                    $sPropertiesFile .= $sPropertiesLine;
                }
            }

            // Texte à ajouter après le tableau des properties
            if ($this->aValues['module_name'] == 'vitis') {
                $sPropertiesFile .= PROPERTIES_FOOTER . PHP_EOL;
            }

            // Fermeture de php
            $sPropertiesFile .= PHP_END;

            // Sauve le fichier (LOCK_EX = accés exclusif au fichier)
            if ($this->aValues['module_name'] == 'vitis')
                $sPathProperties = dirname($_SERVER['SCRIPT_FILENAME']) . '/conf/properties.inc';
            else
                $sPathProperties = dirname($_SERVER['SCRIPT_FILENAME']) . '/conf/' . $this->aValues['module_name'] . '/properties.inc';
            //
            if (file_put_contents($sPathProperties, $sPropertiesFile, LOCK_EX) === false)
                $aReturn = array('status' => 0, 'message' => "FORM_ERROR_FILE_WRITING_CONFIGURATION");
        }
        //else
        //$aReturn = array('status' => 0, 'message' => "Le formulaire de properties passé en paramètre est vide");
        // Retourne le tableau contenant le message
        return $aReturn;
    }

    /*     * ********************************
      Retourne la déclaration d'un tableau en php
      (fonction récursive pour les tableaux multi-dimmensions)
      $aArray : le tableau à retourner
     * ********************************* */

    function exportArray($aArray) {
        $sMyArray = '';
        foreach ($aArray as $sKey => $v) {
            // Pas la 1ere valeur du tableau ?
            if ($sMyArray != '')
                $sMyArray .= ARRAY_VALUE_SEPARATOR;

            // Ajoute la clé du tableau
            if (is_int($sKey))
                $sMyArray .= str_replace('[key]', $sKey, ARRAY_CONTENT_TEMPLATE);
            else
                $sMyArray .= str_replace('[key]', (STR_CH_ENCLOSE . $sKey . STR_CH_ENCLOSE), ARRAY_CONTENT_TEMPLATE);

            // Si la valeur est un tableau : récursif
            if (is_array($v))
                $v = exportArray($v);
            else
                $v = $this->formatValue($v);

            // Ajoute la valeur du tableau
            $sMyArray = str_replace('[value]', $v, $sMyArray);
        }

        // Ajoute la valeur du tableau
        $sMyArray = str_replace('[value]', $sMyArray, ARRAY_DECLARATION_TEMPLATE);
        return $sMyArray;
    }

    /*     * ********************************
      Retourne une valeur formatée suivant le type (str, nombre, bool...)
      $MyValue : Valeur du tableau à formater
     * ********************************* */

    function formatValue($MyValue) {
        // Type numérique ?	--> pour les nombres qui sont entre 'quotes'
        if (is_numeric($MyValue)) {
            if (is_int($MyValue)) {
                $MyValue = (int) $MyValue; // Force en entier
            } else if (is_float($MyValue)) {
                $MyValue = (float) $MyValue; // Force en décimal
            }
            // Transforme les valeur numériques	en chaine de caractères ?
            if (NUMERIC_ENCLOSE) {
                $MyValue = STR_CH_ENCLOSE . addslashes($MyValue) . STR_CH_ENCLOSE;
            }
        }
        // Une chaine de caractère ?
        else if (is_string($MyValue))
            $MyValue = STR_CH_ENCLOSE . addslashes($MyValue) . STR_CH_ENCLOSE;
        else
            $MyValue = var_export($MyValue, true);

        return $MyValue;
    }

}

?>