#!/bin/sh
''''[ ! -z $VIRTUAL_ENV ] && exec python -u -- "$0" ${1+"$@"}; command -v python3 > /dev/null && exec python3 -u -- "$0" ${1+"$@"}; exec python2 -u -- "$0" ${1+"$@"} # '''

import sys
import os
import argparse
import shutil
import tempfile
import traceback

HERE = os.path.dirname(__file__)
READIES = os.path.abspath(os.path.join(HERE, ".."))
sys.path.insert(0, READIES)
import paella  # noqa: F401

os.environ["PYTHONWARNINGS"] = 'ignore:DEPRECATION::pip._internal.cli.base_command'

CLANG_COMMON_VER="15"
CLANG_FORMAT_VER="14"
ARM_CLANG_FORMAT_VER="12"

#----------------------------------------------------------------------------------------------

CLANG_FORMAT_URL_BASE="https://github.com/muttleyxd/clang-tools-static-binaries/releases/download"
CLANG_FORMAT_URL = "{BASE}/master-208096c1/clang-format-{VER}_linux-amd64".format(BASE=CLANG_FORMAT_URL_BASE, VER=CLANG_FORMAT_VER)

class CLangFormatSetup(paella.Setup):
    def __init__(self, args):
        paella.Setup.__init__(self, nop=args.nop)
        self.version = args.version if args.version is not None else CLANG_FORMAT_VER

    def linux(self):
        self.install_downloaders()

        if self.arch == 'x64':
            self.run("wget -q -O /usr/local/bin/clang-format-{VER} {URL}".format(VER=self.version, URL=CLANG_FORMAT_URL), sudo=True)
            self.run("chmod +x /usr/local/bin/clang-format-{VER}".format(VER=self.version), sudo=True)
            self.run("ln -sf /usr/local/bin/clang-format-{VER} /usr/local/bin/clang-format".format(VER=self.version), sudo=True)
        elif self.platform.is_arm():
            self.version = ARM_CLANG_FORMAT_VER
            self.install("clang-format-{VER}".format(VER=self.version))
            self.run("ln -sf `command -v clang-format-{VER}` /usr/local/bin/clang-format".format(VER=self.version), sudo=True)
        else:
            raise paella.Error("Error installing CLang Format on not-x64/arm platform")

    def macos(self):
        self.install("clang-format")

#----------------------------------------------------------------------------------------------

class CLangSetup(paella.Setup):
    def __init__(self, args):
        paella.Setup.__init__(self, nop=args.nop)
        self.modern = args.modern
        self.version = args.version
        self.native = not (self.modern is True or self.version is not None)

    def common_first(self):
        if self.native:
            self.install("clang")

    def debian_compat(self):
        if self.native:
            return

        if self.osnick == "xenial":
            self.install_v13_from_github()
            return

        self.install("software-properties-common apt-transport-https")
        self.install("unzip lsb-release gnupg2")

        if self.osnick == "trusty":
            self.install_trusty()
            self.create_symlinks()
            return

        if self.osnick in ["xenial", "stretch", "buster"]:
            clang_ver = "12"
        elif self.osnick in ["bionic"]:
            clang_ver = "13"
        else:
            clang_ver = args.version if args.version is not None else CLANG_COMMON_VER
        if clang_ver == "": # "" is for auto-detect
            install_args = ""
        else:
            install_args = "{CLANG_VER} all".format(CLANG_VER=clang_ver)
        self.run("wget -q https://apt.llvm.org/llvm.sh -O /tmp/llvm.sh")
        self.run(r'''
            sed -i -e 's/add-apt-repository "${REPO_NAME}"/add-apt-repository -y "${REPO_NAME}"/g' /tmp/llvm.sh
            ''')
        if self.osnick == "xenial":
            self.run(r'''
                sed -i -e 's/libunwind$LLVM_VERSION/libunwind/g' /tmp/llvm.sh
                ''')
        self.run(r'''
            sed -i -e 's/lld-$LLVM_VERSION/lld-$LLVM_VERSION python3-lldb-$LLVM_VERSION/g' /tmp/llvm.sh
            ''')
        self.run(r"""
            bash /tmp/llvm.sh {ARGS}
            rm /tmp/llvm.sh
            """.format(ARGS=install_args), sudo=True)

        clang_ver = sh("ls /usr/bin/clang-* 2>/dev/null | grep -E 'clang-[[:digit:]].*' | cut -f2 -d- | sort -nr | head -1").strip()
        if clang_ver == "" and not self.nop:
            raise paella.Error("Error installing CLang via apt.llvm.org")

        self.install("clang-tools-{VER}".format(VER=clang_ver))

        self.create_symlinks(clang_ver)

    def redhat_compat(self):
        if not self.modern:
            return
        self.run("%s/bin/getepel" % READIES, sudo=True)
        if self.dist in ['centos', 'ol'] and self.os_version[0] == 8:
            self.install("llvm-toolset")
        else:
            self.install("llvm-toolset-7.0")
            self.cp_to_profile_d("/opt/rh/llvm-toolset-7.0/enable", "llvm-toolset-7.0.sh")

    def fedora(self):
        if self.modern:
            self.install("clang")

    def macos(self):
        self.install("llvm")
        # self.install_latest_from_github()

    def common_last(self):
        pass

    def create_symlinks(self, clang_ver=None):
        if clang_ver is None:
            clang_ver = self.version

        # arrange llvm-* and clang-* version-neutral symlinks
        self.run(r"""
            cd /usr/bin
            for f in `ls llvm*-{CLANG_VER} clang*-{CLANG_VER} 2>/dev/null`; do ln -sf $f ${{f/%-{CLANG_VER}/}}; done
            """.format(CLANG_VER=clang_ver), sudo=True)
    
    def install_trusty(self):
        clang_ver = "8"
        self.run("%s/bin/getgcc --modern" % READIES)
        self.run('add-apt-repository  "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-8 main"', sudo=True)
        self.run("wget -q -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -", sudo=True)
        self.run("apt-get update -qq", sudo=True)
        self.install("llvm-8 llvm-8-dev llvm-8-doc llvm-8-examples llvm-8-runtime "
                     "libllvm-8-ocaml-dev libllvm8 "
                     "clang-8 clang-tools-8 clang-8-doc "
                     "libclang-common-8-dev libclang-8-dev libclang1-8 libc++-8-dev libc++abi-8-dev libfuzzer-8-dev "
                     "clang-format-8 python-clang-8 "
                     "lldb-8 lld-8")
    
    def install_v13_from_github(self):
        file = None
        LLVM_VER = "13.0.0"
        if self.arch == "x64":
            arch = "x86_64"
            if self.osnick == "xenial":
                file = "clang+llvm-{ver}-x86_64-linux-gnu-ubuntu-16.04.tar.xz".format(ver=LLVM_VER)
            elif self.osnick == "focal":
                file = "clang+llvm-{ver}-x86_64-linux-gnu-ubuntu-20.04.tar.xz".format(ver=LLVM_VER)
            elif self.os == "macos":
                file = "clang+llvm-{ver}-x86_64-apple-darwin.tar.xz".format(ver=LLVM_VER)
        elif self.arch == "arm64v8":
            arch = "aarch64"
            file = "clang+llvm-{ver}-armv7a-linux-gnueabihf.tar.xz".format(ver=LLVM_VER)
        elif self.arch == "arm64v7":
            file = "clang+llvm-{ver}-armv7a-linux-gnueabihf.tar.xz".format(ver=LLVM_VER)
        if file is not None:
            tar = paella.wget("https://github.com/llvm/llvm-project/releases/download/llvmorg-{ver}/{file}".
                              format(ver=LLVM_VER, file=file), tempdir=True)
            self.run(r'''
                tar -C /opt -xJf {tar}
                ln -s /opt/`basename {tar} .tar.xz` /opt/llvm-13
                '''.format(tar=tar))
        self.cat_to_profile_d(r'''
            export PATH=/opt/llvm-13/bin:${PATH}
            ''', "llvm-13.sh")

    def install_latest_from_github(self):
        file = None
        ver = "15.0.3"
        if self.arch == "x64":
            arch = "x86_64"
            if self.os == "macos":
                file = f"clang+llvm-{ver}-{arch}-apple-darwin.tar.xz"
        elif self.arch == "arm64v8":
            arch = "arm64"
            if self.os == "macos":
                file = f"clang+llvm-{ver}-{arch}-apple-darwin21.0.tar.xz"

        if file is None:
            raise Error("artifacts not available at github")

        tar = paella.wget(f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{ver}/{file}",
                          tempdir=True)
        self.run(r'''
            tar -C /opt -xJf {tar}
            ln -s /opt/`basename {tar} .tar.xz` /opt/llvm-15
            '''.format(tar=tar), sudo=True)
        self.cat_to_profile_d(r'''
            export PATH=/opt/llvm-15/bin:${PATH}
            ''', "llvm-15.sh")

#----------------------------------------------------------------------------------------------

parser = argparse.ArgumentParser(description='Install CLang tools')
parser.add_argument('-n', '--nop', action="store_true", help='no operation')
parser.add_argument('--force', action="store_true", default=False, help='Install clang even if already installed')
parser.add_argument('--format', action="store_true", default=False, help='Install clang-format')
parser.add_argument('--modern', action="store_true", help="Install modern CLang/LLVM version")
parser.add_argument('-v', '--version', type=str, default=None, help='CLang version')
parser.add_argument('--just-fix-links', action="store_true", default=False, help='Fix symlinks to specified version')
args = parser.parse_args()

if args.modern and args.version:
    eprint("arguments --modern and --version are conflicting.")
    exit(1)

if args.just_fix_links:
    if args.format:
        eprint(f"arguments --format and --just-fix-links are conflicting.")
        exit(1)
    if args.version is None:
        eprint(f"argument --just-fix-links requires --version.")
        exit(1)

try:
    if args.format:
        CLangFormatSetup(args).setup()
    else:
        if args.just_fix_links:
            CLangSetup(args).create_symlinks()
            exit(0)
        if paella.Setup.has_command('clang') and not args.force:
            vermsg = sh(f'clang --version')
            eprint(f"clang is present:\n{vermsg}")
            exit(0)
        CLangSetup(args).setup()
except Exception as x:
    traceback.print_exc()
    eprint(str(x))
    exit(1)

exit(0)
