#!/usr/bin/python
# Copyright Cloud Linux Zug GmbH
import os
import getopt
import traceback
import sys
import shutil
import errno
import glob
from os.path import join

__author__ = 'iseletsk'


BASE_DIR = '/usr/share/kcare-eportal/patches/'
#BASE_DIR='/home/iseletsk/kcare-eportal/'
LATEST_DIR = join(BASE_DIR, "release", "release-latest")
PRODUCTION = False


def link_file(name, dir, dest_dir):
    full_name = join(dest_dir, name)
    if os.path.exists(full_name) and not os.path.islink(full_name):
        os.unlink(full_name)

    if not os.path.exists(full_name):
        try:
            src_file_path = join(LATEST_DIR, dir, name)
            os.symlink(src_file_path, full_name)
        except OSError, e:
            if e.errno == errno.EEXIST:
                pass
            else:
                raise e


def link_dir(release, dir):
    print "Linking %s" % dir
    dest_dir = join(BASE_DIR, dir)
    src_dir = join(BASE_DIR, "release", release, dir)
    if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)

    for latest in ('latest.v2', 'latest.v1'):
        path = os.path.join(src_dir, latest)
        if os.path.exists(path):
            with open(path) as f:
                release = f.read()
            break
    else:
        raise IOError('Unable to find release number. File %s not found.'
                      % os.path.join(src_dir, 'latest.vX'))

    try:
        release_level = int(release)
    except (ValueError, TypeError) as e:
        raise Exception("Unable to parse release number: %s" % e)

    release_dir = join(dest_dir, release)
    if os.path.lexists(release_dir):
        try:
            os.unlink(release_dir)
        except OSError, e:
            if e.errno == errno.EISDIR:
                shutil.rmtree(release_dir)
            else:
                raise e

    os.symlink(join(src_dir, release), release_dir)

    # Remove all fixups from current release level.
    # If they are there it means that they were copied
    # by the unrolling release.
    kpatch_fixups_mask = join(src_dir, release, 'kpatch.*fixups')
    for kpatch_fixups_file in glob.glob(kpatch_fixups_mask):
        if os.path.exists(kpatch_fixups_file):
            with open(kpatch_fixups_file) as f:
                for fixup_basename in f:
                    fixup_basename = fixup_basename.strip()
                    if not fixup_basename:
                        continue

                    fixup_module = join(src_dir, release, fixup_basename)
                    if os.path.exists(fixup_module):
                        os.unlink(fixup_module)
                        os.unlink(fixup_module + '.sig')
            os.unlink(kpatch_fixups_file)
            os.unlink(kpatch_fixups_file + '.sig')

    # copy fixups to prev level
    prev_release_level = release_level - 1
    prev_release_dir = join(dest_dir, str(prev_release_level))

    src_level = prev_release_level
    prev_src_dir = join(src_dir, str(prev_release_level))

    while src_level > 0 and os.path.exists(prev_src_dir):

        if prev_release_level > 0:
            kpatch_fixups_file_mask = os.path.join(prev_src_dir, 'kpatch.*fixups')
            for kpatch_fixups_file in glob.glob(kpatch_fixups_file_mask):
                if os.path.exists(kpatch_fixups_file):
                    fixups_file_basename = os.path.basename(kpatch_fixups_file)
                    kpatch_fixups_dest = join(prev_release_dir, fixups_file_basename)
                    shutil.copy(kpatch_fixups_file, kpatch_fixups_dest)
                    shutil.copy(kpatch_fixups_file + '.sig', kpatch_fixups_dest + '.sig')

                    with open(kpatch_fixups_file) as f:
                        for fixup_basename in f:
                            fixup_basename = fixup_basename.strip()
                            if not fixup_basename:
                                continue

                            fixup_src_name = join(prev_src_dir, fixup_basename)
                            fixup_dest_name = join(prev_release_dir, fixup_basename)
                            shutil.copy(fixup_src_name, fixup_dest_name)
                            shutil.copy(fixup_src_name + '.sig', fixup_dest_name + '.sig')

            prev_release_level -= 1
            prev_release_dir = join(dest_dir, str(prev_release_level))

        src_level -= 1
        prev_src_dir = os.path.join(src_dir, str(src_level))

    link_file('kcare.ko', dir, dest_dir)
    link_file('kcare.ko.sig', dir, dest_dir)
    link_file('version', dir, dest_dir)
    link_file(latest, dir, dest_dir)
    # remove v2 if we are rollback to v1
    latest_v2 = join(dest_dir, 'latest.v2')
    if latest == 'latest.v1' and os.path.exists(latest_v2):
        os.unlink(latest_v2)

    # do release-latest
    rl_dir = join(LATEST_DIR, dir)
    if os.path.exists(rl_dir) and os.path.islink(rl_dir):
        os.unlink(rl_dir)
    os.symlink(src_dir, rl_dir)


def fix_dir(dirname, new_release):
    RELDIR = join(BASE_DIR, "release")
    while new_release >= 0:
        for reldir in os.listdir(RELDIR):
            if "release-latest" in reldir:
                continue

            # if the founded directory is the same as
            # release-latest we should continue search
            latest_dir_link = join(LATEST_DIR, dirname)
            if os.path.islink(latest_dir_link):
                original_dir_path = os.readlink(latest_dir_link)
                release_dir = join(RELDIR, reldir, dirname)
                if original_dir_path == release_dir:
                    continue

            dir = join(RELDIR, reldir, dirname, str(new_release))
            if os.path.isdir(dir):
                link_dir(reldir, dirname)
                return True
        new_release -= 1
    return False


def unroll_dir(dirname):
    print "Processing %s" % dirname
    src_dir = join(BASE_DIR, dirname)
    for latest in ('latest.v2', 'latest.v1'):
        path = os.path.join(src_dir, latest)
        if os.path.exists(path):
            with open(path) as f:
                release = f.read()
            break
    else:
        raise IOError('Unable to find release number. File %s not found.'
                      % os.path.join(src_dir, 'latest.vX'))

    new_release = int(release) - 1
    if new_release < 0:
        new_release = 0
        # todo something about it
        #f.write(str(new_release))
    if not fix_dir(dirname, new_release):
        print ("Unable to find previous release for %s level %s"
               % (dirname, new_release))


def unroll_latest():
    for dirname in os.listdir(SOURCE_DIR):
        try:
            if os.path.isdir(join(SOURCE_DIR, dirname)):
                unroll_dir(dirname)
        except:
            traceback.print_exc()
            print SOURCE_DIR

    print "Unroll Done"


def usage():
    print 'Usage: %s [OPTIONS] release_tag' % (sys.argv[0])
    print
    print 'deploys release RELEASE_TAG as latest release'
    print 'patches for the RELEASE_TAG should be located in ' \
          '/usr/share/kcare-eportal/patches/release/RELEASE_TAG'
    print '-p | --production         : deploy to production, otherwise test assumed'
    print '-h | --help               : show this help'
    print '--prefix                  : specify a custom deploy folder'


def main():
    global PRODUCTION
    global BASE_DIR, SOURCE_DIR, LATEST_DIR
    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            'h:p', ['production', 'help', 'prefix='])
    except getopt.GetoptError, e:
        print "ERROR: %s" % (str(e), )
        usage()
        sys.exit(1)

    prefix = ''

    for o, a in opts:
        if o in ['-h', '--help']:
            usage()
            sys.exit(0)
        elif o in ['-p', '--production']:
            PRODUCTION = True
        elif o in ['--prefix']:
            if not a:
                print "--prefix argument is required"
                sys.exit(1)
            prefix = a

    if len(args) != 1:
        print "RELEASE TAG required"
        usage()
        sys.exit(1)

    if not PRODUCTION and not prefix:
        print "--production or --prefix option is required"
        sys.exit(1)

    BASE_DIR = join(BASE_DIR, prefix)
    SOURCE_DIR = join(BASE_DIR, 'release', args[0])
    LATEST_DIR = join(BASE_DIR, 'release', 'release-latest')
    if not os.path.exists(SOURCE_DIR):
        print "%s doesn't exist" % SOURCE_DIR
        sys.exit(1)

    if not os.path.exists(LATEST_DIR):
        try:
            os.makedirs(LATEST_DIR)
        except OSError:
            print "Unable to create %s", LATEST_DIR
            sys.exit(1)

    if PRODUCTION:
        deploy_type = "PRODUCTION"
    else:
        deploy_type = prefix.upper()

    print "Deploying to %s, release %s" % (deploy_type, args[0])
    unroll_latest()


if __name__ == '__main__':
    main()
