import urllib
import zipfile
import os
import sys
import ssl
import httplib
import socket
import re
import json
import cStringIO as StringIO

def download():
    from src.util import helper

    urls = [
        "https://github.com/HelloZeroNet/ZeroNet/archive/master.zip",
        "https://gitlab.com/HelloZeroNet/ZeroNet/repository/archive.zip?ref=master",
        "https://try.gogs.io/ZeroNet/ZeroNet/archive/master.zip"
    ]

    zipdata = None
    for url in urls:
        print "Downloading from:", url,
        try:
            req = helper.httpRequest(url)
            data = StringIO.StringIO()
            while True:
                buff = req.read(1024 * 16)
                if not buff:
                    break
                data.write(buff)
                print ".",
            try:
                zipdata = zipfile.ZipFile(data)
                break  # Success
            except Exception, err:
                data.seek(0)
                print "Unpack error", err, data.read(256)
        except Exception, err:
            print "Error downloading update from %s: %s" % (url, err)

    if not zipdata:
        raise err

    print "Downloaded."

    return zipdata


def update():
    from Config import config
    if getattr(sys, 'source_update_dir', False):
        if not os.path.isdir(sys.source_update_dir):
            os.makedirs(sys.source_update_dir)
        os.chdir(sys.source_update_dir)  # New source code will be stored in different directory

    updatesite_path = config.data_dir + "/" + config.updatesite
    sites_json = json.load(open(config.data_dir + "/sites.json"))
    updatesite_bad_files = sites_json.get(config.updatesite, {}).get("cache", {}).get("bad_files", {})
    print "Update site path: %s, bad_files: %s" % (updatesite_path, len(updatesite_bad_files))
    if os.path.isfile(updatesite_path + "/content.json") and len(updatesite_bad_files) == 0 and sites_json.get(config.updatesite, {}).get("serving"):
        # Update site exists and no broken file
        print "Updating using site %s" % config.updatesite
        zipdata = False
        inner_paths = json.load(open(updatesite_path + "/content.json"))["files"].keys()
        # Keep file only in ZeroNet directory
        inner_paths = [inner_path for inner_path in inner_paths if inner_path.startswith("ZeroNet/")]
    else:
        # Fallback to download
        zipdata = download()
        inner_paths = zipdata.namelist()

    # Checking plugins
    plugins_enabled = []
    plugins_disabled = []
    if os.path.isdir("plugins"):
        for dir in os.listdir("plugins"):
            if dir.startswith("disabled-"):
                plugins_disabled.append(dir.replace("disabled-", ""))
            else:
                plugins_enabled.append(dir)
        print "Plugins enabled:", plugins_enabled, "disabled:", plugins_disabled

    print "Extracting to %s..." % os.getcwd(),
    for inner_path in inner_paths:
        if ".." in inner_path:
            continue
        inner_path = inner_path.replace("\\", "/")  # Make sure we have unix path
        print ".",
        dest_path = re.sub("^([^/]*-master.*?|ZeroNet)/", "", inner_path)  # Skip root zeronet-master-... like directories
        dest_path = dest_path.lstrip("/")
        if not dest_path:
            continue

        # Keep plugin disabled/enabled status
        match = re.match("plugins/([^/]+)", dest_path)
        if match:
            plugin_name = match.group(1).replace("disabled-", "")
            if plugin_name in plugins_enabled:  # Plugin was enabled
                dest_path = dest_path.replace("plugins/disabled-" + plugin_name, "plugins/" + plugin_name)
            elif plugin_name in plugins_disabled:  # Plugin was disabled
                dest_path = dest_path.replace("plugins/" + plugin_name, "plugins/disabled-" + plugin_name)
            print "P",

        dest_dir = os.path.dirname(dest_path)

        if dest_dir and not os.path.isdir(dest_dir):
            os.makedirs(dest_dir)

        if dest_dir != dest_path.strip("/"):
            if zipdata:
                data = zipdata.read(inner_path)
            else:
                data = open(updatesite_path + "/" + inner_path, "rb").read()

            try:
                open(dest_path, 'wb').write(data)
            except Exception, err:
                print dest_path, err

    print "Done."
    return True


if __name__ == "__main__":
    import sys
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))  # Imports relative to src

    from gevent import monkey
    monkey.patch_all()

    from Config import config
    config.parse(silent=True)

    from src.util import SslPatch

    try:
        update()
    except Exception, err:
        print "Update error: %s" % err
    raw_input("Press enter to exit")
