import json, sys, requests, time, os, platform, traceback
from io import BytesIO
try:
    import Cocoa # mac
except: pass

from PyQt4 import QtGui, QtCore

from constants import *

returncode = -1
assetfile = {
    "Windows": "AO2XP-Windows.zip",
    "Darwin": "AO2XP-macOS.zip",
    "Linux": "AO2XP-Linux.tar.gz"
}

def checkForUpdates(force=False):
    update_dialog = QtGui.QProgressDialog()
    update_dialog.setWindowTitle("AO2XP updater")
    update_dialog.setAutoClose(False)
    update_dialog.setAutoReset(False)
    update_dialog.setLabelText("Checking for client updates...")
    update_dialog.resize(512, 96)
    update_dialog.setMinimum(0)
    update_dialog.setMaximum(0)
    update_dialog.setValue(0)
    
    thr = updateThread(update_dialog, force)

    def setProgressValue(value):
        update_dialog.setValue(value)
    def setLabelText(msg):
        update_dialog.setLabelText(msg)
    def showMessageBox(icon, title, msg):
        getattr(QtGui.QMessageBox, str(icon))(None, title, msg)
    def updateAvailableCall(name, version, body):
        updateWindow = updateAvailableWidget(name, version, body)
        result = updateWindow.show()
        thr.waitConfirm = 1 if result else -1
        if not result:
            update_dialog.close()
            returncode = -2
        else:
            update_dialog.setMinimum(0)
            update_dialog.setMaximum(100)

    thr.progressValue.connect(setProgressValue)
    thr.labelText.connect(setLabelText)
    thr.showMessageBox.connect(showMessageBox)
    thr.updateAvailable.connect(updateAvailableCall)
    thr.finished.connect(update_dialog.close)
    thr.start()

    update_dialog.exec_()
    if not update_dialog.wasCanceled() and thr.isRunning(): thr.wait()

    print "update return code:", returncode
    return returncode

class updateAvailableWidget(QtGui.QDialog):
    def __init__(self, title, version, body):
        super(self.__class__, self).__init__()
        self.layout = QtGui.QVBoxLayout(self)
        self.layout.setAlignment(QtCore.Qt.AlignTop)

        self.available = QtGui.QLabel(text="Changelog for update \"%s\":" % title)
        self.changelog = QtGui.QTextEdit()
        self.confirmtext = QtGui.QLabel(text="Do you want to download and install the update?")
        self.yesnolayout = QtGui.QHBoxLayout()
        self.yes = QtGui.QPushButton(text="Yes")
        self.no = QtGui.QPushButton(text="No")

        self.setModal(True)
        self.setWindowTitle("Update %s available" % version)
        self.changelog.setText(body)
        self.changelog.setReadOnly(True)
        self.yes.clicked.connect(self.confirm)
        self.no.clicked.connect(self.cancel)

        self.yesnolayout.addWidget(self.yes)
        self.yesnolayout.addWidget(self.no)
        self.layout.addWidget(self.available)
        self.layout.addWidget(self.changelog)
        self.layout.addWidget(self.confirmtext)
        self.layout.addLayout(self.yesnolayout, QtCore.Qt.AlignBottom)
        self.result = False

        self.resize(640, self.sizeHint().height())

    def confirm(self):
        self.close()
        self.result = True

    def cancel(self):
        self.close()
        self.result = False

    def show(self):
        super(self.__class__, self).show()
        self.exec_()
        return self.result

class updateThread(QtCore.QThread):
    progressValue = QtCore.pyqtSignal(int)
    labelText = QtCore.pyqtSignal(str)
    showMessageBox = QtCore.pyqtSignal(str, str, str)
    updateAvailable = QtCore.pyqtSignal(str, str, str)
    finished = QtCore.pyqtSignal()
    waitConfirm = 0
    
    def __init__(self, nouis, force):
        super(self.__class__, self).__init__()
        self.jm = nouis
        self.force = force

    def run(self):
        global returncode

        try:
            manifest = json.load(BytesIO(requests.get("").content))
            #manifest = json.load(BytesIO(requests.get("http://api.github.com/repos/headshot2017/AO2XP/releases").content))
        except:
            print traceback.format_exc()
            self.showMessageBox.emit("critical", "Error", "Failed to check for updates.\nPlease check your internet connection.")
            self.finished.emit()
            return

        if self.jm.wasCanceled():
            return

        if GAME_VERSION != manifest[0]["tag_name"] or self.force: # update available
            self.updateAvailable.emit(manifest[0]["name"], manifest[0]["tag_name"], manifest[0]["body"])
            self.waitConfirm = 0

            while self.waitConfirm == 0: pass

            if self.waitConfirm > 0:
                resume_bytes = 0
                filename = assetfile[platform.system()]
                link = ""
                for asset in manifest[0]["assets"]:
                    if asset["name"] == filename:
                        link = asset["browser_download_url"]

                if not link:
                    self.showMessageBox.emit("critical", "Error", "Release %s is missing the '%s' file.\nCannot continue updating." % (manifest[0]["tag_name"], filename))
                    self.finished.emit()
                    return

                path = sys.argv[0]
                if platform.system() == "Darwin":
                    path = Cocoa.NSBundle.mainBundle().bundlePath()
                    os.chdir(os.path.split(path)[0]) # right next to the .app package

                updatezip = "update" + os.path.splitext(filename)[1]
                if not os.path.exists(updatezip):
                    downloadfile = open(updatezip, "wb")
                else:
                    existing_data = open(updatezip, "rb").read()
                    downloadfile = open(updatezip, "ab")
                    resume_bytes = len(existing_data)
                    print resume_bytes
                    del existing_data

                self.labelText.emit("Downloading '%s' for update '%s'..." % (filename, manifest[0]["name"]))
                dl = resume_bytes
                speed = 0.0
                start = time.clock()
                calcspeed_time = time.time()
                zip = requests.get(link, stream=True, headers={"Range": "bytes=%d-" % resume_bytes})
                length = resume_bytes + int(zip.headers.get("content-length"))

                for noby in zip.iter_content(chunk_size=4096):
                    if not self.jm.isVisible():
                        downloadfile.close()
                        return

                    downloadfile.write(noby)
                    dl += len(noby)
                    percent = 100 * dl / length
                    if percent != self.jm.value():
                        self.progressValue.emit(percent)
                        self.labelText.emit("Downloading '%s' for update '%s'... %.1f KB/s" % (filename, manifest[0]["name"], speed))
                    
                    if (time.time() - calcspeed_time) >= 0.5:
                        calcspeed_time = time.time()
                        speed = ((dl-resume_bytes)/(time.clock() - start)) / 1024.
                        self.labelText.emit("Downloading '%s' for update '%s'... %.1f KB/s" % (filename, manifest[0]["name"], speed))

                print "downloaded update"
                downloadfile.close()
                if platform.system() == "Darwin":
                    self.labelText.emit("Making a backup of AO base folder...")
                    path = Cocoa.NSBundle.mainBundle().resourcePath()
                    os.system("cp -r "+path+"/base ./appbase")  # move the base folder out of the app folder to avoid deletion
                returncode = 0
        else:
            returncode = -2

        self.finished.emit()