<?php  
/*  
Ecrit et traite un formulaire sur base d'une description en yaml 
ou d'array associatif
*/
class yamlReadwriter {
    public $content=array();
    public $content_structure;
    public $content_file=false;
    public $destination_folder;
    public $filename;
    public $fileType;

    public $snippet_folder;

    public $messages=array();
    public $compact_output=true;

    public $form_submit="Submit";
    public $form_title="";
    public $form_comment="";
    public $form_delete=false;
    public $containsFiles=false;
    public $form_hidden=array();

    function __construct() {
    }

    public function loadYaml($temp_source){
        if(!is_file($temp_source)){
            $this->messages['error'][]="No file $temp_source !";
            return false;
        }
        $this->content_structure=$temp_source;
        $this->content = Spyc::YAMLLoad($temp_source);
    }

    public function setType($type){
        $this->fileType=$type;
    }
    public function getType($tag){
        if(isset($this->content[$tag]) && isset($this->content[$tag]['type'])){
            return $this->content[$tag]['type'];
        }
        return false;
    }

    public function setSnippetFolder($folder){
        if(is_dir($folder)){
            $this->snippet_folder=$folder;
        } else {
            $this->messages['error'][]="No snippet folder! (".$folder.")";
            return false;
        }
    }

    public function setDestinationFolder($folder){
        if(is_dir($folder)){
            $this->destination_folder=$folder;
        } else {
            $this->messages['error'][]="No destination folder! (".$folder.")";
            return false;
        }
    }

    public function loadContentFromYaml($temp_source){
        if(!is_file($temp_source)){
            $this->messages['error'][]="No file $temp_source !";
            return false;
        }

        $array = Spyc::YAMLLoad($temp_source);
        $this->content_file=$temp_source;
        $this->filename=basename($temp_source);
        $this->loadContentFromArray($array);
    }

    public function loadContentFromArray($array){
        // reçoit un array soit simple soit complexe
        foreach($array as $name=>$value){
            if(isset($this->content[$name])){
                // soit c'est un fichier structuré, 
                // soit c'est un multiple

                if(isset($value['value'])){
                    //c'est une valeur structurée
                    $this->content[$name]['value']=$value['value'];
                } else {
                    $this->content[$name]['value']=$value;
                }


                /*
                if(is_array($value) && isset($value['value'])){
                    $this->content[$name]['value']=$value['value'];
                } else {
                    $this->content[$name]['value']=$value;
                }
                */
            }
        }
    }

    public function contentAddField($name,$fields){
        $this->content[$name]=$fields;
    }

    public function contentDeleteField($field){
        if(isset($this->content[$field])){
            unset($this->content[$field]);
        }
    }

    public function writeStructure($dest){

    }

    public function getSimpleArray(){
        $retour=array();
        foreach($this->content as $cle=> $valeur){
            if(is_array($valeur)){
                if(isset($valeur["value"])){
                    if($valeur["type"]!="separateur"){
                        $retour[$cle]=$valeur["value"];  
                    }
                } else {
                    $retour[$cle]=$valeur;
                }
            } else {
                $retour[$cle]=$valeur;
            }
        }
        return $retour;
    }

    public function getFilledValues(){
        $values=$this->getSimpleArray();
        foreach($values as $key=>$value){
            if(is_array($value)){
                if(count($value)==0){
                    unset($values[$key]);
                    continue; 
                }
            } else {
                if(empty($value)){
                    unset($values[$key]);
                    continue;
                }
            }
        }
        return $values;
    }

    // renvoyer la liste des fichiers dans ce yaml
    public function getFileList(){
        $retour=array();
        foreach($this->content as $cle=> $valeur){
            if(is_array($valeur) && isset($valeur['type'])){
                if($valeur['type']=="file"){
                    // aplatir les valeur en array
                    if(is_array($valeur["value"])){
                        // peut être simple (file-descr)
                        
                        if(isset($valeur["value"]["file"])){
                            $retour[]=$valeur["value"];
                        } else {
                            // ou multiple (0->(file-descr) 1->file-descr)
                            foreach($valeur["value"] as $file){
                                if($file==""){ continue; }
                                $retour[]=$file; 
                            }
                        }
                    } else {
                        if($valeur["value"]==""){ continue; }
                        $retour[]=array("file" => $valeur["value"], "description"=>""); 
                    }
                } 
            }
        }
        return $retour;
    }

    // ecrire le form
    public function writeForm(){ 
        include($this->snippet_folder."form-start.php");

        // information nécessaies au traitement
        $this->writeHidden("source",$this->filename);
        $this->writeHidden("structure",$this->content_structure);
        $this->writeHidden("destination_folder",$this->destination_folder);
        $this->writeHidden("compact_output",$this->compact_output);
        $this->writeHidden("type",$this->fileType);

        // hidden demandé spécifiquement
        foreach($this->form_hidden as $name => $value){
            $this->writeHidden($name,$value);
        }

        $sourceitem=$this->filename;
        foreach($this->content as $name => $field){
            // si on a un fichier complet (avec type, content etc.)
            // en faire une version 
            $field = (object) $field;

            switch($field->type){

                case "separateur":
                    include($this->snippet_folder."separateur.php");
                    break;

                case "hidden":
                    include($this->snippet_folder."hidden.php");
                    break;

                default:
                    include($this->snippet_folder."field-start.php");
                    include($this->snippet_folder.$field->type.".php");
                    include($this->snippet_folder."field-end.php");
                    break;
            }
        }
        include($this->snippet_folder."form-end.php");
    }

    private function writeHidden($name,$value){
        echo "<input type=\"hidden\" name=\"".$name."\" value=\"".$value."\">";
    }

    public function addHidden($name,$value){
        if($name){
            $this->form_hidden[$name]=$value;
        } 
    }

    public function processPost($savefile=false){
        if(!isset($_POST)){
            $this->messages['error'][]="No post content";
            return false;
        } 
        $fichier=$_POST["source"];
        // charger le fichier d'origine
        unset($_POST["source"]);
        unset($_POST["structure"]);
        unset($_POST["submit"]);

        if(isset($_POST['action'])){
            switch($_POST['action']){
                case("submitform"):
                    unset($_POST['action']);
                    if(isset($_POST['compact_output'])){
                        $this->compact_output=filter_var($_POST['compact_output'], FILTER_VALIDATE_BOOLEAN);
                        unset($_POST['compact_output']);
                    }

                    // attention, les fichiers arrivent en hidden, pas en file
                    foreach($_POST as $cle => $valeur){
                        $type=$this->getParam($cle,"type");
                        switch($type){
                            case "file":
                                $this->processFiles($cle, $valeur);
                                break;
                            default:
                                $this->processText($cle,$valeur);
                        }

                    }
                    //$this->processText($_POST);
                    if($savefile){
                        // si le fichier de destination n'existe pas, il sera créé par la fonction writeyaml
                        $this->writeyaml();
                    }
                    $this->messages['success'][]="file updated";
                    break;  
            }
        }
    }

    private function processFiles($cle, $valeur){
        // récupérer le nom du fichier et son descriptif
        // renvoyer les valeurs multiples alors que le tag ne l'est pas
        if(is_array($valeur) && !$this->isTagmultiple($cle)){ return false; }

        if(!is_array($valeur)) { 

            $valeur=validateTempfile($valeur);
            $file=trim($valeur);

            if(isset($_POST["description-".$cle])){
                $description=trim($_POST["description-".$cle]);
            } else {
                $description="";
            }
            $this->content[$cle]['value']=array(
                "file"=>$file,
                "description"=>$description
            );
        } else {
            $valeurs=array();
            foreach($valeur as $num=>$val){
                // filtrer les fichiers temp et les déplacer
                $val=validateTempfile($val);
                $file=trim($val);

                if(isset($_POST["description-".$cle][$num])){
                    $description=trim($_POST["description-".$cle][$num]);
                } else {
                    $description="";
                }
                $valeurs[$num]=array(
                    "file"=>$file,
                    "description"=>$description
                );
            }
            $this->content[$cle]['value']=$valeurs;
        }

    }

    private function processText($cle, $valeur){
        if(isset($this->content[$cle])){
            // ici ajouter un contrôle du type de champ: text, radio, file, rtc

            // si les valeurs sont multiples, vérifier que c'est ok et les traiter
            if(is_array($valeur)){
                if(!$this->isTagmultiple($cle)){ return false; }
                $valeurs=array();
                foreach($valeur as $num=>$val){
                    // filtrer les fichiers temp et les déplacer
                    if($this->content[$cle]['type']=="file"){
                        $val=validateTempfile($val);
                    }

                    $valeurs[]=trim($val);
                }
                $this->content[$cle]['value']=$valeurs;
            } else {
                if($this->content[$cle]['type']=="file"){
                    $valeur=validateTempfile($valeur);
                }
                $this->content[$cle]['value']=trim($valeur);

                $this->messages["success"][] ="entré ".$cle." avec valeur ".trim($valeur);  
            }
        } else {
            // récupération des fichiers à supprimer
            if (strpos($cle, "delete-")===0) {
                //$ncle=str_replace("delete-","",$this->content[$cle]);
                if(is_array($valeur)){
                    foreach($valeur as $num=>$filetodelete){
                        include($this->snippet_folder."file-delete.php");
                    }
                } else {
                    $filetodelete=$valeur;
                    include($this->snippet_folder."file-delete.php"); 
                }
                $this->messages["success"][] ="trouvé un doc à supprimer dans ".$cle;  
            } else {
                $this->messages["error"][] ="clé ".$cle." ignorée";
            }
        }

    }

    public function isTagmultiple($cle){
        if(isset($this->content[$cle]["multiple"]) 
           && $this->content[$cle]["multiple"]=="true"){
            return true;

        } else {
            return false;
        }
    }

    public function writeyaml(){
        // contrôle de l'éventuel champ date_creation
        if(isset($this->content['date_creation'])){
            if(!$this->get('date_creation',false)){
                $this->set('date_creation', date('Y-m-d'));
            }
        }

        if($this->compact_output){
            $yaml_str = Spyc::YAMLDump($this->getSimpleArray());
        } else {
            $yaml_str = Spyc::YAMLDump($this->content);
        }
        // si $destination est vide
        // créer le fichier
        if(!$this->filename){
            $this->nameFile();
        }
        if(!$this->destination_folder){
            $this->messages["error"][] ="File ".$this->filename." failed to be written: no destination folder...";
            return false;
        }
        $this->content_file=$this->destination_folder.$this->filename;

        if(file_exists($this->content_file)){
            chmod($this->content_file, 0777); // autoriser la réécriture
        }
        if(file_put_contents($this->content_file,$yaml_str)){
            $this->messages["success"][] ="File ".$this->content_file." written ";
            return $this->filename;
        } else {
            $this->messages["error"][] ="File ".$this->content_file." failed to be written ";
            return false;
        }
    }

    public function deleteyaml(){
        // supprimer les documents attachés et leurs vignettes
        foreach($this->content as $cle=> $valeur){
            if($valeur['type']!="file"){ continue; }
            $co=$valeur["value"];
            if(is_array($co)){
                foreach($co as $dd){
                    $this->deletedoc($dd); 
                }
            } else {
                $this->deletedoc($co);
            }
        }
        // supprimer le fichier
        unlink($this->content_file);
    }

    public function deletedoc($docname){
        if(!trim($docname)){ return; }
        @unlink(IMPORTS.$docname);
        $d=pathinfo($docname);
        $dname=$d['filename'];

        $vignettes=glob(CACHE_VIGNETTES.$dname."*");
        foreach($vignettes as $vignette){
            @unlink($vignette); 
        }
    }

    public function cleanContents(){
        foreach($this->content as $cle=>$valeur){
            if(is_array($valeur)){
                if($valeur["type"]!="separateur"){
                    $this->content[$cle]['value']="";
                }
            } else {
                $this->content[$cle]="";
            }
        }
    }

    public function toObjects($arr){
        if(is_array($arr)){
            return json_decode(json_encode($arr));
        }
    }

    public function set($tag, $value){
        if(isset($this->content[$tag])){
            $this->content[$tag]['value']=$value;
        }
    }

    public function get($tag,$default="",$before="",$after=""){
        $return="";
        if(isset($this->content[$tag])){
            if(is_array($this->content[$tag])){
                $return=$this->content[$tag]["value"];
            } else {
                $return=$this->content[$tag];
            }
        } 
        if($return!=""){
            return $return;
        } else {
            return $default; 
        }
    }

    public function getAsarray($tag){
        if(!$this->get($tag)){
            return array();
        }
        if(is_array($this->get($tag))){
            return $this->get($tag);
        } else {
            return array($this->get($tag));
        }
    }

    public function getTextPart($tag,$limit=300){
        if(isset($this->content[$tag])){
            if($this->getType($tag)=="text" || $this->getType($tag)=="textarea"){
                $txt=$this->get($tag);
                $txt=strip_tags($txt);
                if(strlen(strip_tags($txt)) > $limit ){
                    return substr($txt,0,strpos($txt,' ',$limit)).'...';
                   // return substr($txt, 0, $limit)."...";
                }
                return $txt;
            }

            if(is_array($this->content[$tag])){
                $return=$this->content[$tag]["value"];
            } else {
                $return=$this->content[$tag];
            }
        } 
    }

    // transforme le contenu du tag en liste de array(file, description)
    public function getFilearray($tag){
        // vide ou n'est pas un champ file
        if(!$this->get($tag) || $this->getParam($tag,"type")!="file"){
            return array();
        }        
        $files=$this->get($tag);
        // old school, juste une string
        if(!is_array($files)){
            return array(array(
                "file"=>$files,
                "description" => ""
            )
                        );
        } 
        // contenu unique
        if(isset($files['file'])){
            return array($files);
        } 
        // multi : voir si on a la bonne structure
        for($i=0; $i<count($files);$i++){
            if(is_string($files[$i])){
                $file=$files[$i];
                $files[$i]=array(
                    "file"=>$file,
                    "description" => ""
                );
            }
        }
        return $files;
    }

    public function getFile($tag,$num=0){
        $files=$this->getFilearray($tag);
        if(!count($files)){ return ""; }

        if(isset($files[$num])){
            return $files[$num]['file'];
        } 
        return "";
    }

    public function getDescription($tag,$num=0){
        $files=$this->getFilearray($tag);
        if(!count($files)){ return ""; }

        if(isset($files[$num])){
            return $files[$num]['description'];
        } 
        return "";
    }

    public function getParam($tag,$param,$default=""){
        if(isset($this->content[$tag][$param])){
            return $this->content[$tag][$param];
        } else {
            return $default;
        }
    }

    public function getOptions($tag,$separateur=false){
        $info=$this->get($tag);
        // attention, peut être multiple
        if(is_array($info)){
            for($i=0;$i<count($info);$i++){
                if(isset($this->content[$tag]['options'][$info[$i]])){
                    $info[$i]=$this->content[$tag]['options'][$info[$i]];
                }
            }

        } else {
            if(isset($this->content[$tag]['options'][$info])){
                $info= $this->content[$tag]['options'][$info];
            } else {
                $info = $tag;
            }
        }

        if($separateur){
            if(is_array($info)){
                return implode($separateur, $info);
            }
        }
        return $info;
    }

    public function getHtml($tag, $default=""){
        $r=$this->get($tag,$default);
        return nl2br($r);
    }

    public function GetmultipleOrSingle($tag, $default=""){
        if(!$this->isTagmultiple($tag)){ return $this->get($tag, $default); }
        if(is_array($this->get($tag))){ 
            $separator=$this->getParam($tag,"separator",", ");
            return implode($separator, $this->get($tag,""));
        }
        return $this->get($tag);
    }

    public function boolToFrench($t){
        switch($t){
            case "true":
                return "Oui";
                break;
            case "false":
                return "Non";
                break;
            default:
                return $t;
        }
    }

    public function bool($tag){
        if(!$this->get($tag,false)){
            return false; 
        }
        switch($this->get($tag)){
            case "true":
                return true;
                break;
            case "false":
                return false;
                break;
            default:
                return false;
        }
    }

    // nettoyer avant affichage
    // si c'est un multiple, montrer avec son séparateur
    // si c'est un bollean, afficher oui ou non
    public function getPropre($tag, $default=""){
        $valeurs=$this->get($tag,false);
        if(!$valeurs){
            return $default;
        }
        // transformer tout en array
        if(!is_array($valeurs)){
            $valeurs=array($valeurs);
        }
        // passer en revue les valeurs
        for($i=0;$i<count($valeurs);$i++){
            $t=$valeurs[$i];

            // si il y a des options ou un array
            if($options = $this->getOptionarray($tag)){

                if(isset($options[$t])){
                    $t=$options[$t];
                }
            }

            $t=$this->boolToFrench($t);
            $valeurs[$i]=$t;
        }
        $separator=$this->getParam($tag,"separator",", ");
        return implode($separator, $valeurs);
    } 

    // renvoie un array contenant les options d'un tag
    public function getOptionarray($tag){
        if(isset($this->content[$tag]["optionsarray"])){
            $ar=$this->content[$tag]["optionsarray"];
            return LISTES[$ar];
        } else if($this->hasOptions($tag)){
            return $this->content[$tag]['options'];
        } 
        return false;
    }

    public function hasValue($tag){
        if(empty($this->content[$tag]["value"])){
            return false;
        } else {
            return true;
        }
    }

    public function hasOptions($tag){
        if(isset($this->content[$tag]["options"])){
            return true;
        } else {
            return false;
        }
    }

    private function nameFile(){
        $this->filename=date("YmdHis")."_".$this->nettoie_lien($this->get('titre'))."_".uniqid().".yaml";
    }

    private function stripAccents($string){
        $string = strtolower(str_replace(array(" ","(",")","'"),array("_","","","_"),$string));
        $string= \Transliterator::create('NFD; [:Nonspacing Mark:] Remove; NFC')
            ->transliterate($string);
        return $string;
    }

    // transforme un lien en un truc uniquement constitué de lettres
    public function nettoie_lien($texte){
        $texte = preg_replace("#[^a-zA-Z]#", "", $texte);
        $texte = strtolower(str_replace(array(" ","(",")","'"),array("_","","","_"),$texte));
        return $texte;
    }

}