from __future__ import with_statement

import logging
import os
import platform
import subprocess
import sys
import tempfile

from abl.util import memoized


logger = logging.getLogger(__name__)


class XDeltaApplicationError(Exception):
    pass


@memoized()
def xdelta_command():
    xdelta3 = "xdelta3"
    if platform.system() == "Windows":
        xdelta3 += ".exe"

    return os.path.join(os.path.dirname(sys.executable), xdelta3)


def create_xdelta(old, new, xdelta):

    old_temp = new_temp = xdelta_temp = None
    fileargs = []
    try:
        if old.scheme == "memory":
            old_temp = tempfile.mktemp()
            with old.open("rb") as inf:
                with open(old_temp, "wb") as outf:
                    outf.write(inf.read())
            fileargs.append(old_temp)
        else:
            fileargs.append(str(old))

        if new.scheme == "memory":
            new_temp = tempfile.mktemp()
            with new.open("rb") as inf:
                with open(new_temp, "wb") as outf:
                    outf.write(inf.read())
            fileargs.append(new_temp)
        else:
            fileargs.append(str(new))

        if xdelta.scheme == "memory":
            xdelta_temp = tempfile.mktemp()
            fileargs.append(xdelta_temp)
        else:
            fileargs.append(str(xdelta))

        cmd = [xdelta_command(), "-e", "-s"] + fileargs
        logger.debug("creating xdelta with command")
        logger.debug(" ".join(cmd))
        subprocess.call(cmd)

        if xdelta_temp:
            with open(xdelta_temp, "rb") as inf:
                with xdelta.open("wb") as outf:
                    outf.write(inf.read())

    finally:
        for fn in [old_temp, new_temp, xdelta_temp]:
            if fn is None:
                continue
            os.remove(fn)


def apply_xdelta(old, xdelta, new):

    old_temp = new_temp = xdelta_temp = None
    fileargs = []
    try:
        if old.scheme == "memory":
            old_temp = tempfile.mktemp()
            with old.open("rb") as inf:
                with open(old_temp, "wb") as outf:
                    outf.write(inf.read())
            fileargs.append(old_temp)
        else:
            fileargs.append(str(old))

        if xdelta.scheme == "memory":
            xdelta_temp = tempfile.mktemp()
            with xdelta.open("rb") as inf:
                with open(xdelta_temp, "wb") as outf:
                    outf.write(inf.read())
            fileargs.append(xdelta_temp)
        else:
            fileargs.append(str(xdelta))

        if new.scheme == "memory":
            new_temp = tempfile.mktemp()
            fileargs.append(new_temp)
        else:
            fileargs.append(str(new))

        cmd = [xdelta_command(), "-d", "-s"] + fileargs
        logger.debug("applying xdelta with command")
        logger.debug(" ".join(cmd))
        retcode = subprocess.call(cmd)
        if retcode:
            raise XDeltaApplicationError

        if new_temp:
            with open(new_temp, "rb") as inf:
                with new.open("wb") as outf:
                    outf.write(inf.read())

    finally:
        for fn in [old_temp, new_temp, xdelta_temp]:
            if fn is None or not os.path.exists(fn):
                continue
            os.remove(fn)
