#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#**************************************************************************
#*   Copyright (C) 2017 by S. MANKOWSKI / G. DE BURE support@mankowski.fr
#*   Redistribution and use in source and binary forms, with or without
#*   modification, are permitted provided that the following conditions
#*   are met:
#*   
#*   1. Redistributions of source code must retain the above copyright
#*      notice, this list of conditions and the following disclaimer.
#*   2. Redistributions in binary form must reproduce the above copyright
#*      notice, this list of conditions and the following disclaimer in the
#*      documentation and/or other materials provided with the distribution.
#*   
#*   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
#*   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
#*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
#*   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
#*   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
#*   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#*   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#*   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
#*   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#**************************************************************************

import argparse
import datetime
import os
import shutil
import subprocess
import sys
import fileinput

__VERSION__ = '1.0.0'

toolPath=os.path.dirname(os.path.realpath(__file__))
localPath=os.path.dirname(toolPath)
tempDir='/data'

class Releasor(object):
    def __init__(self, args):
        self.ubuntuVersions=['focal', 'groovy', 'hirsute']
        self.workdingDir=os.path.join(tempDir, 'skrooge-release_' + args.version)
        print("# Working directory :" + self.workdingDir)    
        if args.version.endswith(".0") or args.stable:
            self.ppa = "ppa"
            self.ppatotreat = ["beta", self.ppa]            
        else:
            self.ppa = "beta"
            self.ppatotreat = [self.ppa]
            
        if os.path.exists(self.workdingDir):
            self.logfile = open(os.path.join(self.workdingDir, 'log.txt'), 'w')
        else:
            self.logfile = None

    def prepareWorkingDirectory(self, args):
        print('# Prepare the working directory')
        if os.path.exists(self.workdingDir):
            print('#   Remove '+self.workdingDir)
            shutil.rmtree(self.workdingDir)
        os.mkdir(self.workdingDir)
        os.chdir(self.workdingDir)  
        
        self.logfile = open(os.path.join(self.workdingDir, 'log.txt'), 'w')
        print('#   DONE')
        return 0
        
    def makeTarFile(self, args):
        rc = 0
        if args.fromtar:
            print('# Make the tar file from '+args.fromtar)
            dst = os.path.join(self.workdingDir, 'skrooge-'+args.version+'.tar.xz')
            shutil.copyfile(args.fromtar, dst)
            
            print('#   DONE')
        else:
            print('# Make the tar file')
            cmd = ['git', 'clone', 'https://github.com/KDE/releaseme.git']
            self.logfile.write('### '+' '.join(cmd)+'\n')                
            rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
            if rc == 0:
                cmd = ['releaseme/tarme.rb', '--version', args.version, '--origin', 'trunk', 'skrooge']
                self.logfile.write('### '+' '.join(cmd)+'\n')                
                rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
                
                shutil.rmtree(os.path.join(self.workdingDir, 'skrooge-'+args.version))
                shutil.rmtree('releaseme')
                os.remove(os.path.join(self.workdingDir, 'release_data'))
                sig_file = os.path.join(self.workdingDir, 'skrooge-'+args.version+'.tar.xz.sig')
                if os.path.exists(sig_file):
                    os.remove(sig_file)
            print('#   '+("DONE" if rc == 0 else "FAILED"))
        return rc
    
    def updateTarFile(self, args):
        print('# Update the tar file')
        os.chdir(self.workdingDir)  
        p = 'skrooge-'+args.version
        if os.path.exists(p):
            shutil.rmtree(p)
        tarfile = 'skrooge-'+args.version+'.tar.xz'
        print('#   Untar '+tarfile)
        cmd = ['tar', '-xvf', tarfile]
        self.logfile.write('### '+' '.join(cmd)+'\n')                
        rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
        if rc == 0:
            print('#   Change the CMakeLists.txt')
            with open(os.path.join(p, 'CMakeLists.txt'), 'r') as fileCMakeLists:
                CMakeListsContent = ''
                for line in fileCMakeLists:
                    if line.startswith('SET(SKG_VERSION'):
                        print('#     SKG_VERSION changes to "'+args.version+'"')
                        CMakeListsContent += 'SET(SKG_VERSION "'+args.version+'")\n'              
                    else:
                        if line.startswith('SET(SKG_BETA'):
                            bb = 'BETA' if self.ppa == "beta" else ''
                            print('#     SKG_BETA changes to "'+bb+'"')
                            CMakeListsContent += 'SET(SKG_BETA "'+bb+'")\n'              
                        else:
                            if line.startswith('FEATURE_SUMMARY'):
                                CMakeListsContent += line
                                break
                            else:
                                CMakeListsContent += line     
            with open(os.path.join(p, 'CMakeLists.txt'), 'w') as fileCMakeLists:
                fileCMakeLists.write(CMakeListsContent)
                
            #for line in fileinput.input(['skrooge/org.kde.skrooge.appdata.xml'], inplace=True):
            #    print(line.replace('<releases>', '<releases>\n<release version="'+args.version+'" date="'+datetime.datetime.today().strftime('%Y-%m-%d')+'"/>'), end='')
            
            print('#   Build the new splash screen')
            buildPath = os.path.join(p, 'build')
            os.mkdir(buildPath)
            os.chdir(buildPath) 
            cmd = ['cmake', '..', '-DCMAKE_INSTALL_PREFIX=`kf5-config --prefix`', '-DQT_PLUGIN_INSTALL_DIR=`kf5-config --qt-plugins`']
            self.logfile.write('### '+' '.join(cmd)+'\n')
            rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
            if rc == 0:    
                cmd = ['make', 'splash']
                self.logfile.write('### '+' '.join(cmd)+'\n')                
                rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
                if rc == 0:                   
                    print('#   Create the tar file')
                    os.chdir(self.workdingDir)  
                    shutil.rmtree(buildPath)
                    os.remove(tarfile)
                    cmd = ['tar', '-cJf', tarfile, p]
                    rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
                    self.logfile.write('### '+' '.join(cmd)+'\n')                
                    if rc == 0:
                        cmd = ['gpg2', '--armor', '--detach-sig', '-o', tarfile+'.sig', tarfile]
                        self.logfile.write('### '+' '.join(cmd)+'\n')                
                        rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)                                  
                        if rc == 0:
                            print("Skrooge "+args.version+" released\n\nHi,\n\nCould you publish the following files in skrooge/"+("stable" if args.stable else "unstable")+"?\n")
                            cmd = ['sha256sum', tarfile]
                            self.logfile.write('### '+' '.join(cmd)+'\n')                
                            rc = subprocess.call(cmd)  
                            if rc == 0:
                                cmd = ['sha256sum', tarfile+'.sig']
                                self.logfile.write('### '+' '.join(cmd)+'\n')                
                                rc = subprocess.call(cmd)  
                            print("Thank you.\nRegards.\n"   )                             
        if rc == 0:
            shutil.rmtree(p)
        print('#   '+("DONE" if rc == 0 else "FAILED"))
        return rc    
    
    def modify(self, args):
        print('# Get dsc')
        os.chdir(self.workdingDir)  
        cmd = ['apt', 'source', 'skrooge']
        self.logfile.write('### '+' '.join(cmd)+'\n')                
        rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
        if rc == 0:
            previousPackage = [os.path.join(self.workdingDir, o) for o in os.listdir(self.workdingDir) if os.path.isdir(os.path.join(self.workdingDir, o)) and o.startswith('skrooge-')][0]
            print('#   previousPackage='+previousPackage)
            
            os.chdir(previousPackage)  
            if os.path.exists('debian'):
                shutil.rmtree('debian')
            cmd = ['tar', '-xvf', os.path.join(toolPath, 'skrooge-release-debian.tar.gz')]
            self.logfile.write('### '+' '.join(cmd)+'\n')                
            rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
            if rc == 0:
                cmd = ['uupdate', '-u', 'skrooge-'+args.version+'.tar.xz']
                self.logfile.write('### '+' '.join(cmd)+'\n')                
                rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
                if rc == 0:
                    # Read the changelog
                    os.chdir('../skrooge-'+args.version)
                    with open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/CHANGELOG'), 'r') as fileChangelog:
                        fileChangelog.readline() # To pass the first line
                        changelogContent = ''
                        done = False
                        for line in fileChangelog:
                            if line.strip() == '' and done == False:
                                changelogContent += ' -- Stephane MANKOWSKI (Perso) <stephane@mankowski.fr>  {}\n'.format(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S +0100'))
                                done = True
                            if not line.startswith(' -- '):
                                changelogContent += line  
                        
                        #
                        for ppa in self.ppatotreat:
                            for i in range(len(self.ubuntuVersions)):
                                print('#   {}/{}: {} - {}'.format(i+1, len(self.ubuntuVersions), self.ubuntuVersions[i], ppa))
                                f = open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/debian/control'), 'r')
                                cf = f.read()
                                f.close()
                                
                                #print('#   make changes for '+self.ubuntuVersions[i])
                                #f = open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/debian/control'), 'w')
                                #f.write(cf.replace('libqjson-dev, \n', '').replace('libqjson0, ', ''))
                                #f.close()                                 
                                    
                                with open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/debian/changelog'), 'w') as fileDebianChangelog:
                                    fileDebianChangelog.write('skrooge ('+args.version+'-0ubuntu1~'+ppa+str(i+1)+') '+self.ubuntuVersions[i]+'; urgency=medium\n')
                                    fileDebianChangelog.write(changelogContent+'\n')
                                    
                                    cmd = ['debuild', '-S', '-sa']
                                    self.logfile.write('### '+' '.join(cmd)+'\n')                
                                    rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)       
                                    if rc!=0:
                                        break
                            if rc!=0:
                                break                                    
        print('#   '+("DONE" if rc == 0 else "FAILED"))
        return rc      
    
    def publishLaunchpad(self, args):
        print('# Publish on launchpad')
        rc = 0
        if not args.publish:
            print('#   Publication ignored')
        os.chdir(self.workdingDir)  
        for ppa in self.ppatotreat:
            for i in range(len(self.ubuntuVersions)):
                print('#   {}/{}: {} {}'.format(i+1, len(self.ubuntuVersions), self.ubuntuVersions[i], ppa))
                cmd = ['dput', '-f', 'ppa:s-mankowski/'+ppa+'-kf5', 'skrooge_'+args.version+'-0ubuntu1~'+ppa+str(i+1)+'_source.changes']
                if not args.publish:
                    print('#     '+' '.join(cmd)+'\n')
                else:
                    self.logfile.write('### '+' '.join(cmd)+'\n')                
                    rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)       
                    if rc!=0:
                        break
        print('#   '+("DONE" if rc == 0 else "FAILED"))
        return rc     
    
    def publishKDE(self, args):
        print('# Publish on kde')
        rc = 0
        if not args.publish:
            print("#   Publication ignored")
        os.chdir(self.workdingDir)  
        cmd = ['kdecp5', 'skrooge-'+args.version+'.tar.xz', 'ftp://upload.kde.org/incoming']
        if not args.publish:
            print('#     '+' '.join(cmd)+'\n')
        else:
            self.logfile.write('### '+' '.join(cmd)+'\n')                
            rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)       
            if rc == 0:
                self.logfile.write('### '+' '.join(cmd)+'\n')                
                cmd = ['kdecp5', 'skrooge-'+args.version+'.tar.xz.sig', 'ftp://upload.kde.org/incoming']
                if not args.publish:
                    print('#     '+' '.join(cmd)+'\n')
                else:
                    self.logfile.write('### '+' '.join(cmd)+'\n')                
                    rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)
        print('#   '+("DONE" if rc == 0 else "FAILED"))
        return rc    
    
    def buildAppImage(self, args):
        print('# Generate App Image')
        os.chdir(self.workdingDir)  
        targteappimage = os.path.join(self.workdingDir, "skrooge-"+args.version+'-x86_64.AppImage')
        if os.path.exists(targteappimage):
            os.remove(targteappimage)  
            
        cmd = ['wget', '-c', '-nv', 'https://raw.githubusercontent.com/probonopd/AppImages/master/pkg2appimage']
        self.logfile.write('### '+' '.join(cmd)+'\n')                
        rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)       
        if rc == 0:
            for line in fileinput.input(['pkg2appimage'], inplace=True):
                if(not 'libcurl-slim' in line):
                    print(line, end='')
                
            os.chmod('./pkg2appimage', 0o775)
                
            if args.fromlocal:
                print('#   Get appimage.yml from local path')
                cmd = ['cp', os.path.join(localPath, 'appimage.yml'), '.']
            else:
                cmd = ['wget', '-c', '-nv', 'https://invent.kde.org/office/skrooge/-/raw/master/appimage.yml']
            self.logfile.write('### '+' '.join(cmd)+'\n')                
            rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)       
            if rc == 0:
                if not args.stable:
                    for line in fileinput.input(['appimage.yml'], inplace=True):
                        print(line.replace('ppa-kf5', 'beta-kf5'), end='')
                    
                cmd = ['./pkg2appimage', 'appimage.yml']
                self.logfile.write('### '+' '.join(cmd)+'\n')                
                rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) 
                if rc == 0:                          
                    appimage_file = [os.path.join(self.workdingDir, "out/"+o) for o in os.listdir("./out") if o.endswith('.AppImage')][0]
                    
                    shutil.move(appimage_file, targteappimage)
        print('#   '+("DONE" if rc == 0 else "FAILED"))
        return rc      
    
    def buildSnap(self, args):
        print('# Generate Snap')
        os.chdir(self.workdingDir)  
        targteappimage = os.path.join(self.workdingDir, "skrooge_"+args.version+'_amd64.snap')
        if os.path.exists(targteappimage):
            os.remove(targteappimage)  
            
        if args.fromlocal:
            print('#   Get snapcraft.yaml from local path')
            cmd = ['cp', os.path.join(localPath, 'snapcraft.yaml'), '.']
        else:
            cmd = ['wget', '-c', '-nv', 'https://invent.kde.org/office/skrooge/-/raw/master/snapcraft.yaml']
        self.logfile.write('### '+' '.join(cmd)+'\n')                
        rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile)       
        if rc == 0:
            for line in fileinput.input(['snapcraft.yaml'], inplace=True):
                print(line.replace('version: "X.X.X"', 'version: "'+args.version+'"').replace('source: XXX', 'source: '+localPath if args.fromlocal else 'source: https://invent.kde.org/office/skrooge.git'), end='')
                
            cmd = ['snapcraft']
            self.logfile.write('### '+' '.join(cmd)+'\n')                
            rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) 
        print('#   '+("DONE" if rc == 0 else "FAILED"))
        
        # TO PUBLISH https://docs.snapcraft.io/build-snaps/c
        #      snapcraft push --release=edge skrooge_*.snap
        return rc       

def main():
    parser = argparse.ArgumentParser(prog='skrooge-release', description='skrooge release maker')

    # Global arguments
    parser.add_argument('--version', required=True,  help='The release version')
    parser.add_argument('--fromtar', required=False,  help='To copy an existing tar file instead of building it from git')
    parser.add_argument('--pwd', required=False,  help='The password')
    parser.add_argument('--stable', action='store_true',  help='To define this version as a master version')
    parser.add_argument('--publish', action='store_true',  help='To publish on launchpad and KDE')
    parser.add_argument('--appimage', action='store_true',  help='To generate the appimage only')
    parser.add_argument('--snap', action='store_true',  help='To generate the snap only')    
    parser.add_argument('--fromlocal', action='store_true',  help='To generate The snap and the appimage from local path (' + localPath + ')')    
    args = parser.parse_args()
    
    print("#####################")
    print("# Launching release #")
    print("#####################")
    print("# Version         :" + args.version)
    print("# Stable          :" + ("Y" if args.stable else "N"))
    print("# Publish         :" + ("Y" if args.publish else "N"))
    print("# Appimage        :" + ("Y" if args.appimage else "N"))    
    print("# Snap            :" + ("Y" if args.snap else "N"))
    if args.fromlocal:
        print("# From local path :" + localPath)    
    # Launch the release
    r = Releasor(args)
    rc = 0
    rc=r.prepareWorkingDirectory(args)
    if(rc == 0 and not (args.appimage or args.snap)): rc=r.makeTarFile(args)
    if(rc == 0 and not (args.appimage or args.snap)): rc=r.updateTarFile(args)
    if(rc == 0 and not (args.appimage or args.snap)): rc=r.modify(args)
    if(rc == 0 and not (args.appimage or args.snap)): rc=r.publishLaunchpad(args)
    if(rc == 0 and not (args.appimage or args.snap)): rc=r.publishKDE(args)
    if(rc == 0 and args.appimage): rc=r.buildAppImage(args)
    if(rc == 0 and args.snap): rc=r.buildSnap(args)   
    print("#####################")
    print("# End of release    #" if rc == 0 else "# FAILURE         #")
    print("#####################")
    return rc


if __name__ == '__main__':
    sys.exit(main())
