#!/usr/bin/env python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

"""Interroger la base deploydb
"""

import logging; log = logging.getLogger(__name__)
import sys
from os import path
from argparse import ArgumentParser

from .utils import *
from .parser import Parser
from .objects import catalog

DEFAULT_CONFIG = 'deploydb.conf'
USER_CONFDIR = path.expanduser('~/etc/deploy')
SYSTEM_CONFDIR = '/var/local/deploy'

DEFAULT_MODULES = ['base']
DEFAULT_FUNC = 'base.query'
DEFAULT_ACTION = 'run'

SCRIPTDIR = path.dirname(path.dirname(path.dirname(__file__)))

################################################################################
# Programme principal

logging.basicConfig()

from argparse import ArgumentParser, HelpFormatter
if sys.argv[1:2] == ['--compat']:
    # Avec l'argument --compat, désactiver la classe FancyHelpFormatter qui
    # se base sur une API non documentée
    sys.argv = sys.argv[0:1] + sys.argv[2:]
    FancyHelpFormatter = HelpFormatter
else:
    class FancyHelpFormatter(HelpFormatter):
        """Comme HelpFormatter, mais ne touche pas aux lignes qui commencent par les
        caractères '>>>'. Cela permet de mixer du texte formaté et du texte non
        formaté.
        """
        def _fill_text(self, text, width, indent):
            return ''.join([indent + line for line in text.splitlines(True)])
        def _split_lines(self, text, width):
            lines = ['']
            for line in text.splitlines():
                if line.startswith('>>>'):
                    lines.append(line)
                    lines.append('')
                else:
                    lines[-1] += '\n' + line
            lines = filter(None, lines)
            texts = []
            for line in lines:
                if line.startswith('>>>'):
                    line = line[3:]
                    if line: texts.append(line)
                else:
                    texts.extend(super(FancyHelpFormatter, self)._split_lines(line, width))
            return texts
AP = ArgumentParser(
    usage=u"deploydb args...",
    description=__doc__,
    formatter_class=FancyHelpFormatter,
)
AP.set_defaults(
    missing_ok=False,
    modules=DEFAULT_MODULES,
    func=DEFAULT_FUNC,
    resolve=True,
    action=DEFAULT_ACTION,
)
AP.add_argument('-c', '--config', dest='config',
                help=u"Spécifier le fichier de configuration à utiliser. S'il s'agit d'un nom sans chemin, il est recherché dans les répertoires par défaut.")
AP.add_argument('--missing-ok', action='store_true', dest='missing_ok',
                help=u"Sortir sans erreur si le fichier de configuration n'est pas trouvé")
AP.add_argument('-m', '--module', action='append', dest='modules', metavar='MODULE',
                help=u"Spécifier un module à charger. Cette option peut être spécifiée autant de fois que nécessaire. Par défaut, seul le module 'base' est chargé.")
AP.add_argument('-r', '--func', dest='func',
                help=u"Spécifier la fonction à lancer après le chargement de la base de données. La valeur par défaut est %s" % DEFAULT_FUNC)
AP.add_argument('--no-resolve', action='store_false', dest='resolve',
                help=u"Ne pas résoudre les objets avant de lancer la fonction. Cette option avancée ne devrait pas avoir besoin d'être utilisée.")
AP.add_argument('--run', action='store_const', dest='action', const='run',
                help=u"Lancer la fonction spécifiée avec l'option --func")
AP.add_argument('--dump', action='store_const', dest='action', const='dump',
                help=u"Afficher le contenu de la base de données")
AP.add_argument('args', nargs='*')
o = AP.parse_args()

# charger les modules
MODULES = {}
for module in o.modules:
    MODULES[module] = __import__('%s_module' % module, globals())

# charger la configuration
config = o.config
if config is not None and ('/' in config or path.isfile(config)):
    deploydb_path = [path.abspath(path.dirname(config))]
else:
    deploydb_path = [USER_CONFDIR, SYSTEM_CONFDIR, SCRIPTDIR]
    cname = config if config is not None else DEFAULT_CONFIG
    config = find_in_path(cname, deploydb_path)
    if config is None and not o.missing_ok:
        raise ValueError("Impossible de trouver le fichier de configuration %s" % cname)

catalog.create_object('deploydb', 'path', dir=deploydb_path)
Parser(config)

dd_path = catalog.get('deploydb', 'path')
dd_include = catalog.get('deploydb', 'include', None, create=False)
if dd_include is not None:
    included = set([config])
    while True:
        done = True
        for file in dd_include.get('file', ()):
            # cette valeur peut changer au fur et à mesure que les fichiers sont
            # inclus. la recharger systématiquement
            deploydb_path = dd_path.get('dir', ())
            pf = find_in_path(file, deploydb_path)
            if pf in included: continue
            included.add(pf)
            if pf is not None:
                Parser(pf)
                # il y a peut-être de nouveaux fichiers à inclure. configurer
                # une nouvelle itération
                done = False
            else:
                log.warning("deploydb:include: %s: Fichier introuvable", file)
        if done: break

deploydb_path = dd_path.get('dir', ())
dd_loadcsv = catalog.get('deploydb', 'loadcsv', None, create=False)
if dd_loadcsv is not None:
    otype = dd_loadcsv.first('otype', None)
    otype_col = dd_loadcsv.first('otype_col', 'otype')
    oid_col = dd_loadcsv.first('oid_col', 'oid')
    for file in dd_loadcsv.get('file', ()):
        pf = find_in_path(file, deploydb_path)
        if pf is not None:
            catalog.load_csv(pf, otype, otype_col, oid_col)
        else:
            log.warning("deploydb:loadcsv: %s: Fichier introuvable", file)

# actions
if o.resolve: catalog.resolve()

if o.action == 'run':
    names = o.func.split('.')
    func = MODULES[names[0]]
    for name in names[1:]:
        func = getattr(func, name)
    func(*o.args)

elif o.action == 'dump':
    catalog.dump()
