#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""SafeEnvironment implementation for use of exec"""

import os
import cStringIO

import PythonDefaults
import PythonFile

class SafeEnvironment(dict):
    """Environnement isolé dans lequel on exécute un script"""

    def __init__(self, additionnal=None, parent=None):
        # Création de l'environment initial
        super(self.__class__, self).__init__({
            # Écrit: variable keysep tostring(value)
            "defvar": self.defvar,
            # La convertion en chaîne de charactère
            "tostring": self.tostring,
            # Définition des convertions
            "conv": {
                bool: {
                    True: "yes",
                    False: "no",
                },
                list: lambda l: ", ".join([
                    str(x)
                    for x in l
                ]),
                tuple: lambda l: ", ".join([
                    str(x)
                    for x in l
                ]),
            },
            # Fonction de base pour imprimer quelque chose
            "out": self.out,
            "_out": self._out,
            # Le séparateur pour la forme: variable keysep valeur
            "keysep": "=",
            # Le charactère de commentaire
            "comment_start": "#",
            # Du mapping de certaines fonctions
            "include": self.include,
            # Du mapping de certaines fonctions
            "dump": self.dump,
            # Infos standard pour le fichier (écrasable localement)
            "info": {
                'owner': PythonDefaults.DEFAULT_USER,
                'group': PythonDefaults.DEFAULT_GROUP,
                'mode': PythonDefaults.DEFAULT_ACLS,
            }
        })

        if additionnal is None:
            additionnal = {}
        super(self.__class__, self).update(additionnal)

        # On crée le flux dans lequel le fichier de config sera généré
        self.stream = cStringIO.StringIO()

        # Le Pythonfile parent est référencé ici
        self.parent = parent

        # Les trucs inclus
        self.included = []

    def __setitem__(self, variable, value):
        """Lorsqu'on définit une variable, si elle est listée dans la variable
        exports, on l'incorpore dans le fichier produit"""
        super(self.__class__, self).__setitem__(variable, value)

    def defvar(self, variable, value):
        """Quand on fait un export, on utilise defvar pour incorporer la variable
        et sa valeur dans le fichier produit"""
        # On écrit mavariable = toto, en appliquant une éventuelle conversion à toto
        self.out("%s%s%s" % (variable, self['keysep'], self.tostring(value)))

    def out(self, string=""):
        """C'est le print local. Sauf qu'on écrit dans self.stream"""
        self._out("%s\n" % (string,))

    def _out(self, string=""):
        """C'est le print local sans retour à la ligne."""
        self.stream.write(string)

    def tostring(self, value):
        """On convertit un objet python dans un format "string" sympa.
        En vrai c'est horrible et il faudrait virer ce genre de kludge."""
        convertor = self["conv"].get(type(value))
        if convertor:
            if type(convertor) == dict:
                return convertor[value]
            else:
                return convertor(value)
        else:
            return str(value)

    def dump(self, incfile):
        """On exécute le fichier python dans l'environnement courant

        incfile est le nom du fichier, sans le .py"""
        filename = os.path.join(self.parent.parent.include, "%s.py" % (incfile,))
        python_file = PythonFile.PythonFile(filename, self.parent.parent)
        python_file.run(environment=self)

    def include(self, incfile):
        """Pareil qu'au dessus, mais on ne le fait que si ça n'a pas
        été fait"""
        if incfile in self.included:
            return
        self.included.append(incfile)
        self.dump(incfile)
