"""
Driver model that allows customized deploy bahaviour
"""

__date__ = '2013-12-11'
__license__ = 'GPL v2.0 (or later)'

import subprocess
import os

from git_deploy.git_methods import GitMethods
from git_deploy.config import log, exit_codes, \
    DEFAULT_HOOK


class DeployDriverError(Exception):
    """ Basic exception class for DeployDriver types """
    def __init__(self, message="DeployDriver error.", exit_code=1):
        Exception.__init__(self, message)
        self._exit_code = int(exit_code)

    @property
    def exit_code(self):
        return self._exit_code


class DeployDriverDefault(object):
    """
    Default Driver class
    """

    # class instance
    __instance = None

    def __init__(self, *args, **kwargs):
        """ Initialize class instance """
        self.__class__.__instance = self

    def __new__(cls, *args, **kwargs):
        """ This class is Singleton, return only one instance """
        if not cls.__instance:
            cls.__instance = super(DeployDriverDefault, cls).__new__(cls,
                                                                     *args,
                                                                     **kwargs)
        return cls.__instance

    def sync(self, args):

        #
        # Call deploy hook on client
        #
        #   {% PATH %}/.git/deploy/hooks/default-client-push origin master
        #

        try:
            GitMethods()._dulwich_tag(args['tag'], args['author'])
        except Exception as e:
            log.error(str(e))
            raise DeployDriverError(message=exit_codes[12], exit_code=12)

        sync_file = '{0}/{1}'.format(args['deploy_sync'], DEFAULT_HOOK)

        log.info('{0} :: Calling default sync \'{1}\' ... '.
            format(__name__, sync_file))

        proc = subprocess.Popen([sync_file,
            args['remote'],
            args['branch']],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)

        log.info('SYNC OUT -> ' + '; '.join(
            filter(lambda x: x, proc.communicate())))


class DeployDriverCustom(object):
    """ Driver class for custom hooks """

    # class instance
    __instance = None

    def __init__(self, *args, **kwargs):
        """ Initialize class instance """
        self.__class__.__instance = self

    def __new__(cls, *args, **kwargs):
        """ This class is Singleton, return only one instance """
        if not cls.__instance:
            cls.__instance = super(DeployDriverCustom, cls).__new__(cls, *args,
                                                                    **kwargs)
        return cls.__instance

    def _call_hooks(self, path, phase):
        """Performs calls on path/phase dependent hooks

        :param path: hooks path
        :param phase: deploy phase

        """
        if os.path.exists(path):
            sorted_path = sorted(os.listdir(path))
            for item in sorted_path:
                item_phase = item.split('.')[0]

                # CALL hook and log
                if phase == item_phase:
                    cmd = path + '/' + item
                    log_msg = 'CALLING \'{0}\' ON PHASE \'{1}\''.format(
                      cmd, phase
                    )
                    log.info(__name__ + ' :: {0}'.format(log_msg))
                    proc = subprocess.Popen(path + '/' + item,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
                    log.info(cmd + ' OUT -> ' + '; '.join(
                        filter(lambda x: x, proc.communicate())))

                    # Flag a failed hook
                    if proc.returncode:
                        raise DeployDriverError(exit_code=17, message=exit_codes[17])

        else:
            log.error(__name__ + ' :: CANNOT FIND HOOK PATH \'{0}\''.format(
                path))
            raise DeployDriverError(exit_code=17, message=exit_codes[17])

    def sync(self, args):
        """Call custom syncing behaviour.

        1. Call common pre-sync hooks
        2. Call app specific pre-sync hooks
        3. Call app sync
        4. Call app specific post-sync hooks
        5. Call common post-sync hooks

        """

        # CALL deploy/apps/common
        self._call_hooks(args['deploy_apps_common'], 'pre-sync')

        # CALL deploy/apps/$env
        app_path = '{0}/{1}'.format(args['deploy_apps'], args.env)
        self._call_hooks(app_path , 'pre-sync')

        # CALL sync, deploy/apps/sync/$env.sync
        self._call_hooks(app_path , args.env)
        self._call_hooks(app_path , 'post-sync')
        self._call_hooks(args['deploy_apps_common'] , 'post-sync')

@@ -3733,32 +3733,80 @@
 or(exit_code=17,
+%0A                                               
  message=exit_co
@@ -4720,24 +4720,1299 @@
 common'%5D , 'post-sync')%0A
+%0A%0Aclass DeployDriverDryRun(object):%0A    %22%22%22%0A    Dryrun Driver class%0A    %22%22%22%0A%0A    # class instance%0A    __instance = None%0A%0A    def __init__(self, *args, **kwargs):%0A        %22%22%22 Initialize class instance %22%22%22%0A        self.__class__.__instance = self%0A%0A    def __new__(cls, *args, **kwargs):%0A        %22%22%22 This class is Singleton, return only one instance %22%22%22%0A        if not cls.__instance:%0A            cls.__instance = super(DeployDriverDryRun, cls).__new__(cls,%0A                                                                     *args,%0A                                                                     **kwargs)%0A        return cls.__instance%0A%0A    def sync(self, args):%0A%0A        log.info('%7B0%7D :: DRYRUN SYNC'.format(__name__))%0A        log.info('--%3E TAG %5C'%7B0%7D%5C''.format(args%5B'tag'%5D))%0A        log.info('--%3E AUTHOR %5C'%7B0%7D%5C''.format(args%5B'author'%5D))%0A        log.info('--%3E REMOTE %5C'%7B0%7D%5C''.format(args%5B'remote'%5D))%0A        log.info('--%3E BRANCH %5C'%7B0%7D%5C''.format(args%5B'branch'%5D))%0A%0A        if args%5B'hook_script'%5D:%0A            log.info('--%3E You%5C've specified a CUSTOM HOOK.')%0A            # TODO - emit all scripts and content to be run in order%0A        else:%0A            log.info('--%3E You are using the DEFAULT SYNC.')%0A            # TODO - emit all scripts and content to be run in order%0A%0A%0A%0A

add - a dryrun driver class.