#
# diffoscope: in-depth comparison of files, archives, and directories
#
# Copyright © 2023 FC Stegerman <flx@obfusk.net>
#
# diffoscope is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# diffoscope is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with diffoscope.  If not, see <https://www.gnu.org/licenses/>.

import os.path
import re
import tempfile
import zipfile

from contextlib import contextmanager

from diffoscope.tools import tool_required
from diffoscope.difference import Difference

from .utils.command import Command
from .utils.file import File

# The binary representation of this dummy Android XML file:
# <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1" android:compileSdkVersion="29" android:compileSdkVersionCodename="10.0.0" package="com.example" platformBuildVersionCode="29" platformBuildVersionName="10.0.0">
#   <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
# </manifest>
#
# This is created by running:
#   $ aapt2 link -o output.apk -I /usr/share/android-framework-res/framework-res.apk --manifest AndroidManifest-in.xml
#   $ unzip output.apk AndroidManifest.xml
# The framework-res.apk needed by aapt2 here is provided by the Debian package
# `android-framework-res`.
# See https://salsa.debian.org/reproducible-builds/diffoscope/-/issues/340
AXML = (
    b"\x03\x00\x08\x00\xf0\x03\x00\x00\x01\x00\x1c\x00l\x02\x00\x00"
    b"\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\\x00\x00\x00"
    b"\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x008\x00\x00\x00R\x00\x00\x00"
    b"v\x00\x00\x00\x9c\x00\x00\x00\xd2\x00\x00\x00\xd8\x00\x00\x00"
    b"\xe8\x00\x00\x00\xfa\x00\x00\x00\x14\x01\x00\x00l\x01\x00\x00"
    b"\x80\x01\x00\x00\x92\x01\x00\x00\xc6\x01\x00\x00\xfa\x01\x00\x00\r\x00m\x00"
    b"i\x00n\x00S\x00d\x00k\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00\x00\x00\x0b\x00"
    b"v\x00e\x00r\x00s\x00i\x00o\x00n\x00C\x00o\x00d\x00e\x00\x00\x00\x0b\x00v\x00"
    b"e\x00r\x00s\x00i\x00o\x00n\x00N\x00a\x00m\x00e\x00\x00\x00\x10\x00t\x00a\x00"
    b"r\x00g\x00e\x00t\x00S\x00d\x00k\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00"
    b"\x00\x00\x11\x00c\x00o\x00m\x00p\x00i\x00l\x00e\x00S\x00d\x00k\x00V\x00e\x00"
    b"r\x00s\x00i\x00o\x00n\x00\x00\x00\x19\x00c\x00o\x00m\x00p\x00i\x00l\x00e\x00"
    b"S\x00d\x00k\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00C\x00o\x00d\x00e\x00"
    b"n\x00a\x00m\x00e\x00\x00\x00\x01\x001\x00\x00\x00\x06\x001\x000\x00.\x00"
    b"0\x00.\x000\x00\x00\x00\x07\x00a\x00n\x00d\x00r\x00o\x00i\x00d\x00"
    b"\x00\x00\x0b\x00c\x00o\x00m\x00.\x00e\x00x\x00a\x00m\x00p\x00l\x00"
    b"e\x00\x00\x00*\x00h\x00t\x00t\x00p\x00:\x00/\x00/\x00s\x00c\x00h\x00e\x00"
    b"m\x00a\x00s\x00.\x00a\x00n\x00d\x00r\x00o\x00i\x00d\x00.\x00c\x00o\x00"
    b"m\x00/\x00a\x00p\x00k\x00/\x00r\x00e\x00s\x00/\x00a\x00n\x00d\x00r\x00"
    b"o\x00i\x00d\x00\x00\x00\x08\x00m\x00a\x00n\x00i\x00f\x00e\x00s\x00"
    b"t\x00\x00\x00\x07\x00p\x00a\x00c\x00k\x00a\x00g\x00e\x00\x00\x00\x18\x00"
    b"p\x00l\x00a\x00t\x00f\x00o\x00r\x00m\x00B\x00u\x00i\x00l\x00d\x00V\x00"
    b"e\x00r\x00s\x00i\x00o\x00n\x00C\x00o\x00d\x00e\x00\x00\x00\x18\x00p\x00l\x00"
    b"a\x00t\x00f\x00o\x00r\x00m\x00B\x00u\x00i\x00l\x00d\x00V\x00e\x00r\x00"
    b"s\x00i\x00o\x00n\x00N\x00a\x00m\x00e\x00\x00\x00\x08\x00u\x00s\x00e\x00s\x00"
    b"-\x00s\x00d\x00k\x00\x00\x00\x00\x00\x80\x01\x08\x00 \x00\x00\x00"
    b"\x0c\x02\x01\x01\x1b\x02\x01\x01\x1c\x02\x01\x01p\x02\x01\x01r\x05\x01\x01"
    b"s\x05\x01\x01\x00\x01\x10\x00\x18\x00\x00\x00\x01\x00\x00\x00"
    b"\xff\xff\xff\xff\x08\x00\x00\x00\n\x00\x00\x00\x02\x01\x10\x00"
    b"\xb0\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff"
    b"\x0b\x00\x00\x00\x14\x00\x14\x00\x07\x00\x00\x00\x00\x00\x00\x00"
    b"\n\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\x08\x00\x00\x10"
    b"\x01\x00\x00\x00\n\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00"
    b"\x08\x00\x00\x03\x06\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00"
    b"\xff\xff\xff\xff\x08\x00\x00\x10\x1d\x00\x00\x00\n\x00\x00\x00"
    b"\x05\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x03\x07\x00\x00\x00"
    b"\xff\xff\xff\xff\x0c\x00\x00\x00\t\x00\x00\x00\x08\x00\x00\x03\t\x00\x00\x00"
    b"\xff\xff\xff\xff\r\x00\x00\x00\xff\xff\xff\xff\x08\x00\x00\x10"
    b"\x1d\x00\x00\x00\xff\xff\xff\xff\x0e\x00\x00\x00\x07\x00\x00\x00"
    b"\x08\x00\x00\x03\x07\x00\x00\x00\x02\x01\x10\x00L\x00\x00\x00"
    b"\x02\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x0f\x00\x00\x00"
    b"\x14\x00\x14\x00\x02\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00"
    b"\x00\x00\x00\x00\xff\xff\xff\xff\x08\x00\x00\x10\x15\x00\x00\x00"
    b"\n\x00\x00\x00\x03\x00\x00\x00\xff\xff\xff\xff\x08\x00\x00\x10"
    b"\x1d\x00\x00\x00\x03\x01\x10\x00\x18\x00\x00\x00\x02\x00\x00\x00"
    b"\xff\xff\xff\xff\xff\xff\xff\xff\x0f\x00\x00\x00\x03\x01\x10\x00"
    b"\x18\x00\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff"
    b"\x0b\x00\x00\x00\x01\x01\x10\x00\x18\x00\x00\x00\x01\x00\x00\x00"
    b"\xff\xff\xff\xff\x08\x00\x00\x00\n\x00\x00\x00"
)


class ArscDumpWithAapt(Command):
    MASK_STDERR = True

    @tool_required("aapt2")
    def cmdline(self):
        return ["aapt2", "dump", "resources", self.path]


class ArscFile(File):
    DESCRIPTION = "Android package resource table (ARSC)"
    FILE_TYPE_HEADER_PREFIX = b"\x02\x00\x0c\x00"
    FILE_TYPE_RE = re.compile(r"^Android package resource table\b")
    FILE_EXTENSION_SUFFIX = {".arsc"}

    @tool_required("aapt2")
    def compare_details(self, other, source=None):
        with as_apk(self.path) as self_apk, as_apk(other.path) as other_apk:
            return [
                Difference.from_operation(
                    ArscDumpWithAapt, self_apk, other_apk
                )
            ]


@contextmanager
def as_apk(path):
    # Generate a temporary ZIP file containing the resources.arsc file and a
    # dummy AndroidManifest.xml.  The aapt2 dump resources command requires the
    # resources.arsc file to be inside an APK (ZIP file), and for a valid
    # AndroidManifest.xml file to be present as well.  However, the XML file
    # does not actually need to correspond to the resources.arsc file (though
    # this will result in warnings on stderr, which we ignore); providing a
    # dummy XML file produces the exact same dump.
    with open(path, "rb") as fhi:
        with tempfile.TemporaryDirectory() as tdir:
            apk = os.path.join(tdir, "out.apk")
            with zipfile.ZipFile(apk, "w") as zf:
                zf.writestr("AndroidManifest.xml", AXML)
                with zf.open("resources.arsc", "w") as fho:
                    while True:
                        data = fhi.read(4096)
                        if not data:
                            break
                        fho.write(data)
            yield apk
