from __future__ import with_statement

import argparse
import logging
import operator
import os
import re
import shutil
import sys
import tempfile
import zipfile

from datetime import datetime

from abl.installer.delta import DeltaBuilder
from abl.installer.util import relpath
from abl.util import partition
from abl.vpath.base import URI


logger = logging.getLogger("abl.installer")


def delta_creator(args=None):
    """
    Entry-point for console-script. Only usable inside venv for development.
    """
    parser = argparse.ArgumentParser(description="Ableton Update Delta File Creator")

    parser.add_argument(
        "--loglevel",
        help="Loglevel to use. Valid values DEBUG, INFO, ERROR",
        default="ERROR",
    )

    parser.add_argument(
        "-f", "--from", required=True, help="The older Live Version", dest="from_"
    )
    parser.add_argument("-t", "--to", required=True, help="The new Live Version")
    parser.add_argument(
        "--name", required=True, help="The name to use for the delta-file"
    )
    parser.add_argument(
        "--use-xdelta", help="Use xdelta to compress", default=False, action="store_true"
    )

    args = parser.parse_args(sys.argv[1:] if args is None else args)

    logging.basicConfig(
        level=getattr(logging, args.loglevel),
        stream=sys.stderr,
        format="%(levelname)s:%(asctime)s:%(name)s:%(message)s",
    )

    old_path = URI(os.path.normpath(os.path.expanduser(args.from_)))
    new_path = URI(os.path.normpath(os.path.expanduser(args.to)))

    def error(message):
        logger.error(message)
        sys.exit(1)

    for d in (old_path, new_path):
        assert d.isdir(), "No directory - %s" % str(d)

    now = datetime.now()
    date_time = (now.year, now.month, now.day, now.hour, now.minute, now.second)

    def create_delta(old_path, new_path, delta_basename):
        try:
            # We have to use a unicode path to make Python call the Unicode-aware Windows
            # APIs and write non-ASCII filenames as UTF-8 in the ZIP file.
            delta_path = URI(unicode(tempfile.mkdtemp()))
            db = DeltaBuilder(old_path, new_path)
            db.produce_delta(delta_path, args.use_xdelta)

            zip_name = delta_basename
            logger.info("Zipping delta into '%s'", zip_name)
            zf = zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED)
            for root, dirs, filenames in delta_path.walk(followlinks=False):
                filenames_and_links = list(filenames)
                for dir_ in dirs:
                    if (root / dir_).islink():
                        filenames_and_links.append(dir_)

                for filename in filenames_and_links:
                    source = root / filename
                    if source.islink():
                        content = source.readlink().path
                        # URI is auto-adding a leading "./" to relative paths, this might
                        # break signed apps in MacOS 10.15
                        if content.startswith("./"):
                            content = content[2:]
                    else:
                        with source.open("rb") as inf:
                            content = inf.read()

                    zi = zipfile.ZipInfo(
                        filename=relpath(delta_path, root / filename), date_time=date_time
                    )
                    # alps aren't compressed, everything else is
                    zi.compress_type = (
                        zipfile.ZIP_DEFLATED
                        if os.path.splitext(filename)[1] != ".alp"
                        else zipfile.ZIP_STORED
                    )
                    zi.external_attr = source.info(followlinks=False).mode << 16
                    zf.writestr(zi, content)
            zf.close()
        finally:
            delta_path.remove(recursive=True)

    create_delta(old_path, new_path, args.name)


if __name__ == "__main__":
    delta_creator()
