#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script pour la génération et la modification de plugins pour Jeedom.
"""

import json
import os
import re
import sys

##########
# Config #
##########
config = {
    "plugin_template_repo": "https://github.com/NextDom/plugin-ExtraTemplate"
                            ".git",
    "default_package_name": "Exemple",
    "default_changelog_url": "https://nextdom.github.io/plugin-%s/#language"
                             "#/changelog",
    "default_documentation_url":
        "https://nextdom.github.io/plugin-%s/#language#/",
    "jeedom_categories": [
        "security",
        "automation protocol",
        "programming",
        "organization",
        "weather",
        "communication",
        "devicecommunication",
        "multimedia",
        "wellness",
        "monitoring",
        "health",
        "nature",
        "automatisation",
        "energy"
    ]
}

#################
# Templates PHP #
#################
php_header = """<?php

/* This file is part of Jeedom.
 *
 * Jeedom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jeedom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Jeedom. If not, see <http://www.gnu.org/licenses/>.
 */

"""

feature_ajax = """
try {
    require_once dirname(__FILE__) . '/../../../../core/php/core.inc.php';
    include_file('core', 'authentification', 'php');

    if (!isConnect('admin')) {
        throw new \\Exception(__('401 - Accès non autorisé', __FILE__));
    }

    ajax::init();

    throw new \\Exception(__('Aucune méthode correspondante à : ', __FILE__) . 
    init('action'));
    /*     * *********Catch exeption*************** */
} catch (\\Exception $e) {
    ajax::error(displayException($e), $e->getCode());
}
"""

feature_cmd_class = """
require_once dirname(__FILE__) . '/../../../../core/php/core.inc.php';

class PluginNameCmd extends cmd
{
    public function execute($_options = array())
    {

    }
}
"""

feature_core_class = """
require_once dirname(__FILE__) . '/../../../../core/php/core.inc.php';

class PluginName extends eqLogic
{

}
"""

wizard_configuration = """
require_once dirname(__FILE__) . '/../../../core/php/core.inc.php';
include_file('core', 'authentification', 'php');
if (!isConnect()) {
    include_file('desktop', '404', 'php');
    die();
}
?>
"""

wizard_core_class = """
/* * ***************************Includes********************************* */
require_once dirname(__FILE__) . '/../../../../core/php/core.inc.php';
require_once 'PluginNameCmd.class.php';

class PluginName extends eqLogic
{
    /*     * *************************Attributs****************************** */



    /*     * ***********************Methode static*************************** */

    /*
     * Fonction exécutée automatiquement toutes les minutes par Jeedom
      public static function cron() {

      }
     */


    /*
     * Fonction exécutée automatiquement toutes les heures par Jeedom
      public static function cronHourly() {

      }
     */

    /*
     * Fonction exécutée automatiquement tous les jours par Jeedom
      public static function cronDaily() {

      }
     */



    /*     * *********************Méthodes 
    d'instance************************* */

    public function preInsert()
    {
        
    }

    public function postInsert()
    {
        
    }

    public function preSave()
    {
        
    }

    public function postSave()
    {
        
    }

    public function preUpdate()
    {
        
    }

    public function postUpdate()
    {
        
    }

    public function preRemove()
    {
        
    }

    public function postRemove()
    {
        
    }

    /*
     * Non obligatoire mais permet de modifier l'affichage du widget si vous 
     en avez besoin
      public function toHtml($_version = 'dashboard') {

      }
     */

    /*
     * Non obligatoire mais ca permet de déclencher une action après 
     modification de variable de configuration
      public static function postConfig_<Variable>() {
      }
     */

    /*
     * Non obligatoire mais ca permet de déclencher une action avant 
     modification de variable de configuration
      public static function preConfig_<Variable>() {
      }
     */

    /*     * **********************Getteur Setteur*************************** */
}
"""

wizard_core_cmd_class = """
/* * ***************************Includes********************************* */
require_once dirname(__FILE__) . '/../../../../core/php/core.inc.php';

class PluginNameCmd extends cmd
{
    /*     * *************************Attributs****************************** */
    /*     * ***********************Methode static*************************** */
    /*     * *********************Methode d'instance************************* */
    /*
     * Non obligatoire permet de demander de ne pas supprimer les commandes 
     même si elles ne sont pas dans la nouvelle configuration de l'équipement 
     envoyé en JS
      public function dontRemoveCmd() {
      return true;
      }
     */

    public function execute($_options = array())
    {
        
    }

    /*     * **********************Getteur Setteur*************************** */
}
"""

wizard_desktop_php = """
if (!isConnect('admin')) {
    throw new Exception('{{401 - Accès non autorisé}}');
}
"""

wizard_install = """
require_once dirname(__FILE__) . '/../../../core/php/core.inc.php';

function PluginName_install() {
    
}

function PluginName_update() {
    
}


function PluginName_remove() {
    
}
"""


#######################
# Classes utilitaires #
#######################
class File(object):
    """
    Librairie pour la gestion des fichiers
    """

    @staticmethod
    def sed_replace(regexp, replacement, target_file):
        """Exécute la commande sed sur un fichier
        :params regexp:      Expression régulière
        :params replacement: Chaîne de remplacement
        :params target_file: Fichier cible
        :type regexp:        str
        :type replacement:   str
        :type target_file:   str
        """
        sed_replace_pattern = "sed -i'' 's/{}/{}/g' {} 2> /dev/null"
        if 'darwin' in sys.platform: # pragma: no cover
            sed_replace_pattern = "sed -i '' 's/{}/{}/g' {} 2> /dev/null"

        os.system(sed_replace_pattern.format(regexp, replacement, target_file))

    @staticmethod
    def replace_in_file(target_file, old_name, new_name):
        """Remplace l'ancien nom par le nouveau
        :param target_file: Fichier à traiter
        :param old_name:    Ancien nom du plugin
        :param new_name:    Nouveau nom du plugin
        :type target_file:  str
        :type old_name:     str
        :type new_name:     str
        """
        File.sed_replace(old_name, new_name, target_file)
        File.sed_replace(old_name[0].lower() + old_name[1:], new_name[
            0].lower() + new_name[1:], target_file)
        File.sed_replace(old_name.lower(), new_name.lower(), target_file)
        File.sed_replace(old_name.upper(), new_name.upper(), target_file)
        File.sed_replace(old_name.capitalize(),
                         new_name.capitalize(),
                         target_file)

    @staticmethod
    def create_php_file(content, dest_file):
        """
        Créé un fichier PHP et son en-tête puis ajoute le contenu.
        :param content:   Contenu PHP
        :param dest_file: Fichier destination
        """
        with open(dest_file, 'w') as dest:
            dest.write(php_header)
            dest.write(content)

    @staticmethod
    def create_php_file_and_replace(content, dest_file, old_name, new_name):
        """
        Créé un fichier PHP et son en-tête, ajoute le contenu, puis remplace
        les valeurs nécessaires.
        :param content:   Contenu PHP
        :param dest_file: Fichier destination
        :param old_name:  Ancien nom du plugin
        :param new_name:  Nouveau nom du plugin
        """
        File.create_php_file(content, dest_file)
        File.replace_in_file(dest_file, old_name, new_name)

    @staticmethod
    def is_content_in_file(file_path, content):
        """Test si un fichier contient une chaine de caractères
        :param file_path: Chemin du fichier
        :param content:   Contenu à tester
        :type file_path:  str
        :type content:    str
        :return:          True si le contenu a été trouvé
        :rtype:           bool
        """
        result = False
        try:
            with open(file_path, 'r') as file_content:
                if content in file_content.read():
                    result = True
        except FileNotFoundError:
            pass
        return result

    @staticmethod
    def add_line_under(path_file, needle, line_to_add):
        """
        Ajoute une ligne après que le champ needle est été trouvé
        :param path_file:   Chemin du fichier à traiter
        :param needle:      Elément à rechercher
        :param line_to_add: Contenu de la ligne à ajouter
        :return:
        """
        result = False
        lines = []
        with open(path_file, 'r') as core_file_content:
            lines = core_file_content.readlines()
        output = []
        for line in lines:
            output.append(line)
            if needle in line and not result:
                output.append(line_to_add + '\n')
                result = True
        with open(path_file, 'w') as core_file_content:
            for line in output:
                core_file_content.write(line)
        return result

    @staticmethod
    def write_json_file(file_path, json_data):
        """
        Ecrit le fichier au format JSON
        :param file_path: Chemin du fichier
        :param json_data: Données à écrire
        :type file_path:  str
        :type json_data:  dict
        :return:          True si l'écriture à réussie
        :rtype:           bool
        """
        result = False
        with open(file_path, 'w') as dest:
            if sys.version_info[0] < 3: # pragma: no cover
                dump = json.dumps(json_data, sort_keys=True, indent=4,
                                  ensure_ascii=False)
                dump = dump.encode('utf-8').decode('string-escape')
            else:
                dump = json.dumps(json_data, sort_keys=True, indent=4)
                dump = dump.encode('utf-8').decode('unicode-escape')
            dest.write(dump + '\n')
            result = True
        return result


class IO(object):
    """
    Librairie pour la gestion des entrées sorties
    """

    # Message afficher pour sortir du menu
    cancel_menu = 'Sortir'
    # Message affiché lors d'un choix
    choice_prompt = 'Choix : '
    # Message affiché lors d'un mauvais choix
    bad_choice = 'Mauvais choix'
    ############################
    # Couleur pour l'affichage #
    ############################
    red_color = '\033[31m'
    yellow_color = '\033[93m'
    green_color = '\033[92m'
    end_color = '\033[0m'

    @staticmethod
    def print_error(msg):
        """Affiche un message d'erreur
        :params msg: Message à afficher
        :type msg:   str
        """
        print(IO.red_color + '/' + IO.yellow_color + '!' + IO.red_color + '\\' +
              IO.end_color + ' ' + msg)

    @staticmethod
    def print_success(msg):
        """Affiche un message de confirmation
        :params msg: Message à afficher
        :type msg:   str
        """
        print(IO.green_color + 'v' + IO.end_color + ' ' + msg)

    @staticmethod
    def is_string(obj):
        """Test si une variable est une chaine de caractères
        :params obj: Objet à tester
        :return:     True si l'object est une chaine de caractères
        :rtype:      bool
        """
        str_type = str

        if sys.version_info[0] < 3: # pragma: no cover
            str_type = basestring  # pylint: disable=undefined-variable
        return isinstance(obj, str_type)

    @staticmethod
    def get_user_input(msg):
        """Obtenir une entrée d'un utilisateur
        Compatible Python 2 et 3
        :params msg: Message à afficher
        :type msg:   str
        :return:     Entrée de l'utilisateur
        :rtype:      str
        """
        result = None
        if sys.version_info[0] < 3: # pragma: no cover
            result = raw_input(msg)  # pylint: disable=undefined-variable
        else:
            result = input(msg)
        return result

    @staticmethod
    def show_menu(menu, title=None, show_cancel=True):
        """Afficher un menu
        :params menu:        Tableau des choix à afficher
        :params show_cancel: Affiche un message pour sortir du menu
        :params title:       Titre du menu
        :type menu:          array
        :type show_cancel:   bool
        :type title:         str
        """
        print('')
        if title is not None:
            print('-=| ' + title + ' |=-\n')
        for index, menu_item in enumerate(menu):
            print('  ' + str(index + 1) + '. ' + menu_item)
        if show_cancel:
            print('  0. ' + IO.cancel_menu)

    @staticmethod
    def get_menu_choice(menu, title=None, show_cancel=True):
        """Demande à l'utilisateur de faire un choix dans un menu
        :params menu:        Tableau des choix à afficher
        :params show_cancel: Affiche le 0 pour sortir
        :params title:       Titre du menu
        :type menu:          List[str]
        :type show_cancel:   bool
        :type title:         str
        :return:             Choix de l'utilisateur ou -1
        :rtype:              int
        """
        loop = True
        user_choice = 9999
        menu_choice_length = len(menu)

        while loop:
            IO.show_menu(menu, title, show_cancel)
            try:
                raw_user_choice = IO.get_user_input(IO.choice_prompt)
                user_choice = int(raw_user_choice)
            except NameError:
                user_choice = 9999
            except ValueError:
                user_choice = 9999
                # Sortir si l'utilisateur appuie sur Enter
                if show_cancel:
                    if IO.is_string(raw_user_choice) and \
                            raw_user_choice == "":
                        user_choice = 0
            if user_choice < menu_choice_length + 1:
                # Choix de l'utilisateur -1 pour retrouver l'index du tableau
                # et -1 si l'utilisateur a choisi 0 (Sortir)
                user_choice -= 1
                loop = False
                # Si l'utilisateur doit répondre, la boucle continue
                if user_choice == -1 and not show_cancel:
                    loop = True
            else:
                IO.print_error(IO.bad_choice)
        return user_choice

    @staticmethod
    def ask_y_n(question, default='o'):
        """Afficher une question dont la réponse est oui ou non
        :param question: Question à afficher
        :param default:  Réponse par défaut. o par défaut
        :type question:  str
        :type default:   str
        :return:         Réponse de l'utilisateur
        :rtype:          str
        """
        choices = 'O/n'
        if default != 'o':
            choices = 'o/N'
        choice = IO.get_user_input(
            '%s [%s] : ' % (question, choices)).lower()
        if choice == default or choice == '':
            return default
        return choice

    @staticmethod
    def ask_with_default(question, default):
        """Affiche une question avec une réponse par défaut
        :param question: Question à afficher
        :param default:  Réponse par défaut
        :type question:  str
        :type default:   str
        :return:         Réponse de l'utilisateur
        :rtype:          str
        """
        answer = IO.get_user_input('%s [%s] : ' % (question, default))
        if answer == '':
            answer = default
        return answer


class Jeedom(object):
    """
    Librairie pour la gestion des informations spécifiques à Jeedom
    """

    @staticmethod
    def ask_for_i18n_folder_creation(i18n_path):
        """
        Demande pour ajouter le répertoire de traduction
        :param i18n_path: Chemin du plugin
        :type i18n_path:  str
        """
        if not os.path.exists(i18n_path):
            answer = IO.ask_y_n('Voulez-vous créer le répertoire core/i18n ?')
            if answer == 'o':
                os.mkdir(i18n_path)

    @staticmethod
    def add_language(plugin_path):
        """
        Ajout un language
        :param plugin_path: Chemin du plugin
        :type plugin_path:  str
        :return:            True si la traduction a été rajoutée
        :rtype:             bool
        """
        i18n_path = Jeedom.get_i18n_path(plugin_path)
        i18n_list = filter(lambda item: not item.startswith('.'),
                           os.listdir(i18n_path))
        if i18n_list:
            print('Liste des traductions présentes : ')
            for i18n in i18n_list:
                print(' - ' + i18n)
        loop = True
        language = ''
        while loop:
            language = IO.get_user_input('Nom de la traduction : ')
            if language == '':
                loop = False
            elif Jeedom.is_valid_i18n_name(language):
                if language + '.json' in os.listdir(i18n_path):
                    IO.print_error(language + ' existe déjà.')
                else:
                    loop = False
            else:
                IO.print_error('La langue doit être au format fr_FR.')
        if language != '':
            scan_data = Jeedom.scan_for_strings(plugin_path)
            json_data = Jeedom.merge_i18n_json(plugin_path, {}, scan_data)
            File.write_json_file(i18n_path + os.sep + language + '.json',
                                 json_data)
            IO.print_success('La langue ' + language + ' a été ajoutée')

    @staticmethod
    def get_i18n_path(plugin_path):
        """
        Renvoie le chemin du répertoire contenant les traductions du plugin
        :return: Chemin vers le répertoire des traductions
        :rtype:  str
        """
        return os.path.join(plugin_path, 'core', 'i18n')

    @staticmethod
    def merge_i18n_json(plugin_path, base_json, scan_data):
        """
        Fusionne les anciennes données avec les nouvelles
        :param plugin_path: Chemin du plugin
        :param base_json:   Données présentes
        :param scan_data:   Données scannées
        :type plugin_path:  str
        :type base_json:    dict
        :type scan_data:    List(dict)
        :return:            Données fusionnées
        :rtype:             dict
        """
        for data in scan_data:
            file_path = Jeedom.transform_path_to_i18n_path(plugin_path,
                                                           data['file_path'])
            # Décode l'unicode si besoin
            if not isinstance(file_path, str): # pragma: no cover
                file_path = file_path.encode('ascii')

            # Création du dictionnaire vide
            if file_path not in base_json.keys():
                base_json[file_path] = {}

            # Ajoute les éléments
            for item in data['items']:
                if item not in base_json[file_path].keys():
                    base_json[file_path][item] = item
            # Renomme la clé pour Jeedom
            base_json[file_path] = base_json.pop(file_path)
        return base_json

    @staticmethod
    def transform_path_to_i18n_path(plugin_path, file_path):
        """
        Transforme le chemin pour qu'il soit compatible avec Jeedom
        :param plugin_path: Chemin du plugin
        :param file_path:   Chemin du fichier
        :type plugin_path:  str
        :type file_path:    str
        :return:     Chemin converti
        :rtype:      str
        """
        file_path_striped = file_path.replace(plugin_path, '')
        normal_path = 'plugins' + os.sep + os.path.basename(plugin_path) + \
                      os.sep + file_path_striped
        # En fonction du path fournit, il peut y avoir des doublons
        normal_path = normal_path.replace('//', '/')
        return normal_path  # .replace('/', '\/')

    @staticmethod
    def scan_for_strings(path, result=None):
        """
        Parcourt un répertoire à la recherche de chaines à traduire
        :param path:   Chemin du répertoire à parcourir
        :param result: Résultat compléter par recursion
        :type path:    str
        :type result:  list
        :return:       Liste des chaines à traduire
        :rtype:        list
        """
        if result is None:
            result = []
        for item in os.listdir(path):
            item_path = path + os.sep + item
            if os.path.isdir(item_path):
                Jeedom.scan_for_strings(item_path, result)
            else:
                if item.endswith('php'):
                    content = Jeedom.scan_file_for_strings(item_path)
                    if content:
                        result.append({
                            'file_path': item_path,
                            'items': list(set(content))
                        })
        return result

    @staticmethod
    def scan_file_for_strings(file_path):
        """
        Parcourt les fichiers à la recherche de chaînes à traduire
        :param file_path: Fichier à parcourir
        :type file_path:  str
        :return:          Liste des chaines à traduire
        :rtype:           list
        """
        result = []
        with open(file_path, 'r') as file_content:
            readed_content = file_content.read()
            # noinspection RegExpRedundantEscape
            result.extend(re.findall('\\{\\{(.*?)\\}\\}', readed_content))
            result.extend(re.findall('__\\(\'(.*?)\'', readed_content))
        result.extend(re.findall('__\\("(.*?)"', readed_content))
        return result

    @staticmethod
    def is_valid_i18n_name(name):
        """
        Test si la langue est au bon format
        :param name: Nom à tester
        :type name:  str
        :return:     True si le format est correct
        :rtype:      bool
        """
        result = False
        re_search = re.search('^[a-z]{2}_[A-Z]{2}$', name)
        if re_search is not None:
            result = True
        return result


class MethodData:
    """
    Classe contenant les informations d'une méthode
    """
    class_file_path = ''
    class_name = ''
    method_name = ''
    method_visibility = 'public'
    method_is_static = False
    method_comment = ''
    method_params = ''

    def get_method_declaration(self):
        """Obtenir la déclaration de la classe dans le fichier
        """
        return 'class ' + self.class_name + ' '

    def get_method_func(self):
        """Obtenir la déclaration de la méthode données
        """
        output = '\n'
        if self.method_comment != '':
            output += '    /**\n     * ' + self.method_comment + '\n     */\n'
        output += '    ' + self.method_visibility + ' '
        if self.method_is_static:
            output += 'static '
        output += 'function ' + self.method_name + '('
        if self.method_params != '':
            output += self.method_params
        output += ')\n    {\n\n    }\n'
        return output


class PHPFile(object):
    """
    Librairie pour la gestion des fichiers PHP
    """

    @staticmethod
    def add_method(method_data):
        """Ajoute la méthode à la classe
        :params method_data: Données de la méthode
        :type method_data:   MethodData
        """
        result = False
        if os.path.exists(method_data.class_file_path):
            if PHPFile.check_class(method_data.class_file_path,
                                   method_data.class_name):
                if not PHPFile.check_if_method_exists(
                        method_data.class_file_path,
                        method_data.method_name):
                    result = PHPFile.write_method_in_class(method_data)
                else:
                    IO.print_error('La méthode existe déjà')
            else:
                IO.print_error('La classe n\'existe pas')
        else:
            IO.print_error('Le fichier global n\'existe pas')
        return result

    @staticmethod
    def check_class(class_file_path, class_name):
        """Test si la classe existe
        :params class_file_path: Répertoire de la classe
        :params class_name:      Nom de la classe
        :type class_file_path:   str
        :type class_name:        str
        :return:                 True si la classe existe
        :rtype:                  bool
        """
        result = False
        try:
            with open(class_file_path) as file_content:
                if class_name in file_content.read():
                    result = True
        except FileNotFoundError:
            pass
        return result

    @staticmethod
    def check_if_method_exists(class_file_path, method_name):
        """Test si la classe existe
        :params class_file_path: Répertoire de la classe
        :params method_name:     Nom de la méthode
        :type class_file_path:   str
        :type method_name:       str
        :return:                 True si la méthode existe
        :rtype:                  bool
        """
        result = False
        with open(class_file_path) as file_content:
            if method_name + '()' in file_content.read():
                result = True
        return result

    @staticmethod
    def check_and_write_class(file_path, class_name, extends=None):
        """Lancer l'écriture d'une nouvelle classe
        :params file_path:  Chemin du fichier devant contenir la classe
        :params class_name: Nom de la classe à créer
        :params extends:    Nom de la classe héritée
        :type file_path:    str
        :type class_name:   str
        :type extends:      str
        """
        if PHPFile.write_class(file_path, class_name, extends):
            IO.print_success('La classe ' + class_name + ' a été créée')
        else:
            IO.print_success('La classe ' + class_name +
                             ' n\'a pas pu être créée')

    @staticmethod
    def write_class(file_path, class_name, extends=None):
        """Ajoute une classe à un fichier PHP
        :params file_path:  Chemin du fichier devant contenir la classe
        :params class_name: Nom de la classe à créer
        :params extends:    Nom de la classe héritée
        :type file_path:    str
        :type class_name:   str
        :type extends:      str
        """
        result = False
        add_php_tag = False
        if not os.path.exists(file_path):
            add_php_tag = True
        with open(file_path, 'a') as php_file:
            if add_php_tag:
                php_file.write('<?php\n')
            class_declaration = '\n\nclass ' + class_name
            if extends is not None:
                class_declaration += ' extends ' + extends + '\n{\n\n}\n'
            php_file.write(class_declaration)
            result = True
        return result

    @staticmethod
    def write_method_in_class(method_data):
        """Ecrit la méthode à la classe
        :params method_data: Données de la méthode
        :type method_data:   MethodData
        """
        output = []
        bracket_count = 0
        start_bracket_count = False
        method_added = False
        class_declaration = 'class ' + method_data.class_name
        try:
            content = None
            with open(method_data.class_file_path,
                      'r') as class_file_content:
                content = class_file_content.readlines()
            # Recherche de la dernière accolade de la classe
            for line in content:
                if not start_bracket_count and (
                        class_declaration in line or
                        class_declaration == line):
                    start_bracket_count = True
                if not method_added and start_bracket_count:
                    if '{' in line:
                        bracket_count += 1
                    if '}' in line:
                        bracket_count -= 1
                        # Dernière accolade de la classe
                        if bracket_count == 0:
                            output.append(method_data.get_method_func())
                            method_added = True
                output.append(line)
            # Réécrit le fichier
            with open(method_data.class_file_path,
                      'w') as class_file_content:
                for line in output:
                    class_file_content.write(line)
        except FileNotFoundError:
            pass
        return method_added


class Tools(object):
    """
    Classe facilitant l'initialisation de l'outil
    """

    @staticmethod
    def show_help():
        """Affiche l'aide
        """
        print(sys.argv[0] + ' [PLUGIN-NAME] [--help]')
        print('  --help :      Affiche de menu.')
        print('  PLUGIN-NAME : Indiquer le nom du plugin à modifier.')

    @staticmethod
    def parse_args(argv):
        """Analyse les arguments
        :params argv: Arguments
        :type argv:   list
        :return:      Liste ou None si le programme doit quitter
        """
        result = ''
        if '--help' in argv or len(argv) > 2:
            Tools.show_help()
            result = None
        elif len(argv) > 1:
            result = argv[1]

        return result

    @staticmethod
    def is_plugin_dir(path):
        """Test si le répertoire contient un plugin
        :param path: Chemin à tester
        :return:     True si c'est un plugin
        """
        info_path = os.path.join(path, 'plugin_info', 'info.json')
        return os.path.exists(info_path)

    @staticmethod
    def get_plugin_data(path):
        """Lire les informations du plugin
        :param path: Chemin du plugin
        :return:     Informations du plugin
        :rtype:      dict
        """
        result = None
        info_path = os.path.join(path, 'plugin_info', 'info.json')
        try:
            with open(info_path) as info_json:
                info_json_data = json.load(info_json)
                if 'id' in info_json_data.keys():
                    result = [path, info_json_data['id']]
        except (json.decoder.JSONDecodeError, FileNotFoundError):
            pass
        return result

    @staticmethod
    def get_plugins_in_dir(path):
        """Obtenir la liste des plugins dans un répertoire
        :param path: Répertoire parent
        :return:     Liste des plugins
        :rtype:      list
        """
        result = []
        abspath = os.path.abspath(path)
        for item in os.listdir(abspath):
            item_path = abspath + os.sep + item
            if os.path.isdir(item_path):
                if Tools.is_plugin_dir(item_path):
                    plugin = Tools.get_plugin_data(item_path)
                    if plugin is not None:
                        result.append(plugin)
        return result


#####################
# Classes des menus #
#####################
class BaseMenu(object):
    """Classe mère des menus
    Fournit les méthodes nécessaire à l'affichage des menus et des actions
    courantes.
    """
    title = None
    menu = []
    bad_command = 'Mauvaise commande'

    def start(self):
        """Démarre l'affichage du menu
        """
        loop = True
        while loop:
            user_choice = IO.get_menu_choice(self.menu, self.title)
            if user_choice == -1:
                loop = False
            else:
                self.launch(user_choice + 1)

    def launch(self, number):
        """Lance une action
        :params number: Numéro de l'action à lancer
        :type number:   int
        :return:        Résultat de l'action
        :rtype:         bool
        """
        return_value = False
        method_name = 'action_' + str(number)
        # DEBUG
        # method = getattr(self, method_name)
        # return_value = method()
        try:
            method = getattr(self, method_name)
            return_value = method()
        except AttributeError:
            IO.print_error(self.bad_command)
        return return_value


class FeaturesMenu(BaseMenu):
    """Classe du menu permettant d'ajouter des fonctionnalités.
    """
    title = 'Ajouter des fonctionnalités'
    menu = ['Ajouter la classe générale',
            'Ajouter la classe des commandes',
            'Ajouter une méthode cron',
            'Ajouter la réponse aux requêtes Ajax']
    plugin_name = ''
    plugin_path = ''

    def __init__(self, plugin_path, plugin_name):
        """Constructeur
        :param plugin_path: Répertoire du plugin
        :param plugin_name: Nom du plugin
        :type plugin_path:  str
        :type plugin_name:  str
        """
        if sys.version_info[0] < 3: # pragma: no cover
            super(FeaturesMenu, self).__init__()
        else:
            super().__init__()
        self.plugin_name = plugin_name
        self.plugin_path = plugin_path

    def action_1(self):
        """Créer la classe principale
        """
        self.add_core_class()

    def action_2(self):
        """Créer la classe de gestion des commandes
        """
        self.add_cmd_class()

    def action_3(self):
        """Ajouter une tâche cron au plugin
        """
        self.add_cron()

    def action_4(self):
        """Créer la classe de gestion des requêtes Ajax
        """
        self.add_ajax()

    def add_core_class(self):
        """
        Ajoute la classe core d'un plugin
        """
        target_file = os.path.join(self.plugin_path, 'core', 'class',
                                   self.plugin_name + '.class.php')
        if os.path.exists(target_file):
            IO.print_error('Le fichier existe déjà')
        else:
            File.create_php_file_and_replace(feature_core_class, target_file,
                                             'PluginName',
                                             self.plugin_name)
            IO.print_success('Le fichier a été créé.')

    def add_cmd_class(self):
        """
        Ajoute la classe cmd d'un plugin
        """
        target_core_file = os.path.join(self.plugin_path, 'core', 'class',
                                        self.plugin_name + '.class.php')
        target_cmd_file = os.path.join(self.plugin_path, 'core', 'class',
                                       self.plugin_name + 'Cmd.class.php')
        if self.is_core_class_exists(target_core_file):
            separated = IO.ask_y_n('Utiliser des fichiers séparés ?')
            if separated == 'o':
                self.insert_require_in_core(target_core_file)
                File.create_php_file_and_replace(feature_cmd_class,
                                                 target_cmd_file,
                                                 'PluginName', self.plugin_name)
            else:
                PHPFile.write_class(target_core_file, self.plugin_name + 'Cmd',
                                    'cmd')

                method_data = MethodData()
                method_data.class_file_path = target_core_file
                method_data.class_name = self.plugin_name + 'Cmd'
                method_data.method_name = 'execute'
                method_data.method_params = '$_options = array()'
                method_data.method_visibility = 'public'
                PHPFile.write_method_in_class(method_data)

    def is_core_class_exists(self, core_file):
        """
        Test si le fichier core du plugin existe.
        :param core_file:   Chemin du fichier core
        :type core_file:    str
        :return:            True si le fichier de la classe a été créée
        :rtype:             bool
        """
        result = False
        if os.path.exists(core_file):
            if PHPFile.check_class(core_file, self.plugin_name):
                result = True
        else:
            create = IO.ask_y_n(
                'Le fichier de la classe principale n\'existe pas, '
                'voulez-vous le créer ?')
            if create == 'o':
                self.add_core_class()
                result = True
        return result

    def insert_require_in_core(self, core_file):
        """
        Ajoute l'inclusion du fichier
        :param core_file:   Chemin du fichier core
        :type core_file:    str
        """
        File.add_line_under(core_file, 'require_once dirname(__FILE__)',
                            'require_once \'./' + self.plugin_name +
                            'Cmd.class.php\';\n')

    def add_cron(self):
        """
        Ajoute une tâche cron au plugin
        :return:
        """
        core_file_path = os.path.join(self.plugin_path, 'core', 'class',
                                      self.plugin_name +
                                      '.class.php')

        crons_titles = [
            'Toutes les minutes',
            'Toutes les 5 minutes',
            'Toutes les 15 minutes',
            'Toutes les 30 minutes',
            'Toutes les heures',
            'Tous les jours'
        ]
        crons_functions = [
            'cron',
            'cron5',
            'cron15',
            'cron30',
            'cronHourly',
            'cronDaily'
        ]

        choice = IO.get_menu_choice(crons_titles, 'Choix de la récurrence')
        if choice >= 0:
            method_data = MethodData()
            method_data.class_file_path = core_file_path
            method_data.class_name = self.plugin_name
            method_data.method_name = crons_functions[choice]
            method_data.method_is_static = True
            method_data.method_comment = crons_titles[choice]
            if PHPFile.add_method(method_data):
                IO.print_success('La méthode ' + method_data.method_name +
                                 ' a été ajoutée')

    def add_ajax(self):
        """
        Ajoute la classe pour traiter les requêtes AJAX
        """
        ajax_path = os.path.join(self.plugin_path, 'core', 'ajax')
        ajax_file_path = ajax_path + os.sep + self.plugin_name + '.ajax.php'
        if not os.path.exists(ajax_path):
            os.mkdir(ajax_path)
        if os.path.exists(ajax_file_path):
            with open(ajax_file_path) as ajax_content:
                if 'ajax::init' in ajax_content.read():
                    IO.print_error('Le fichier existe déjà')
        else:
            File.create_php_file(feature_ajax, ajax_file_path)
            IO.print_success('Le fichier a été créé')


class I18nMenu(BaseMenu):
    """
    Menu de l'internationalisation
    """
    title = 'Gestion des traductions'
    menu = ['Ajouter une traduction',
            'Mettre à jour les fichiers']
    plugin_name = ''
    plugin_path = ''

    def __init__(self, plugin_path, plugin_name):
        """Constructeur
        :param plugin_path: Répertoire du plugin
        :param plugin_name: Nom du plugin
        :type plugin_path:  str
        :type plugin_name:  str
        """
        if sys.version_info[0] < 3:
            super(I18nMenu, self).__init__()
        else:
            super().__init__()
        self.plugin_name = plugin_name
        self.plugin_path = plugin_path

    def action_1(self):
        """
        Ajout d'un répertoire pour les traductions
        :return: True si une langue a été rajoutée
        :rtype:  bool
        """
        self.add_language()

    def action_2(self):
        """
        Met à jour les traductions
        """
        self.update_languages()

    def add_language(self):
        """
        Ajoute la classe pour traiter les requêtes AJAX
        """
        i18n_path = Jeedom.get_i18n_path(self.plugin_path)
        if not os.path.exists(i18n_path):
            Jeedom.ask_for_i18n_folder_creation(i18n_path)
        if os.path.exists(i18n_path):
            Jeedom.add_language(self.plugin_path)

    def update_languages(self):
        """
        Ajoute la classe pour traiter les requêtes AJAX
        """
        i18n_path = Jeedom.get_i18n_path(self.plugin_path)
        if os.path.exists(i18n_path):
            i18n_list = os.listdir(i18n_path)
            if i18n_list:
                scan_data = Jeedom.scan_for_strings(self.plugin_path)
                for i18n in i18n_list:
                    json_data = {}
                    try:
                        with open(i18n_path + os.sep + i18n) as i18n_content:
                            json_data = json.loads(i18n_content.read())
                    except ValueError:
                        pass
                    json_data = Jeedom.merge_i18n_json(self.plugin_path,
                                                       json_data,
                                                       scan_data)
                    # Json retire le \ avant les / à la lecture
                    parsed_json_data = {}
                    for key in json_data.keys():
                        parsed_json_data[key.replace('/', '\\/')] = json_data[
                            key]
                    File.write_json_file(i18n_path + os.sep + i18n,
                                         parsed_json_data)
            else:
                IO.print_error('Aucune traduction')
        else:
            IO.print_error('Aucun répertoire pour les traductions')


class InfoMenu(BaseMenu):
    """Classe du menu permettant de modifier les informations du plugin.
    """
    title = 'Modifier les informations du plugin'
    menu = ['Modifier le nom affiché dans les menus',
            'Modifier la description',
            'Modifier la licence',
            'Modifier l\'auteur',
            'Modifier la catégorie']
    plugin_name = ''
    plugin_path = ''
    plugin_info_path = ''

    def __init__(self, plugin_path, plugin_name):
        """Constructeur
        :params plugin_name: Nom du plugin
        :type plugin_name:   str
        """
        self.plugin_name = plugin_name
        self.plugin_path = plugin_path
        self.plugin_info_path = os.path.join(plugin_path, 'plugin_info',
                                             'info.json')

    def action_1(self):
        """Modifier le nom affiché dans les menus
        """
        name = IO.get_user_input('Nouveau nom : ')
        self.replace_info_json('name', name)

    def action_2(self):
        """Modifier la description
        """
        description = IO.get_user_input('Nouvelle description : ')
        self.replace_info_json('description', description)

    def action_3(self):
        """Modifier la licence
        """
        licence = IO.get_user_input('Nouvelle licence : ')
        self.replace_info_json('licence', licence)

    def action_4(self):
        """Modifier l'auteur
        """
        author = IO.get_user_input('Nouvel auteur : ')
        self.replace_info_json('author', author)

    def action_5(self):
        """Modifier la catégorie
        """
        category = IO.get_menu_choice(config['jeedom_categories'],
                                      'Choix de la catégorie')
        if category >= 0:
            self.replace_info_json('category',
                                   config['jeedom_categories'][category])

    def replace_info_json(self, key, new_value):
        """Remplace les informations dans le fichier info.json
        :params key:       Clé à modifier
        :params new_value: Nouvelle valeur de la clé
        :type key:         str
        :type new_value:   str
        """
        info_json_path = os.path.join(self.plugin_path, 'plugin_info',
                                      'info.json')
        if os.path.exists(info_json_path):
            File.sed_replace(
                '\\("' + key + '"[ ]\\{0,1\\}: "\\).*\\("\\)',
                '\\1' + new_value + '\\2',
                info_json_path)
            IO.print_success('L\'information a été modifiée.')
        else:
            IO.print_error('Le fichier info.json n\'a pas été trouvé')


class RootMenu(BaseMenu):
    """
    Menu principal de l'outil.
    """
    title = 'Outil de gestion d\'un plugin'
    menu = ['Modifier l\'identifiant du plugin',
            'Modifier les informations du plugin',
            'Ajouter des fonctionnalités',
            'Gestion des traductions']
    plugin_path = ''
    plugin_name = ''

    def __init__(self, plugin_path, plugin_name):
        """Constructeur
        Initialise le chemin vers le fichier qui stocke le nom du plugin.
        :params plugin_name: Nom du plugin
        :params plugin_path: Chemin du plugin
        :type plugin_name:   str
        :type plugin_path:   str
        """
        self.plugin_name = plugin_name
        self.plugin_path = plugin_path

    def action_1(self):
        """Renomme le plugin
        Modifie le nom des répertoires, des fichiers ainsi que le contenu
        des fichiers.
        """
        new_name = IO.get_user_input('Nouveau nom du plugin : ')
        self.rename_plugin(new_name)
        self.plugin_name = new_name
        self.plugin_path = 'plugin-' + new_name

    def action_2(self):
        """Lance le menu de modification des informations
        """
        info_menu = InfoMenu(self.plugin_path, self.plugin_name)
        info_menu.start()

    def action_3(self):
        """Lance le menu de modification des informations
        """
        features_menu = FeaturesMenu(self.plugin_path, self.plugin_name)
        features_menu.start()

    def action_4(self):
        """Lance le menu de gestion des traductions
        """
        i18n_menu = I18nMenu(self.plugin_path, self.plugin_name)
        i18n_menu.start()

    def rename_plugin(self, new_name):
        """Renomme le plugin
        Modifie le nom des répertoires, des fichiers ainsi que le contenu
        des fichiers.
        """
        result = False
        path = os.path.abspath(self.plugin_path)
        new_path = os.path.abspath(path + os.sep + '..' + os.sep + 'plugin-' +
                                   new_name)
        if os.path.exists(path):
            if not os.path.exists(new_path):
                # Renomme le répertoire racine du plugin
                os.rename(path, new_path)
                # Renomme le contenu du plugin
                self.start_rename_plugin(new_path, self.plugin_name, new_name)
                IO.print_success(
                    'Le plugin ' + self.plugin_name + ' a été renommé en ' +
                    new_name)
                result = True
            else:
                IO.print_error('Le répertoire  plugin-' + new_name +
                               ' existe déjà')
                result = False
        else:
            IO.print_error('Le plugin ' + path + ' n\'a pas été trouvé')
            result = False
        return result

    def start_rename_plugin(self, current_path, old_name, new_name):
        """Remplace les occurences dans les noms des fichiers, les répertoires,
        et au sein des fichiers
        :param current_path: Répertoire courant
        :param old_name:     Ancien nom
        :param new_name:     Nouveau nom
        :type current_path:  str
        :type old_name:      str
        :type new_name:      str
        """
        core_template_test = 'core' + os.sep + 'template'
        if old_name != '' and new_name != '':
            # Remplacement des occurences dans les noms des fichiers et
            # des répertoires
            for item in os.listdir(current_path):
                item_path = os.path.join(current_path, item)
                # A enlever quand plugin-template sera renommé plugin-Template
                if not item_path.endswith(core_template_test):
                    item = self.rename_item(current_path + os.sep,
                                            item,
                                            old_name,
                                            new_name)
                if os.path.isdir(current_path + os.sep + item):
                    self.start_rename_plugin(current_path + os.sep + item,
                                             old_name,
                                             new_name)
                else:
                    # Remplacement des occurences dans le fichier
                    File.replace_in_file(
                        current_path + os.sep + item, old_name, new_name)

    @staticmethod
    def rename_item(path, item, old_name, new_name):
        """Renomme un élément si besoin
        :param path:     Chemin courant
        :param item:     Fichier à tester
        :param old_name: Ancien nom du plugin
        :param new_name: Nouveau nom du plugin
        :type path:      str
        :type item:      str
        :type old_name:  str
        :type new_name:  str
        :return:         Fichier avec le nouveau nom si il a été renommé
        :rtype:          str
        """
        result = item
        # Cas simple
        if old_name in item:
            result = item.replace(old_name, new_name)
            os.rename(path + item, path + result)
        # En majuscule
        elif old_name.upper() in item:
            result = item.replace(old_name.upper(), new_name.upper())
            os.rename(path + item, path + result)
        # En minuscule
        elif old_name.lower() in item:
            result = item.replace(old_name.lower(), new_name.lower())
            os.rename(path + item, path + result)
        # Avec une majuscule au début
        elif old_name.capitalize() in item:
            result = item.replace(old_name.capitalize(), new_name.capitalize())
            os.rename(path + item, path + result)
        return result


class WizardMenu(BaseMenu):
    """
    Classe du menu de l'assistant
    """
    plugins_list = []
    actions = []

    def __init__(self, initial_plugins_list):
        """Constructeur
        Initialise le chemin vers le fichier qui stocke le nom du plugin.
        :params initial_plugins_list: Liste des plugins disponibles
        :type initial_plugins_list:   List
        """
        # Configuration du menu
        # Premier choix : Assistant
        self.plugins_list = initial_plugins_list
        self.actions = []
        self.menu = []
        self.menu.append('Démarrer l\'assistant')
        self.actions.append([WizardMenu.start_wizard, None])
        # Recherche si le plugin template existe déjà
        add_template_download = True
        for plugin in self.plugins_list:
            if 'template' in plugin[1] or 'Template' in plugin[1]:
                add_template_download = False
        if add_template_download:
            self.menu.append('Télécharger le plugin ExtraTemplate')
            self.actions.append([WizardMenu.git_extratemplate, None])
        # Ajout de la liste des plugins dans le répertoire
        for plugin in self.plugins_list:
            self.menu.append('Modifier le plugin ' + plugin[1])
            self.actions.append([WizardMenu.start_tools, plugin])

    def start(self):
        """Démarre l'affichage du menu
        """
        loop = True
        return_value = False
        while loop:
            user_choice = IO.get_menu_choice(self.menu)
            if user_choice == -1:
                loop = False
            else:
                # DEBUG
                # if self.actions[user_choice][1] is None:
                #     return_value = self.actions[user_choice][0]()
                # else:
                #     return_value = self.actions[user_choice][0](
                #     self.actions[user_choice][1])
                try:
                    if self.actions[user_choice][1] is None:
                        return_value = self.actions[user_choice][0]()
                    else:
                        return_value = self.actions[user_choice][0](
                            self.actions[user_choice][1])
                except AttributeError:
                    IO.print_error(self.bad_command)
                    return_value = False
        return return_value

    @staticmethod
    def start_wizard():
        """Lance l'assistant
        """
        plugin_data = WizardMenu.ask_plugin_informations()
        if plugin_data is not None:
            WizardMenu.create_folder_struct(plugin_data)
            WizardMenu.gen_info_json(plugin_data)
            WizardMenu.gen_installation_php(plugin_data)
            WizardMenu.gen_configuration(plugin_data)
            WizardMenu.gen_desktop_php(plugin_data)
            WizardMenu.gen_core_php(plugin_data)
        exit(0)

    @staticmethod
    def ask_plugin_informations():
        """Obtenir les informations pour le futur plugin.
        :return: Informations compilées
        :rtype:  dict
        """
        data = {}

        print(' - Le nom apparait dans l\'interface de Jeedom')
        data['name'] = IO.ask_with_default('Nom',
                                           config['default_package_name'])
        plugin_id = data['name'].lower().replace(' ', '_').capitalize()

        print(' - L\'identifiant différencie le plugin des autres.')
        data['id'] = IO.ask_with_default('ID', plugin_id)

        # Test si le répertoire existe à ce niveau pour éviter la suite du
        # questionnaire
        if os.path.exists('plugin-' + data['id']):
            IO.print_error('Le répertoire du plugin existe déjà')
            data = None
        else:
            data['description'] = IO.get_user_input(
                'Description (optional) : ')
            data['license'] = IO.ask_with_default('Licence', 'GPL')
            data['author'] = IO.get_user_input('Auteur (optionnal) : ')
            data['require'] = IO.ask_with_default('Version requise de Jeedom',
                                                  '3.0')
            data['version'] = IO.ask_with_default('Version du plugin', '1.0')
            category_choice = IO.get_menu_choice(config['jeedom_categories'],
                                                 'Choix de la catégorie', False)
            data['category'] = config['jeedom_categories'][category_choice]
            configuration = None

            if IO.ask_y_n('Générer la page de configuration ?', 'o') == 'o':
                configuration = []
                loop = True
                menu = ['Champ texte',
                        'Case à cocher']
                values = ['text',
                          'checkbox']
                while loop:
                    print('Ajouter un champ ?')
                    result = IO.get_menu_choice(menu)
                    if result == -1:
                        loop = False
                    else:
                        label = IO.get_user_input('Label : ')
                        code = IO.get_user_input('Code : ')
                        configuration.append({
                            'type': values[result],
                            'label': label,
                            'code': code})
            data['configuration'] = configuration
            data['documentation_language'] = IO.ask_with_default(
                'Langue de la documentation (fr_FR, en_US)', 'fr_FR')

            # Generate shortcuts
            plugin_path = 'plugin-' + data['id']
            data[
                'plugin_info_path'] = plugin_path + os.sep + 'plugin_info' + \
                                      os.sep
            data['core_path'] = plugin_path + os.sep + 'core' + os.sep
            data['desktop_path'] = plugin_path + os.sep + 'desktop' + os.sep

        return data

    @staticmethod
    def create_folder_struct(plugin_data):
        """Créé la structure de répertoires
        :param plugin_data: Données du plugin
        :type plugin_data:  dict
        """
        subfolders = [
            'core',
            'desktop',
            'docs',
            'plugin_info'
        ]
        desktop_subfolders = [
            'css',
            'js',
            'modal',
            'php'
        ]
        core_subfolders = [
            'ajax',
            'class',
            'php',
        ]
        # Parent folder
        plugin_dir = 'plugin-' + plugin_data['id']
        os.mkdir(plugin_dir)
        # First level subfolders
        for subfolder in subfolders:
            os.mkdir(plugin_dir + os.sep + subfolder)
        # Desktop subfolders
        for desktop_subfolder in desktop_subfolders:
            os.mkdir(
                plugin_dir + os.sep + 'desktop' + os.sep + desktop_subfolder)
        # Core subfolders
        for core_subfolder in core_subfolders:
            os.mkdir(plugin_dir + os.sep + 'core' + os.sep + core_subfolder)
        # license file
        license_file = open(plugin_dir + os.sep + 'LICENSE', 'w')
        license_file.close()
        # Documentation folder
        os.mkdir(plugin_dir + os.sep + 'docs' + os.sep +
                 plugin_data['documentation_language'])

    @staticmethod
    def gen_info_json(plugin_data):
        """Ecrit le fichier d'information du plugin
        :param plugin_data: Données du plugin
        :type plugin_data:  dict
        """
        with open(plugin_data['plugin_info_path'] + 'info.json', 'w') as dest:
            dest.write(
                '{\n'
                '  "id": "%s",\n'
                '  "name": "%s",\n'
                '  "licence": "%s",\n'
                '  "require": "%s",\n'
                '  "version": "%s",\n'
                '  "category": "%s",\n'
                '  "hasDependency": false,\n'
                '  "hasOwnDaemon": false,\n'
                '  "maxDependancyInstallTime": 0,\n'
                '  "documentation": "%s",\n'
                '  "changelog": "%s"' % (
                    plugin_data['id'],
                    plugin_data['name'],
                    plugin_data['license'],
                    plugin_data['require'],
                    plugin_data['version'],
                    plugin_data['category'],
                    config['default_documentation_url'] % (plugin_data['id']),
                    config['default_changelog_url'] % (plugin_data['id'])
                )
            )
            if plugin_data['description'] != '':
                dest.write(
                    ',\n  "description": "%s"' % (plugin_data['description']))
            if plugin_data['author'] != '':
                dest.write(',\n  "author": "%s"' % (plugin_data['author']))
            dest.write('\n}\n')
            dest.close()

    @staticmethod
    def gen_installation_php(plugin_data):
        """Ecrit la classe d'installation du plugin dans plugin_info
        :param plugin_data: Données du plugin
        :type plugin_data:  dict
        """
        target_file = plugin_data['plugin_info_path'] + 'install.php'
        File.create_php_file_and_replace(wizard_install, target_file,
                                         'PluginName',
                                         plugin_data['id'])

    @staticmethod
    def gen_configuration(plugin_data):
        """Ecrit le formulaire de configuration du plugin dans plugin_info
        :param plugin_data: Données du plugin
        :type plugin_data:  dict
        """
        if plugin_data['configuration']:
            target_file = plugin_data['plugin_info_path'] + 'configuration.php'
            File.create_php_file(wizard_configuration, target_file)
            with open(target_file, 'a') as dest:
                dest.write('<form class="form-horizontal">\n  <fieldset>\n')
                for item in plugin_data['configuration']:
                    dest.write('    <div class="form-group">\n'
                               '      <label class="col-sm-3 control-label">\n'
                               '        {{%s}}\n'
                               '      </label>\n'
                               '      <div class="col-sm-9">\n'
                               '        <input class="configKey form-control" '
                               '' % (item['label']))
                    if item['type'] == 'checkbox':
                        dest.write('type="checkbox" ')
                    dest.write('data-l1key="%s" />\n'
                               '      </div>\n'
                               '    </div>\n'
                               '' % (item['code']))
                dest.write('  </fieldset>\n</form>\n')

    @staticmethod
    def gen_desktop_php(plugin_data):
        """Ecrit le fichier PHP du desktop pour le rendu
        :param plugin_data: Données du plugin
        :type plugin_data:  dict
        """
        target_file = plugin_data['desktop_path'] + os.sep + 'php' + os.sep + \
                      plugin_data['id'] + '.php'
        File.create_php_file(wizard_desktop_php, target_file)

    @staticmethod
    def gen_core_php(plugin_data):
        """Ecrit le fichier PHP du core
        :param plugin_data: Données du plugin
        :type plugin_data:  dict
        """
        target_file = plugin_data['core_path'] + os.sep + 'class' + os.sep + \
                      plugin_data['id'] + '.class.php'
        File.create_php_file_and_replace(wizard_core_class,
                                         target_file,
                                         'PluginName',
                                         plugin_data['id'])

        target_file = plugin_data['core_path'] + os.sep + 'class' + os.sep + \
                      plugin_data['id'] + 'Cmd.class.php'
        File.create_php_file_and_replace(wizard_core_cmd_class,
                                         target_file,
                                         'PluginName',
                                         plugin_data['id'])

    @staticmethod
    def git_extratemplate():
        """Télécharge une copie du plugin ExtraTemplate
        :params data: Inutilisé
        """
        if not os.path.exists('plugin-ExtraTemplate'):
            sys_return = os.system(
                'git clone ' + config['plugin_template_repo'] +
                ' 2> /dev/null')
            if sys_return == 0:
                IO.print_success(
                    'Le plugin plugin-ExtraTemplate a été téléchargé')
            else:
                IO.print_error('Erreur dans le téléchargement de '
                               'plugin-ExtraTemplate.')
        else:
            IO.print_error(
                'Le plugin plugin-ExtraTemplate est déjà téléchargé.')

        WizardMenu.start_tools(['plugin-ExtraTemplate', 'ExtraTemplate'])

    @staticmethod
    def start_tools(plugin_data):
        """Lance l'outil
        :params plugin_data: Tableau contenant le chemin et le nom du plugin
        :type plugin_data:   List[str]
        """
        root_menu = RootMenu(plugin_data[0], plugin_data[1])
        root_menu.start()


def start():
    """
    Point de d'entrée en mode CLI
    """

    readed_args = Tools.parse_args(sys.argv)
    if readed_args is not None:
        plugins_list = []
        if readed_args == '':
            plugins_list = Tools.get_plugins_in_dir('.')
        wizard_menu = WizardMenu(plugins_list)
        wizard_menu.start()


# Gestion des accents pour python 2
if sys.version_info[0] < 3: # pragma: no cover
    reload(sys)  # pylint: disable=undefined-variable
    sys.setdefaultencoding('utf8')  # pylint: disable=no-member

if __name__ == '__main__':
    start() # pragma: no cover