#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 Huawei Device Co., Ltd.
# 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 sys
import argparse
import os
import shutil
import json
from interface_mgr import InterfaceMgr

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from scripts.util.file_utils import read_json_file, write_file, write_json_file  # noqa: E402 E501
from scripts.util import build_utils  # noqa: E402

SHARED_LIBRARY_BUILD_TEMPLATE = '''
config("%s_config") {
  include_dirs = [ "%s/include", ]
}
ohos_prebuilt_shared_library("%s") {
  source = "%s"
  public_configs = [ ":%s_config" ]
  subsystem_name = "%s"
  part_name = "%s"
  install_enable = false
}

'''
JAR_BUILD_TEMPLATE = '''
java_prebuilt("%s") {
  jar_path = "%s"
'''
MAPLE_BUILD_TEMPLATE = '''
ohos_maple_java_prebuilt("%s") {
  mplt = %s
  jar_path = "%s"
'''


# check sdk header file signature,
# skip check if file dir not exist
def check_header_files(checkfile_dir: str, subsystem_sdk_dir: str, subsystem_name: str,
                       module_name: str, interface_mgr):
    if checkfile_dir is None or not os.path.exists(checkfile_dir):
        return
    interface_mgr.check(checkfile_dir, subsystem_sdk_dir, subsystem_name,
                        module_name)


def get_build_config_from_label(label: str, current_toolchain_dir: str) -> str:
    build_config = label.replace('//', 'gen/').replace(':', '/')
    bc_file = '{}/{}.build_config'.format(current_toolchain_dir, build_config)
    return bc_file


def add_dynamic_deps(output: str, build_config: str) -> str:
    external_deps = build_utils.expand_file_args(
        ['@FileArg({}:deps_info:external_deps)'.format(build_config)])
    if external_deps[0] != '[  ]':
        output += "external_deps = " + external_deps[0]
    output += "}"
    return output


# Copy the SDK module to the sdk output directory,
# and generate the prebuilt template to BUILD.gn file.
def gen_build_file(input_file: str, sdk_out_dir: str, generate_sig: bool,
                   signature_file_check_dir: str):
    data = read_json_file(input_file)
    if data is None:
        raise Exception(
            "sdk interface description info error, file [{}] does not exist.".
            format(input_file))

    build_file_content = 'import("//build/ohos.gni")\n'
    sdk_info = {}
    # interface check
    interface_mgr = InterfaceMgr()
    for module_desc in data:
        subsystem_name = module_desc.get('subsystem_name')
        part_name = module_desc.get('part_name')
        origin_name = module_desc.get('origin_name')
        module_type = module_desc.get('type')
        module_name = module_desc.get('name')
        module_info_file = module_desc.get('source_info')
        _current_toolchain_dir = module_desc.get('current_toolchain_dir')

        # read module_info.json file, get module source path
        source_data = read_json_file(module_info_file)
        if not source_data:
            print("read module [{}] info file failed, filename [{}].".format(
                module_name, module_info_file))
            continue

        # copy lib file
        source = source_data.get('source')
        if module_type == 'jar':
            source = source_data.get('alternative_source')

        # check sdk type consistency
        suffix = module_type
        if module_type == "none":
            continue
        # Don't check suffix for maple sdk.
        if module_type == "maple":
            pass
        elif not source.endswith(suffix):
            raise Exception(
                "sdk module [{}] type configuration is inconsistent.".format(
                    module_name))

        module_sdk_out_dir = os.path.join(sdk_out_dir, module_name)
        if not os.path.exists(module_sdk_out_dir):
            os.makedirs(module_sdk_out_dir, exist_ok=True)
        if module_type == "maple":
            pass
        else:
            shutil.copy(source, module_sdk_out_dir)

        # copy headers file
        header_base = module_desc.get('header_base')
        if not header_base:
            raise Exception("{}: header_base config error.".format(
                module_desc.get('label')))

        header_files = module_desc.get('header_files')
        # copy to sdk/{subsystem_name}/{module_name}/include/
        for header_file in header_files:
            rel_h_file = os.path.join(header_base, header_file)

            header_file_dest = os.path.join(module_sdk_out_dir, 'include',
                                            header_file)
            header_file_dest_dir = os.path.dirname(header_file_dest)
            if not os.path.exists(header_file_dest_dir):
                os.makedirs(header_file_dest_dir, exist_ok=True)

            shutil.copy(rel_h_file, header_file_dest)

        # generate or check header files
        if module_type == 'so' and generate_sig is False:
            check_header_files(signature_file_check_dir, sdk_out_dir,
                               origin_name, module_name, interface_mgr)

        # gen build file
        lib_file = os.path.join(module_name, os.path.basename(source))
        if module_type == 'so':
            build_file_content += SHARED_LIBRARY_BUILD_TEMPLATE % (
                module_name, module_name, module_name, lib_file, module_name,
                subsystem_name, part_name)
        elif module_type == 'jar':
            bc = get_build_config_from_label(source_data.get("label"),
                                             _current_toolchain_dir)
            build_file_content += JAR_BUILD_TEMPLATE % (module_name, lib_file)
            build_file_content = add_dynamic_deps(build_file_content, bc)
        elif module_type == 'maple':
            bc = get_build_config_from_label(source_data.get("label"),
                                             _current_toolchain_dir)
            bc_mplts = build_utils.expand_file_args(
                ['@FileArg({}:outputs:output_mplt)'.format(bc)])
            bc_mplts = build_utils.parse_and_flatten_gn_lists(bc_mplts)
            sdk_mplts = []
            base_dir = os.path.basename(module_sdk_out_dir)
            for mplt in bc_mplts:
                shutil.copy(mplt, module_sdk_out_dir)
                sdk_mplts.append(os.path.join(base_dir,
                                              os.path.basename(mplt)))

            bc_jar_path = build_utils.expand_file_args(
                ["@FileArg({}:deps_info:unprocessed_jar_path)".format(bc)])
            bc_jar_path = build_utils.parse_and_flatten_gn_lists(bc_jar_path)
            sdk_jar_path = []
            for jar in bc_jar_path:
                shutil.copy(jar, module_sdk_out_dir)
                sdk_jar_path.append(
                    os.path.join(base_dir, os.path.basename(jar)))
            build_file_content += MAPLE_BUILD_TEMPLATE % (
                module_name, json.dumps(sdk_mplts, sort_keys=True,
                                        indent=2), sdk_jar_path[0])
            build_file_content = add_dynamic_deps(build_file_content, bc)
        elif module_type == 'rlib':
            pass
        else:
            raise Exception(
                'The type of sdk module configuration is not supported.')

        module_info = {
            'source': lib_file,
            'label': module_desc.get('label'),
            'type': module_type
        }
        sdk_info[module_name] = module_info
    return build_file_content, sdk_info


def generate_sdk(input_file: str, sdk_out_dir: str, output_build_file: str, sdk_info_file: str,
                 generate_sig: str, signature_file_check_dir: str,
                 signature_file_gen_dir: str):
    build_file_content, sdk_info = gen_build_file(input_file, sdk_out_dir,
                                                  generate_sig,
                                                  signature_file_check_dir)
    # write file, build gn file
    write_file(output_build_file, build_file_content)
    # sdk info return
    write_json_file(sdk_info_file, sdk_info)

    # gen signature file
    if generate_sig is True:
        interface_mgr = InterfaceMgr()
        interface_mgr.gen_sig_file_by_subsystem(sdk_out_dir,
                                                signature_file_gen_dir)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--input-file', help='', required=True)
    parser.add_argument('--sdk-out-dir', help='', required=True)
    parser.add_argument('--output-build-file', help='', required=True)
    parser.add_argument('--sdk-info-file', help='', required=True)
    parser.add_argument('--generate-sig',
                        dest='generate_sig',
                        action='store_true')
    parser.set_defaults(generate_sig=False)
    parser.add_argument('--signature-file-check-dir', help='', required=False)
    parser.add_argument('--signature-file-gen-dir', help='', required=False)
    parser.add_argument('--depfile', help='', required=False)
    args = parser.parse_args()

    generate_sdk(args.input_file, args.sdk_out_dir, args.output_build_file,
                 args.sdk_info_file, args.generate_sig,
                 args.signature_file_check_dir, args.signature_file_gen_dir)

    if args.depfile:
        _dep_files = []
        _dep_files.append(args.input_file)
        build_utils.write_depfile(args.depfile, args.output_build_file, _dep_files, add_pydeps=False)
    return 0


if __name__ == '__main__':
    sys.exit(main())