#!/usr/bin/env python3
# -*- 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 re
import sys
import os
import xml.etree.ElementTree as ET


class CheckGnOnline(object):
    TARGET_NAME = ('ohos_shared_library',
                   'ohos_static_library', 'ohos_executable',
                   'ohos_source_set',
                   'ohos_copy',
                   'ohos_group',
                   'ohos_prebuilt_executable',
                   'ohos_prebuilt_shared_library',
                   'ohos_prebuilt_static_library',
                   'ohos_prebuilt_etc')

    def __init__(self, gn_data: dict) -> None:
        self.status = True
        self.gn_data = gn_data
        self.err_info = list()

    def merge_line(self) -> dict:
        ret_dict = dict()
        for key, values in self.gn_data.items():
            contents = ''
            row_range = list()
            start = -2
            end = -2
            for line in values:
                pos = line[0]
                content = line[1]

                if pos == end + 1:
                    end = pos
                    contents += '\n{}'.format(content)
                elif start != end:
                    row_range.append([start, end, contents])
                    contents = content
                    start = pos
                    end = pos
                else:
                    contents = content
                    start = pos
                    end = pos

                if pos == values[-1][0] and start != end:
                    row_range.append([start, end, contents])

            ret_dict.update({key: row_range})
        return ret_dict

    def check_have_product_name(self, key: str, line: list) -> None:
        rules = '规则4.1 部件编译脚本中禁止使用产品名称变量'
        check_list = ['product_name', 'device_name']
        flag = {check_list[0]: False, check_list[1]: False}
        for check_item in check_list:
            if line[1].find(check_item) != -1 and line[1].find("==") != -1:
                flag[check_item] = True
        if any(flag.values()):
            issue = '存在'
            issue += check_list[0] if flag[check_list[0]] else ''
            issue += ',' if all(flag.values()) else ''
            issue += check_list[1] if flag[check_list[1]] else ''
            pos = "line:{}  {}".format(line[0], line[1])
            self.err_info.append([key, pos, rules, issue])
            self.status = False
        return

    def check_abs_path(self, key: str, line: list) -> None:
        rules = '规则3.1 部件编译脚本中只允许引用本部件路径,禁止引用其他部件的绝对或相对路径'
        issue = '存在绝对路径'
        abs_path_pattern = r'"\/(\/[^\/\n]+){1,63}"'
        abs_info = list()
        abs_iter = re.finditer(abs_path_pattern, line[1])
        for match in abs_iter:
            path = match.group().strip('"')
            if path.startswith('//build') or path.startswith('//third_party'):
                break
            if path.startswith('//prebuilts'):
                break
            if path.startswith('//out'):
                break
            abs_info.append(path)
        if len(abs_info) > 0:
            pos = "line:{}  {}".format(line[0], line[1])
            self.err_info.append([key, pos, rules, issue])
            self.status = False
        return

    def iter_modified_line(self, key, modified_line, target_pattern) -> None:
        rules = '规则3.2 部件编译目标必须指定部件和子系统名'
        check_list = ['subsystem_name', 'part_name']
        for line in modified_line:
            targets = re.finditer(target_pattern, line[2], re.M)
            for it_target in targets:
                flag = {check_list[0]: False, check_list[1]: False}
                target = it_target.group()
                start_target = target.split('\n')[0]
                target_pos = line[2].find(start_target)
                line_offset = line[2].count('\n', 1, target_pos)

                if target.find(check_list[0]) == -1:
                    flag[check_list[0]] = True
                if target.find(check_list[1]) == -1:
                    flag[check_list[1]] = True
                if any(flag.values()):
                    issue = 'target不存在'
                    issue += check_list[0] if flag[check_list[0]] else ''
                    issue += ',' if all(flag.values()) else ''
                    issue += check_list[1] if flag[check_list[1]] else ''
                    pos = "line:{}  {}".format(
                        line[0] + line_offset, start_target)
                    self.err_info.append([key, pos, rules, issue])
                    self.status = False
        return

    def check_pn_sn(self) -> None:
        rules = '规则3.2 部件编译目标必须指定部件和子系统名'
        target_pattern = r"^( *)("
        for target in self.TARGET_NAME:
            target_pattern += target
            if target != self.TARGET_NAME[-1]:
                target_pattern += r'|'
        target_pattern += r")[\s|\S]*?\n\1}$"

        gn_data_merge = self.merge_line()
        for key, values in gn_data_merge.items():
            self.iter_modified_line(key, values, target_pattern)
        return

    def load_ohos_xml(self, path):
        ret_dict = dict()
        tree = ET.parse(path)
        root = tree.getroot()
        for node in root.iter():
            if node.tag != 'project':
                continue
            repo_info = node.attrib
            ret_item = {repo_info['name']: repo_info['groups']}
            ret_dict.update(ret_item)
        return ret_dict

    def is_checked(self, file, xml_dict):
        repo_name = file.split(',')[0].split('/')[-3]

        if repo_name in xml_dict.keys():
            if not file.endswith("(new file)"):
                if xml_dict[repo_name].find('ohos:mini') != -1:
                    return False
                if xml_dict[repo_name].find('ohos:small') != -1:
                    return False
        if repo_name.startswith('device_') or repo_name.startswith('vendor'):
            return False
        if repo_name.startswith('build') or repo_name.startswith('third_party'):
            return False
        return True

    def pre_check(self):
        xml_dict = self.load_ohos_xml(".repo/manifests/ohos/ohos.xml")
        for key in list(self.gn_data.keys()):
            if not self.is_checked(key, xml_dict):
                self.gn_data.pop(key)

    def check(self):
        if not self.gn_data:
            return
        for key, values in self.gn_data.items():
            for line in values:
                if line[1].strip().startswith("import"):
                    continue;
                self.check_have_product_name(key, line)
                self.check_abs_path(key, line)
        self.check_pn_sn()

    def output(self):
        self.pre_check()
        self.check()

        return self.status, self.err_info


if __name__ == "__main__":
    data = {'a.gn': [[11, 'dsfsdf product_name'], [13, '("//build/dasd/")']],
            'b.gn': [[4, 'sdfd'],
                     [5, 'ohos_shared_library("librawfile") {'],
                     [14, 'sdfd'],
                     [15, 'ohos_shared_library("librawfile") {'],
                     [16, '  include_dirs = ['],
                     [17, '    "//base/global/resource_management/frameworks/resmgr/include",'],
                     [18, '  subsystem_name = "global"'], [19, '}'],
                     [25, '  subsystem_name = "global"'], [29, '}']]}

    a = CheckGnOnline(data)
    a.output()
    exit(0)