# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

__all__ = (
    'Wyapp',
    'wyapp_matcher', 'wyappname_matcher',
)

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

from .utils import *
from .expr import *
from .objects import XT, fileP, pathP, mpathP, lowerP, Object, catalog
from .parser import Parser
from .base_module import withdomain, host_matcher, hostname_matcher

################################################################################
# Wyapp

def match_wyapp(qwyapp, object):
    if withpath(qwyapp): # wyapp avec chemin
        return qwyapp in object.get('wyapp', ())
    else: # nom de wyapp
        return qwyapp in object.get('wyappname', ())
def wyapp_matcher(qwyapp):
    return lambda object: match_wyapp(qwyapp, object)

def match_wyappname(qwyapp, object):
    qwyapp = path.basename(qwyapp)
    return qwyapp in object.get('wyappname', ())
def wyappname_matcher(qwyapp):
    return lambda object: match_wyappname(qwyapp, object)

class Wyapp(Object):
    ATTRS = XT(Object,
               values=pathP, wyapp=mpathP, wyappdir=pathP,
               basedir=pathP, dirspec=fileP)

    def _resolve(self, catalog):
        if self.oid == '*': return
        default = catalog.get(self.otype, '*', None, False)

        wyapps = self.get('wyapp', [])
        basedir = self.get('basedir', None)
        if basedir is not None:
            wyapps.extend(self.resolve_basedir(basedir, dirs=True))
        dirspec = self.get('dirspec', None)
        if dirspec is not None:
            wyapps.extend(self.resolve_filespec(dirspec, dirs=True))

        if wyapps:
            # générer wyappdir et wyappname à partir de wyapp
            wyappdirs = [path.dirname(wyapp) for wyapp in wyapps]
            if wyappdirs: wyappdirs = self.wyappdir = ulistof(wyappdirs)

            wyappnames = [path.basename(wyapp) for wyapp in wyapps]
            if wyappnames: wyappnames = self.wyappname = ulistof(wyappnames)

        else:
            # générer wyapps à partir de wyappdir et wyappname
            wyappdirs = self.get('wyappdir', ())
            if not wyappdirs and default is not None:
                wyappdirs = default.get('wyappdir', ())
            if wyappdirs: wyappdirs = self.wyappdir = ulistof(wyappdirs)

            wyappnames = self.get('wyappname', ())
            if not wyappnames: wyappnames = [self.oid]
            if wyappnames: wyappnames = self.wyappname = ulistof(wyappnames)

        if wyappdirs:
            wyapps = []
            for wyappname in wyappnames:
                found = []
                for wyappdir in wyappdirs:
                    wyapp = path.join(wyappdir, wyappname)
                    if path.exists(wyapp):
                        found.append(wyapp)
                        break
                if not found:
                    found = [path.join(wyappdirs[0], wyappname)]
                wyapps.extend(found)
        else:
            wyapps = wyappnames
        if wyapps: wyapps = self.wyapp = ulistof(wyapps)

        if not self.values:
            self.values = wyapps

################################################################################
# Actions

def query_rwyinst(*args):
    """afficher la commande pour déployer avec la commande $1 le wyapp $2 sur
    l'hôte $3 dans le profil $4

    $1 doit valoir 'rwyinst' ou être un chemin vers ce script

    $2 peut être
    * un nom de wyapp: tout les wyapps de ce nom sont sélectionnés
    * un chemin complet: si un wyapp avec le chemin complet est trouvé, ne
      sélectinner que celui-là, sinon faire comme si on n'avait spécifié que le
      nom du wyapp
    * non spécifié: tout les wyapps devant être déployé sur l'hôte sont
      cherchés

    $3 peut être
    * un nom d'hôte: tous les hôtes de ce nom sont sélectionés
    * un nom d'hôte pleinement qualifié: si le nom d'hôte pleinement qualifié
      est trouvé, ne sélectionner que celui-là, sinon faire comme si on n'avait
      spécifié que le nom d'hôte
    * non spécifié: tous les hôtes vers lequel doit être déployé le wyapp sont
      cherchés

    $4 peut valoir
    * 'NONE': seuls les déploiements sans profils définis sont sélectionnés.
      c'est la valeur par défaut.
    * 'ALL' ou '': ne pas tenir compte du profil lors de la sélection des
      wyapps et des hôtes
    * toute autre valeur, e.g prod ou test: seuls les déploiement de ce profil
      sont sélectionnés
    Il est possible de spécifier plusieurs profils en les séparant par des
    virgules. Par exemple, 'prod,NONE' permet de sélectionner les déploiements
    sans profil ou dans le profil 'prod'

    le wyapp, ou l'hôte, ou les deux sont requis. le profil est facultatif.

    Les valeurs $5..$* sont des définitions d'attributs utilisées pour mettre à
    jour les faits trouvés. Ces définitions sont utilisés comme argument de
    wyinst.
    """
    rwyinst = args[0] if args[0:1] else None
    if rwyinst is not None and (rwyinst == 'rwyinst' or rwyinst.endswith('/rwyinst')):
        verb = 'rwyinst'
    else:
        raise ValueError("Le verbe est requis et doit valoir 'rwyinst'")
    qwyapp = args[1:2] and args[1] or None
    qhost = args[2:3] and args[2] or None
    qprofile = args[3] if args[3:4] else 'NONE'
    supplattrs = args[4:]

    if not qwyapp and not qhost:
        raise ValueError("Il faut spécifier wyapp et/ou host")

    if not qwyapp:
        wyapps = None
    elif cwithpath(qwyapp):
        qwyapp = path.abspath(qwyapp)
        wyapps = catalog.find_objects('wyapp', expr=wyapp_matcher(qwyapp))
        if not wyapps:
            wyapps = catalog.find_objects('wyapp', expr=wyappname_matcher(qwyapp))
    else:
        wyapps = catalog.find_objects('wyapp', expr=wyappname_matcher(qwyapp))

    if not qhost:
        hosts = None
    else:
        if cwithpath(qhost):
            qhost = path.basename(path.abspath(qhost))
        if withdomain(qhost):
            hosts = catalog.find_objects('host', expr=host_matcher(qhost))
            if not hosts:
                hosts = catalog.find_objects('host', expr=hostname_matcher(qhost))
        else:
            hosts = catalog.find_objects('host', expr=hostname_matcher(qhost))

    if qprofile == '': qprofile = 'ALL'
    qprofiles = flattenstr([qprofile])
    if 'ALL' in qprofiles:
        qprofile = None
    else:
        expr = []
        for qprofile in qprofiles:
            if qprofile == 'NONE':
                qprofile = NONE(EXISTS('profile'))
            else:
                qprofile = dict(profile=qprofile)
            expr.append(qprofile)
        qprofile = ANY(*expr)

    # wyapps et hosts sont spécifiés
    if wyapps is not None and hosts is not None:
        facts = catalog.find_facts(
            verb=verb,
            tsotype='wyapp', tsexpr=dict(oid=[wyapp.oid for wyapp in wyapps]),
            ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]),
            expr=qprofile,
        )

    # Seuls les wyapps sont spécifiés: chercher les hôtes
    elif wyapps is not None:
        facts = catalog.find_facts(
            verb=verb,
            tsotype='wyapp', tsexpr=dict(oid=[wyapp.oid for wyapp in wyapps]),
            ttotype='host',
            expr=qprofile,
        )

    # Seuls les hôtes sont spécifiés: chercher les wyapps
    elif hosts is not None:
        facts = catalog.find_facts(
            verb=verb,
            tsotype='wyapp',
            ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]),
            expr=qprofile,
        )

    # afficher la commande
    if supplattrs: parser = Parser()
    for fact, tsobjects, ttobjects in facts:
        hs = flattenseq([host.host for host in ttobjects])
        ws = flattenseq([wyapp.wyapp for wyapp in tsobjects])
        if supplattrs: parser.parse_attrs(supplattrs, fact)
        vars = []
        for name, values in fact.attrs.items():
            vars.append("%s=%s" % (name, qshell(':'.join(values))))
        for w in ws:
            # préférer si possible le chemin fourni par l'utilisateur
            if withpath(qwyapp): w = qwyapp
            hs = ':'.join(hs)
            parts = [xwyinst, '--no-deploydb', '-h', qshell(hs), qshell(w), '--', '-y']
            if vars: parts.append(' '.join(vars))
            print ' '.join(parts)
