# Copyright (c) 2021 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import re
import shlex
from enum import Enum, auto

from .gn import GnBuilder


class IMXApp(Enum):
    CHIP_TOOL = auto()
    LIGHT = auto()
    THERMOSTAT = auto()
    ALL_CLUSTERS = auto()
    ALL_CLUSTERS_MINIMAL = auto()
    OTA_PROVIDER = auto()

    def ExamplePath(self):
        if self == IMXApp.CHIP_TOOL:
            return 'chip-tool'
        if self == IMXApp.LIGHT:
            return 'lighting-app/linux'
        if self == IMXApp.THERMOSTAT:
            return 'thermostat/linux'
        if self == IMXApp.ALL_CLUSTERS:
            return 'all-clusters-app/linux'
        if self == IMXApp.ALL_CLUSTERS_MINIMAL:
            return 'all-clusters-minimal-app/linux'
        if self == IMXApp.OTA_PROVIDER:
            return 'ota-provider-app/linux'

    def OutputNames(self):
        if self == IMXApp.CHIP_TOOL:
            yield 'chip-tool'
            yield 'chip-tool.map'
        if self == IMXApp.LIGHT:
            yield 'chip-lighting-app'
            yield 'chip-lighting-app.map'
        if self == IMXApp.THERMOSTAT:
            yield 'thermostat-app'
            yield 'thermostat-app.map'
        if self == IMXApp.ALL_CLUSTERS:
            yield 'chip-all-clusters-app'
            yield 'chip-all-clusters-app.map'
        if self == IMXApp.ALL_CLUSTERS_MINIMAL:
            yield 'chip-all-clusters-minimal-app'
            yield 'chip-all-clusters-minimal-app.map'
        if self == IMXApp.OTA_PROVIDER:
            yield 'chip-ota-provider-app'
            yield 'chip-ota-provider-app.map'


class IMXBuilder(GnBuilder):

    def __init__(self,
                 root,
                 runner,
                 app: IMXApp,
                 release: bool = False):
        super(IMXBuilder, self).__init__(
            root=os.path.join(root, 'examples', app.ExamplePath()),
            runner=runner)
        self.release = release
        self.app = app

    def GnBuildArgs(self):
        try:
            entries = os.listdir(self.SysRootPath('IMX_SDK_ROOT'))
        except FileNotFoundError:
            if self.SysRootPath('IMX_SDK_ROOT') == 'IMX_SDK_ROOT':
                # CI test, use default value
                target_cpu = 'arm64'
                arm_arch = 'armv8-a'
                sdk_target_sysroot = os.path.join(self.SysRootPath('IMX_SDK_ROOT'), 'sysroots/cortexa53-crypto-poky-linux')
                cross_compile = 'aarch64-poky-linux'
                cc = 'aarch64-poky-linux-gcc'
                cxx = 'aarch64-poky-linux-g++'
            else:
                raise Exception('the value of env IMX_SDK_ROOT is not a valid path.')
        else:
            for entry in entries:
                if entry.startswith(r'environment-setup-'):
                    env_setup_script = entry
                    break

            try:
                env_setup_script
            except NameError:
                raise Exception('The SDK environment setup script is not found, make sure the env IMX_SDK_ROOT is correctly set.')
            else:

                with open(os.path.join(self.SysRootPath('IMX_SDK_ROOT'), env_setup_script), 'r') as env_setup_script_fd:
                    lines = env_setup_script_fd.readlines()
                    for line in lines:
                        line = line.strip('\n')
                        m = re.match(r'^\s*export\s+SDKTARGETSYSROOT=(.*)', line)
                        if m:
                            sdk_target_sysroot = shlex.split(m.group(1))[0]

                        m = re.match(r'^\s*export\s+CC=(.*)', line)
                        if m:
                            cc = shlex.split(m.group(1))[0]
                        m = re.match(r'^\s*export\s+CXX=(.*)', line)
                        if m:
                            cxx = shlex.split(m.group(1))[0]

                        m = re.match(r'^\s*export\s+ARCH=(.*)', line)
                        if m:
                            target_cpu = shlex.split(m.group(1))[0]
                            if target_cpu == 'arm64':
                                arm_arch = 'armv8-a'
                            elif target_cpu == 'arm':
                                arm_arch = 'armv7ve'
                            else:
                                raise Exception('ARCH should be arm64 or arm in the SDK environment setup script.')

                        m = re.match(r'^\s*export\s+CROSS_COMPILE=(.*)', line)
                        if m:
                            cross_compile = shlex.split(m.group(1))[0][:-1]

                try:
                    sdk_target_sysroot
                except NameError:
                    raise Exception('SDKTARGETSYSROOT is not found in the SDK environment setup script.')
                else:
                    try:
                        cc
                        cxx
                    except NameError:
                        raise Exception('CC and/or CXX are not found in the SDK environment setup script.')
                    else:
                        cc = cc.replace('$SDKTARGETSYSROOT', sdk_target_sysroot)
                        cxx = cxx.replace('$SDKTARGETSYSROOT', sdk_target_sysroot)
                try:
                    target_cpu
                    cross_compile
                except NameError:
                    raise Exception('ARCH and/or CROSS_COMPILE are not found in the SDK environment setup script.')

        args = [
            'treat_warnings_as_errors=false',
            'target_os="linux"',
            'target_cpu="%s"' % target_cpu,
            'arm_arch="%s"' % arm_arch,
            'import(\"//build_overrides/build.gni\")',
            'custom_toolchain=\"${build_root}/toolchain/custom\"',
            'sysroot="%s"' % sdk_target_sysroot,
            'target_cflags=[ "-DCHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME=\\"mlan0\\"", "-DCHIP_DEVICE_CONFIG_LINUX_DHCPC_CMD=\\"udhcpc -b -i %s \\"" ]',
            'target_cc="%s/sysroots/x86_64-pokysdk-linux/usr/bin/%s/%s"' % (self.SysRootPath('IMX_SDK_ROOT'), cross_compile,
                                                                            cc),
            'target_cxx="%s/sysroots/x86_64-pokysdk-linux/usr/bin/%s/%s"' % (self.SysRootPath('IMX_SDK_ROOT'), cross_compile,
                                                                             cxx),
            'target_ar="%s/sysroots/x86_64-pokysdk-linux/usr/bin/%s/%s-ar"' % (self.SysRootPath('IMX_SDK_ROOT'), cross_compile,
                                                                               cross_compile),
        ]

        if self.release:
            args.append('is_debug=false')
        else:
            args.append('optimize_debug=true')

        return args

    def SysRootPath(self, name):
        if name not in os.environ:
            raise Exception('Missing environment variable "%s"' % name)
        return os.environ[name]

    def build_outputs(self):
        outputs = {}

        for name in self.app.OutputNames():
            path = os.path.join(self.output_dir, name)
            if os.path.isdir(path):
                for root, dirs, files in os.walk(path):
                    for file in files:
                        outputs.update({
                            file: os.path.join(root, file)
                        })
            else:
                outputs.update({
                    name: os.path.join(self.output_dir, name)
                })

        return outputs
