#!/usr/bin/env python
#
# This file is part of REPA: Release Engineering Process Assistant.
#
# Copyright (C) 2013 Intel Corporation
#
# REPA is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.

"""
REPA: Release Engineering Process Assistant.

Copyright (C) Intel Corporation 2013
Licence: GPL version 2
Author: Ed Bartosh <eduard.bartosh@intel.com>

Common module.
Common functions, classes, exceptions.
"""

import sys
import os
import time
import json
from functools import wraps, partial

OBS_PROJECT_PREFIX = "home:prerelease:"

class RepaException(Exception):
    """Custom repa exception. All repa modules should use it."""
    pass

def get_prerelease_projects(obs, target_prj, tag=None):
    """Get list of prerelease projects for specified target project."""
    return obs.get_project_list('^%s%s:' % (OBS_PROJECT_PREFIX, target_prj))

def get_project_by_name(obs, name, target):
    """Lookup for a project in OBS by submission or group name."""
    if name.startswith('submitgroup/'):
        name += '-group'
    projects = list(obs.get_projects('^%s%s:%s$' % (OBS_PROJECT_PREFIX, target,
                                                    name.replace('/', ':'))))
    if not projects:
        raise RepaException('OBS project not found for %s %s' % \
                            (target, name))
    if len(projects) > 1:

        plist = '\n  '.join(prj for prj, _desc, _build_results in projects)
        raise RepaException('%s %s resolves into multiple projects:\n  %s' % \
                            (target, name, plist))

    return projects[0][0], json.loads(projects[0][1]), projects[0][2]


def _resolve_submissions(obs, name, target):
    """Get list of submissions with meta. Resolves submitgroups."""
    project, meta, _bresults = get_project_by_name(obs, name, target)
    if name.startswith('submitgroup'):
        for subm in meta['submissions']:
            sprj, smeta, _bresults = get_project_by_name(obs, subm, target)
            yield subm, sprj, smeta
    else:
        yield name, project, meta


def delete_project(obs, name, target):
    """Delete OBS project related to submission or submit group."""
    project = get_project_by_name(obs, name, target)[0]
    obs.delete_project(project)


def accept_or_reject(obs, submission, state, target, comment=''):
    """Create SRs and set their state for one submission or for a group."""
    for name, project, meta in _resolve_submissions(obs, submission, target):
        # osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
        # osc request accept [-m TEXT] ID
        print "submission %s" % str(name)

        commit = meta.get('git_commit') or meta['git_tag']
        submitter = meta.get('submitter')
        message = ''
        if submitter:
            message = "Submitter: %s\n" % submitter

        message += "Comments: %s \nGit project: %s\nTag: %s\nCommit: %s" \
            % (comment or "submission %s" % str(name),
              meta['projects'][0],
              meta['git_tag'],
              commit)

        # Create SR
        reqid = obs.create_sr(project, obs.get_source_packages(project),
                              str(meta['obs_target_prj']), message=message)

        print 'created SR %s' % reqid

        # and immediately set its state
        message = "SR %s is set to %s" % (reqid, state)
        if comment:
            message += comment
        obs.set_sr_state(reqid, state=state,
                         message=str(message), force=True)
        print 'set SR state to', state

    # delete submit group
    if submission.startswith('submitgroup'):
        delete_project(obs, submission, target)

class CancelRetryError(Exception):
    """Exception for handling cancelling of the retry loop. Needed for
    transparently re-raising the previous exception."""
    def __init__(self):
        Exception.__init__(self)
        self.typ, self.val, self.backtrace = sys.exc_info()

def retry(exceptions, tries=10, sleep=1):
    """Decorator for re-trying function calls"""
    def decorator(func):
        """The "real" decorator function"""
        @wraps(func)
        def wrap(*args, **kwargs):
            """Wrapper for re-trying func"""
            for attempt in range(1, tries + 1):
                try:
                    return func(*args, **kwargs)
                except CancelRetryError as err:
                    raise err.typ, err.val, err.backtrace
                except exceptions as err:
                    if attempt >= tries:
                        raise
                    elif sleep:
                        time.sleep(sleep)
        return wrap
    return decorator

class Colorizer:
    """Colorize text with ANSI colors."""
    colors = {'black': '\033[90m', 'red':     '\033[91m',
              'green': '\033[92m', 'yellow':  '\033[93m',
              'blue':  '\033[94m', 'magenta': '\033[95m',
              'cyan':  '\033[96m', 'white':   '\033[97m'
             }
    reset = '\033[0m'

    def __init__(self, enable=True):
        self.enabled = enable

    def __getattr__(self, name):
        if name in self.colors:
            return partial(self.colorize, color=name)

    def disable(self):
        """Disable colorizing."""
        self.enabled = False

    def enable(self):
        """Enable colorizing."""
        self.enabled = True

    def colorize(self, text, color):
        """Colorize text."""
        if self.enabled:
            return self.reset + self.colors[color] + text + self.reset
        else:
            return text

def get_download_url(meta):
    """Get download url from meta."""
    if 'download_url' in meta:
        return meta['download_url']
    # Guess url from image url if download_url is not in the meta
    if 'images' not in meta:
        return
    for img in meta['images']:
        if 'url' in img:
            return img['url'].split('images')[0]


def get_obs_url(meta, buildurl='https://build.tizen.org'):
    """Get obs project url from meta."""
    if 'obs_url' in meta:
        return meta['obs_url']
    # Make it from git tag and obs_target_prj if obs_url is not in the meta
    if 'obs_target_prj' not in meta:
        return
    if 'name' not in meta and 'git_tag' not in meta:
        return
    name = meta.get('git_tag') or meta.get('name')
    return os.path.join(buildurl, 'project/show?project=home:prerelease:%s:%s'
                        % (meta['obs_target_prj'], name.replace('/', ':')))

