#!/usr/bin/env python

#coding=utf-8



#

# Copyright (c) 2022 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 os

import json





class BaseRule(object):

    RULE_NAME = ""



    def __init__(self, mgr, args):

        self._mgr = mgr

        self._args = args

        self.__white_lists = self.load_files("whitelist.json")

        self.__out_path = mgr.get_product_out_path()

        self.__base_sofiles = ["libc.so", "libutils.z.so", "ld-musl-aarch64.so.1", "libconfiguration.z.so", "libusbmanager.z.so",

                               "libsms.z.so"]

    

    def load_lists(self):

        self.__passthroughs_lists = self.load_list_json("Passthrough", "passthrough_info.json")

        self.__llndk_lists = self.load_list_json("LLndk", "llndk_info.json")

        self.__chipsetsdksp_lists = self.load_list_json("ChipsetsdkSP", "chipsetsdk_sp_info.json")

        self.__chipsetsdk_lists = self.load_list_json("ChipsetSDK", "chipsetsdk_info.json")



    def load_files(self, name):

        rules_dir = []

        rules_dir.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../rules"))

        if self._args and self._args.rules:

            rules_dir = rules_dir + self._args.rules



        res = []

        for d in rules_dir:

            rules_file = os.path.join(d, self.__class__.RULE_NAME, name)

            try:

                with open(rules_file, "r") as f:

                    jsonstr = "".join([line.strip() for line in f if not line.strip().startswith("//")])

                    res = res + json.loads(jsonstr)

            except:

                pass



        return res



    def get_mgr(self):

        return self._mgr



    def get_white_lists(self):

        return self.__white_lists



    def log(self, info):

        print(info)



    def warn(self, info):

        print("\033[35m[WARNING]\x1b[0m: %s" % info)



    def error(self, info):

        print("\033[91m[NOT ALLOWED]\x1b[0m: %s" % info)



    def get_help_url(self):

        return "https://gitee.com/openharmony/developtools_integration_verification/tree/master/tools/deps_guard/rules/%s/README.md" % self.__class__.RULE_NAME

    

    def get_out_path(self):

        return self.__out_path



    def is_only(self, mod):

        vendor_paths = ["vendor/", "updater_vendor/", "chip_prod/", "eng_chipset/", "log/", "userdata/"]

        system_paths = ["system/", "updater/", "ramdisk/", "eng_system/", "patch/", "updater_ramdisk/"]



        innerapi_tags = ["llndk", "chipsetsdk", "chipsetsdk_indirect", "chipsetsdk_sp", "chipsetsdk_sp_indirect", 

                         "passthrough", "passthrough_indirect"]

        is_system = False

        is_vendor = False

    

        if not (mod["name"].endswith(".so") or mod["name"].endswith(".so.1")):

            return "other"

    

        mod_path = mod["path"]

        for system_path in system_paths:

            if system_path in mod_path:

                is_system = True

                break

        for vendor_path in vendor_paths:

            if vendor_path in mod_path:

                is_vendor = True

                break

    

        mod_innerapi_tags = mod["innerapi_tags"]

        if all(item not in mod_innerapi_tags for item in innerapi_tags):

            if is_system:

                return "system"

            elif is_vendor:

                return "vendor"

            else:

                return "other"

        else:

            return "other"   

 

    def get_dep_whitelist(self):   

        whitelist_file = os.path.join(self.get_out_path().replace("out", "out/products_ext"), "chipsetsdk_dep_whitelist.json")

        if not os.path.exists(whitelist_file):

            whitelist_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), f"../rules/chipsetsdk_dep_whitelist.json")

        res = []

        if os.path.exists(whitelist_file):

            self.log("****chipsetsdk dep whitelist file is {}****".format(whitelist_file))

            with open(whitelist_file, "r") as f:

                contents = f.read()

            if not contents:

                self.log("****system/vendor only whitelist.json {} is null****".format(whitelist_file))

            json_data = json.loads(contents)

            for so in json_data:

                so_file_name = so.get("so_file_name")

                dep_file_name = so.get("dep_file_name")

                so_dict = {so_file_name: dep_file_name}

                if so_dict not in res:

                    res.append(so_dict)



        return res



    # To be override

    def check(self):

        # Default pass

        return True



    def check_if_deps_correctly(self, check_modules, valid_mod_tags, valid_dep_tags, white_lists):

        # check if mod and callee have wrong innerapi tags

        self.load_lists()

        passed = True

        for mod in check_modules:

            innerapi_tags = mod["innerapi_tags"]

            if innerapi_tags and all(item in valid_mod_tags for item in innerapi_tags):

                for dep in mod["deps"]:

                    callee = dep["callee"]



                    if callee["name"] in self.__base_sofiles:

                        continue



                    if "duplicate" in callee.keys() and callee["duplicate"]:

                        passed = False

                        self.error("NEED MODIFY: %s has the same name in other package path, sofile name should be modified" % (callee["name"]))



                    dep_innerapi_tags = callee["innerapi_tags"]

                    wrong_tags = [item for item in dep_innerapi_tags if item not in valid_dep_tags]



                    in_whitelist = False

                    for so_dict in white_lists:

                        for k, v in so_dict.items():

                            if k == mod["name"] and v == callee["name"]:

                                in_whitelist = True

                                break



                    if in_whitelist:

                        continue

                    

                    # llndk can dep system only sofile

                    if "system" in valid_dep_tags and self.is_only(callee) == "system":

                        continue



                    # check system/vendor only

                    if self.is_only(callee) == "system":

                        passed = False

                        self.error("NEED MODIFY: %s with innerapi_tags [%s] cannot depend system only file %s with %s" 

                            % (mod["name"], ",".join(innerapi_tags), callee["name"], callee["labelPath"]))

                    elif self.is_only(callee) == "vendor":

                        passed = False

                        self.error("NEED MODIFY: %s with innerapi_tags [%s] cannot depend vendor only file %s with %s" 

                            % (mod["name"], ",".join(innerapi_tags), callee["name"], callee["labelPath"]))



                    if dep_innerapi_tags and all(item in valid_dep_tags for item in dep_innerapi_tags):

                        continue

                    elif not dep_innerapi_tags:

                        continue



                    passed = False

                    self.error("NEED MODIFY: %s with innerapi_tags [%s] has dep file %s with %s contains wrong dep innerapi_tags [%s] in innerapi_tags [%s]" 

                        % (mod["name"], ",".join(innerapi_tags), callee["name"], callee["labelPath"], ",".join(wrong_tags), ",".join(dep_innerapi_tags)))

                # dep is missing, not sure if it's correct. need verify it

                for dep_name in mod["missing"]:

                    if dep_name in self.__base_sofiles:

                        continue

                    in_whitelist = False

                    for so_dict in white_lists:

                        for k, v in so_dict.items():

                            if k == mod["name"] and v == dep_name:

                                in_whitelist = True

                                break



                    if in_whitelist:

                        continue

                    else:

                        self.error("NEED MODIFY: system only module %s depends on %s which is unknown so type"

                                   %(mod["name"], dep_name))

                        passed = False



            else:

                wrong_tags = [item for item in innerapi_tags if item not in valid_mod_tags]

                self.error("NEED MODIFY: module %s with %s contains wrong mod innerapi_tags [%s] in innerapi_tags [%s]" 

                           % (mod["name"], mod["path"], ",".join(wrong_tags), ",".join(innerapi_tags)))

                return False



        return passed



    def get_allow_list(self):

        return self.__passthroughs_lists + self.__llndk_lists + self.__chipsetsdksp_lists



    def get_vendor_allow_list(self):

        return self.__passthroughs_lists + self.__llndk_lists + self.__chipsetsdksp_lists + self.__chipsetsdk_lists



    def parser_rules_file(self, rules_file, res):

        try:

            self.log("****Parsing rules file in {}****".format(rules_file))

            with open(rules_file, "r") as f:

                contents = f.read()

            if not contents:

                self.log("****rules file {} is null****".format(rules_file))

                return res

            json_data = json.loads(contents)

            for so in json_data:

                so_file_name = so.get("so_file_name")

                if so_file_name and so_file_name not in res:

                    res.append(so_file_name)

        except (FileNotFoundError, IOError, UnicodeDecodeError) as file_open_or_decode_err:

            self.error(file_open_or_decode_err)

        

        return res



    def load_list_json(self, rule_name, name):

        rules_dir = []

        if self._args and self._args.rules:

            self.log("****add more info in:{}****".format(self._args.rules))

            rules_dir = rules_dir + self._args.rules



        chipsetsdk_rules_path = self.get_out_path().replace("out", "out/products_ext")

        new_rule_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../rules")

        if os.path.exists(chipsetsdk_rules_path):

            rules_dir.append(chipsetsdk_rules_path)

            self.log("****add more info in dir:{}****".format(chipsetsdk_rules_path))

        elif os.path.exists(new_rule_path):

            rules_dir.append(new_rule_path)

            self.log("****add path:{}****".format(new_rule_path))

        res = []

        for d in rules_dir:

            rules_file = os.path.join(d, rule_name, name)

            if os.path.isfile(rules_file):

                res = self.parser_rules_file(rules_file, res)

            else:

                self.warn("****rules path not exist: {}****".format(rules_file))        

        

        return res