#!/usr/bin/python3

# I have to think more before making a python algorithm to know how much it will be used in the future on a regular basis because I've been tricked with the updater.

# Used to use python3 not pypy3 - well in fact pypy3 doesn't seem to support new python3.6 so it's not a good idea to use it because it supports natively (with apt) only no more supported python version
# Could think about trying whether or not it is faster with pypy3

# What happen if user update his game while this script is running?

# Clear `addChangelogs.txt` after update being published.

# Could add a check or automatically change `version `in `LemnosLife/Main/main.cpp`. At least that the executable changed I guess, or can do weird stuff with dumping its strings.

# Make a process to revert an update, maybe.

import time
import os
import zipfile
import shutil
import filecmp
import tempfile
import datetime
import sys
from distutils.dir_util import copy_tree

begin = time.time()

path = '/var/www/html/MAJ/'
updateMAJInfo = True
if len(sys.argv) >= 2:
    updateMAJInfo = sys.argv[1] != 'noUpdateMAJInfo'

versions = []

for _, d, _ in os.walk(path):
    #print(d)
    for dEl in d:
        if dEl.count('.') == 2:
            #print(dEl)
            dElNbStr = dEl.replace('.', '')
            dElNb = int(dElNbStr)
            versions += [dElNb]
    break

def nbToStr(version):
    versionStr = str(version)
    res = '.'.join(versionStr)
    return res

versions = sorted(versions)
#print(versions)
currentVersion = nbToStr(versions[-1])
newVersion = nbToStr(versions[-1] + 1)

#print(currentVersion, newVersion)

#exit(2)

dry = False
needDebug = True

lastTime = time.time()
def printWithTime(obj):
    global lastTime
    currentTime = time.time()
    delta = currentTime - lastTime
    lastTime = currentTime
    now = datetime.datetime.now()
    deltaStr = str(round(delta, 2))
    if deltaStr[-2:] == '.0':
        deltaStr = deltaStr[:-2]
    print(f'{now.strftime("%H:%M:%S")} {deltaStr}s {obj}')

def debug(obj):
    if needDebug:
        printWithTime(obj)

#debug('Test')

#exit(5)

def zipObj(path, mode, zipMode):
    return zipfile.ZipFile(path, mode, zipMode, compresslevel=9) # CHANGE FOR 9 AT THE END
# less compressing make zip and unzip faster, should find a good equilibrium 6 is good according to documentation

# Could think about using threads...

os.chdir(path)

# `newVersion` folder deletion etc used to be here.

def zipdir(path, ziph):
    for root, dirs, files in os.walk(path):
        #print(path, root)
        #exit(0)
        rootInitial = root
        for dir in dirs:
            root = rootInitial
            #print(f'Initial root: {root}!')
            '''if root[:2] == './':
                root = root[2:] + '/'
            else:
                root = '' '''
            root = root.replace('/var/www/html/MAJ/new/', '')
            if root != '':
                root += '/'
            #print(f'Root res: {root}!')
            folder = root + dir + '/'
            #print(f'Foldering: {folder}')
            zfi = zipfile.ZipInfo(folder)
            ziph.writestr(zfi, '')
        root = rootInitial
        for file in files:
            first = os.path.join(root, file)
            # Replace `4`.
            second = os.path.relpath(os.path.join(root, file), os.path.join(path, '..'))[4:] # to remove new/
            #print(first, second)
            ziph.write(first, second)

def getFiles(path):
    l = []
    pathLen = len(path)
    for r, _, files in os.walk(path):
        r = r[pathLen:]
        if r != '':
            r += '/'
        #print(r)
        for file in files:
            filePath = r + file
            #print(filePath)
            l += [filePath]
    return l

debug('Basic stuff done.')

newFiles = getFiles('new/')
#for newFile in newFiles:
#    print(newFile)
currentFiles = getFiles('current/')

realNewFiles = []
for newFile in newFiles:
    if not newFile in currentFiles:
        printWithTime('New file: ' + newFile)
        realNewFiles += [newFile]
    else:
        # shallow (third argument is important (by default only work on metadata)).
        if not filecmp.cmp('new/' + newFile, 'current/' + newFile, shallow=True):
            printWithTime('Different: ' + newFile)
            realNewFiles += [newFile]

debug('Cross files done.')

if realNewFiles == []:
    print('No modified file found, aborting!')
    exit(1)

if os.path.isdir(newVersion):
    shutil.rmtree(newVersion)
os.mkdir(newVersion)
newVersionFolder = newVersion + '/'
with open(newVersionFolder + 'changelogs.txt', 'w') as f:
    f.write('WithZIP')

def updateZip(zipname, filenames, data):
    # Generate a temporary file.
    tmpfd, tmpname = tempfile.mkstemp(dir=os.path.dirname(zipname))
    os.close(tmpfd)

    # Create a temporary copy of the archive without filename.
    with zipfile.ZipFile(zipname) as zin:
        with zipfile.ZipFile(tmpname, 'w') as zout:
            #zout.comment = zin.comment # Preserve the comment.
            for item in zin.infolist():
                if not item.filename in filenames:
                    zout.writestr(item, zin.read(item.filename))

    # Replace with the temporary archive.
    os.remove(zipname)
    os.rename(tmpname, zipname)

    # Now add filename with its new data.
    # with zipfile.ZipFile(zipname, mode='a', compression=zipfile.ZIP_DEFLATED) as zf:
    #     zf.writestr(filename, data)

foldersVersions = []

for r, _, _ in os.walk('.'):
    if r.count('/') == 1:
        r = r[2:]
        rParts = r.split('.')
        rPartsLen = len(rParts)
        if rPartsLen == 3 and r != newVersion:
            foldersVersions += [r]

versions = []

for foldersVersion in foldersVersions:
    versions += [int(foldersVersion.replace('.', ''))]

#print(versions)
versions = list(reversed(sorted(versions)))
#print(versions)
#exit(3)

foldersVersions = []
for version in foldersVersions:
    foldersVersions += ['.'.join(list(str(version)))]
    
#print(foldersVersions)
#exit(4)

if True:#False:
    for r, _, files in os.walk('.'):
        #print(r)
        if r.count('/') == 1:
            r = r[2:]
            #print(r)
            rParts = r.split('.')
            rPartsLen = len(rParts)
            if rPartsLen == 3 and r != newVersion:# and r != currentVersion: # r[:2] == '1.'
                printWithTime(r)
                changesFile = r + '/changes.zip'
                if r != currentVersion:
                    with zipfile.ZipFile(changesFile) as zip:
                        zipFiles = zip.namelist()
                    filesToRemove = []
                    for realNewFile in realNewFiles:
                        if realNewFile in zipFiles:#zip.is_zipfile(realNewFile))
                            filesToRemove += [realNewFile]
                    updateZip(changesFile, filesToRemove, '')
                with zipObj(changesFile, 'a' if r != currentVersion else 'w', zipfile.ZIP_DEFLATED) as zip:
                    for realNewFile in realNewFiles:
                        zip.write('new/' + realNewFile, realNewFile)
                #pass

if not dry:
    for r in foldersVersions:
        printWithTime(r)
        changesFile = r + '/changes.zip'
        if r != currentVersion:
            with zipfile.ZipFile(changesFile) as zip:
                zipFiles = zip.namelist()
            filesToRemove = []
            for realNewFile in realNewFiles:
                if realNewFile in zipFiles:
                    filesToRemove += [realNewFile]
            updateZip(changesFile, filesToRemove, '')
        with zipObj(changesFile, 'a' if r != currentVersion else 'w', zipfile.ZIP_DEFLATED) as zip:
            for realNewFile in realNewFiles:
                zip.write('new/' + realNewFile, realNewFile)

debug('Intermediary version done.')

if not dry:
    with zipObj(newVersion + '/changes.zip', 'w', zipfile.ZIP_DEFLATED) as zipf:
        zipdir(path + 'new/', zipf)

debug('Last version done.')

# Do the following at the end when the update is uploaded for users but can still manage backend not directly use data by players after.

# Should add `if not dry:` it seems.
with open('latest.txt', 'w') as f:
    f.write(newVersion)

MAJ_ENCODING = 'UTF-8'#'iso-8859-1'

if updateMAJInfo:
    with open('addChangelogs.txt', encoding=MAJ_ENCODING) as f:
        lines = f.readlines()
    for linesIndex, line in enumerate(lines):
        lines[linesIndex] = '- ' + line

    x = datetime.datetime.now()

    firstLine = f'Alpha-{newVersion} ({x.strftime("%d/%m/%y à %Hh%M")})\n'
    lines.insert(0, firstLine)
    addChangelogs = ''.join(lines)
    with open('changelogs.txt', encoding=MAJ_ENCODING) as f: # utf-8 doesn't work for SDL
        lines = f.readlines()
    linesStr = addChangelogs + '\n' + ''.join(lines)
    #print(linesStr)
    with open('changelogs.txt', 'w', encoding=MAJ_ENCODING) as f:
        f.write(linesStr)

debug('Final declaration done.')

# Really need new and current folders? Yes `filecmp` doesn't work in the zip for instance.

if not dry:
    shutil.rmtree('current/')
    copy_tree('new/', 'current/')

debug('Post declaration backend stuff done.')
print('Total time (s) spent: ' + str(round(time.time() - begin, 2)))
