import argparse
import base64
import json
import logging
import os
import pathlib
import urllib

from volatility3.cli import PrintedProgress
from volatility3.framework import contexts, constants
from volatility3.framework.automagic import linux, mac

vollog = logging.getLogger(__name__)


class BannerCacheGenerator:
    def __init__(self, path: str, url_prefix: str):
        self._path = path
        self._url_prefix = url_prefix

    def convert_url(self, url):
        parsed = urllib.parse.urlparse(url)

        relpath = os.path.relpath(parsed.path, os.path.abspath(self._path))

        return urllib.parse.urljoin(self._url_prefix, relpath)

    def run(self):
        context = contexts.Context()
        json_output = {"version": 1}

        path = self._path
        filename = "*"

        for banner_cache in [linux.LinuxBannerCache, mac.MacBannerCache]:
            sub_path = banner_cache.os
            potentials = []
            for extension in constants.ISF_EXTENSIONS:
                # Hopefully these will not be large lists, otherwise this might be slow
                try:
                    for found in (
                        pathlib.Path(path)
                        .joinpath(sub_path)
                        .resolve()
                        .rglob(filename + extension)
                    ):
                        potentials.append(found.as_uri())
                except FileNotFoundError:
                    # If there's no linux symbols, don't cry about it
                    pass

            new_banners = banner_cache.read_new_banners(
                context,
                "BannerServer",
                potentials,
                banner_cache.symbol_name,
                banner_cache.os,
                progress_callback=PrintedProgress(),
            )
            result_banners = {}
            for new_banner in new_banners:
                # Only accept file schemes
                value = [
                    self.convert_url(url)
                    for url in new_banners[new_banner]
                    if urllib.parse.urlparse(url).scheme == "file"
                ]
                if value and new_banner:
                    # Convert files into URLs
                    result_banners[str(base64.b64encode(new_banner), "latin-1")] = value

            json_output[banner_cache.os] = result_banners

        output_path = os.path.join(self._path, "banners.json")
        with open(output_path, "w") as fp:
            vollog.warning(f"Banners file written to {output_path}")
            json.dump(json_output, fp)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--path", default=os.path.dirname(__file__))
    parser.add_argument(
        "--urlprefix",
        help="Web prefix that will eventually serve the ISF files",
        default="http://localhost/symbols",
    )

    args = parser.parse_args()

    bcg = BannerCacheGenerator(args.path, args.urlprefix)
    bcg.run()
