"""
    !!! YOU MUST DELETE THE EXISTING {MCM_NUMBER}.JSON FILES BEFORE RUNNING THE SCRIPT !!!
    This script is used to generate tags.json files for each MCM folder in the Detailed-Views folder.
"""

import json
import os
import re
import copy

default_view_path = r"C:\Program Files\Inductive Automation\Ignition\data\projects\BNA8\com.inductiveautomation.perspective\views\autStand\Detailed_Views"

def merge_tags(t1, t2):
    """
    Merge two tag folder trees, deduplicating tags by name within each folder, preserving folder structure.
    """
    if not (isinstance(t1, dict) and isinstance(t2, dict)):
        return copy.deepcopy(t1) if t2 is None else copy.deepcopy(t2)

    if t1.get('tagType') == 'Folder' and t2.get('tagType') == 'Folder':
        merged = {k: v for k, v in t1.items() if k != 'tags'}
        merged['tags'] = []
        tags1 = {tag['name']: tag for tag in t1.get('tags', [])}
        tags2 = {tag['name']: tag for tag in t2.get('tags', [])}
        all_names = set(tags1) | set(tags2)
        for name in sorted(all_names):
            tag1 = tags1.get(name)
            tag2 = tags2.get(name)
            if tag1 and tag2:
                merged['tags'].append(merge_tags(tag1, tag2))
            elif tag1:
                merged['tags'].append(copy.deepcopy(tag1))
            elif tag2:
                merged['tags'].append(copy.deepcopy(tag2))
        return merged
    else:
        return copy.deepcopy(t1) if t2 is None else copy.deepcopy(t2)

# Filtering and classification rules
def classify_device(name, mcm_device_name):
    skip_keywords = ["Line", "Image", "Button", "Camera", "END", "image", "PR", "GS", "ERSC", "DIV"]

    def create_parameters(mcm_name):
        return {
            "parameters": {
                "MCM": {
                    "dataType": "String",
                    "value": mcm_name
                }
            }
        }

    if any(re.search(rf'{kw}\d+$', name) for kw in ["FIO", "SIO", "DPM"]):
        for kw in ["FIO", "SIO", "DPM"]:
            if re.search(rf'{kw}\d+$', name):
                result = {"folder": f"IO_BLOCK/{kw}", "typeId": "IO_BLOCK", "tagType": "UdtInstance", "name": name}
                result.update(create_parameters(mcm_device_name))
                return result

    # Beacon rules
    beacon_match = re.search(r'_BCN\d+_(R|G|A|H|B)$', name)
    if beacon_match:
        result = {
            "folder": "Beacon",
            "typeId": "Beacon",
            "tagType": "UdtInstance",
            "name": name
        }
        result.update(create_parameters(mcm_device_name))
        return result


    # Photoeye rules with correct UDT typeId
    for kw in ["TPE", "LRPE", "PE"]:
        if re.search(rf'{kw}\d*$', name):
            udt_path = f"PE/{kw}"  # full UDT path
            result = {"folder": f"Photoeye/{kw}", "typeId": udt_path, "tagType": "UdtInstance", "name": name}
            result.update(create_parameters(mcm_device_name))
            return result

    # Encoders rules with correct UDT typeId
    for kw in ["ENSH", "ENW"]:
        if re.search(rf'{kw}\d*$', name):
            udt_path = f"Encoder"  # full UDT path
            result = {"folder": f"Encoder/{kw}", "typeId": udt_path, "tagType": "UdtInstance", "name": name}
            result.update(create_parameters(mcm_device_name))
            return result

    if "PRX" in name:
        result = {"folder": "PRX", "typeId": "PRX", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result

    if "PR" in name or any(kw in name for kw in skip_keywords):
        return None

    if "EPC" in name:
        result = {"folder": "Station/EPC", "typeId": "Station/EPC", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result
    
    if "SS1" in name or "SS2" in name:
        result = {"folder": "Station/SS", "typeId": "Station/SS", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result

    if name.startswith("S03") and "JR" in name:
        result = {"folder": "Station/Chute_JR", "typeId": "Station/Chute_JR", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result
    

    if "JR" in name:
        result = {"folder": "Station/JR", "typeId": "Station/JR", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result

    if "EN" in name:
        result = {"folder": "Station/EN", "typeId": "Station/EN", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result

    if any(kw in name for kw in ["DPM", "FIO", "SIO", "HUB"]):
        for kw in ["DPM", "FIO", "SIO", "HUB"]:
            if kw in name:
                result = {"folder": f"IO_BLOCK/{kw}", "typeId": "IO_BLOCK", "tagType": "UdtInstance", "name": name}
                result.update(create_parameters(mcm_device_name))
                return result

    if "TIPPER" in name:
        result = {"folder": "Conveyor/Tipper", "typeId": "Conveyor/Tipper", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result

    if "EX" in name:
        result = {"folder": "Conveyor/Extendo", "typeId": "Conveyor/Extendo", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result

    if "FL" in name and "CH" in name:
        result = {"folder": "Chute/FL_CHUTE", "typeId": "Chute/FL_CHUTE", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result
        
    if "VFD" in name:
        result = {"folder": "VFD", "typeId": "VFD", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result
    
    if "SOL" in name:
        result = {"folder": "SOL", "typeId": "SOL", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result
    
    if "CH" in name:
        result = {"folder": "Chute", "typeId": "Chute", "tagType": "UdtInstance", "name": name}
        result.update(create_parameters(mcm_device_name))
        return result

    result = {"folder": "Conveyor", "typeId": "Conveyor", "tagType": "UdtInstance", "name": name}
    result.update(create_parameters(mcm_device_name))
    return result

# Recursive extraction of all nested devices
def extract_all_devices(children):
    devices = []
    for child in children:
        meta = child.get("meta", {})
        name = meta.get("name")
        if name:
            devices.append(name)
        nested = child.get("children", [])
        if nested:
            devices.extend(extract_all_devices(nested))
    return devices

def main():
    if not os.path.isdir(default_view_path):
        print(f"Directory not found: {default_view_path}")
        return

    for folder_name in os.listdir(default_view_path):
        folder_path = os.path.join(default_view_path, folder_name)
        if not os.path.isdir(folder_path):
            continue
        view_json_path = os.path.join(folder_path, "view.json")
        if not os.path.isfile(view_json_path):
            print(f"No view.json in {folder_path}, skipping.")
            continue

        with open(view_json_path, encoding="utf-8") as f:
            view = json.load(f)

        children = view.get("root", {}).get("children", [])
        all_device_names = extract_all_devices(children)

        mcm_device_name = next((n for n in all_device_names if "MCM" in n), folder_name)
        devices = []
        seen_names = set()

        for name in all_device_names:
            if name not in seen_names:
                seen_names.add(name)
                classified = classify_device(name, mcm_device_name)
                if classified:
                    devices.append(classified)

        # Initialize folder structure
        folders = {
            "IO_BLOCK": {"FIO": [], "SIO": [], "DPM": []},
            "Station": {"JR": [], "Chute_JR": [], "SS": [], "EPC": [], "EN": []},
            "Photoeye": {"PE": [], "TPE": [], "LRPE": []},
            "Beacon": [],
            "Encoder": {"ENSH": [], "ENW": []},
            "Chute": [],
            "Conveyor":[],
            "VFD": [],
            "PRX":[],
            "SOL": []

        }

        # Organize devices into folders
        for dev in devices:
            folder_path = dev["folder"].split("/")
            if folder_path[0] == "IO_BLOCK":
                folders["IO_BLOCK"][folder_path[1]].append(dev)
            elif folder_path[0] == "Station":
                folders["Station"][folder_path[1]].append(dev)
            elif folder_path[0] == "SOL":
                folders["SOL"].append(dev)
            elif folder_path[0] == "Chute":
                folders["Chute"].append(dev)
            elif folder_path[0] == "Photoeye":
                folders["Photoeye"][folder_path[1]].append(dev)
            elif folder_path[0] == "Beacon":
                folders["Beacon"].append(dev)
            elif folder_path[0] == "Encoder":
                folders["Encoder"][folder_path[1]].append(dev)
            elif folder_path[0] == "Conveyor":
                folders["Conveyor"].append(dev)
            elif folder_path[0] == "VFD":
                folders["VFD"].append(dev)
            elif folder_path[0] == "PRX":
                folders["PRX"].append(dev)

        # Build output structure
        output = {
            "name": mcm_device_name,
            "tagType": "Folder",
            "tags": [
                {"name": mcm_device_name, "typeId": "MCM", "tagType": "UdtInstance", "tags": []},
                {
                    "name": "IO_BLOCK",
                    "tagType": "Folder",
                    "tags": [
                        {"name": "FIO", "tagType": "Folder", "tags": folders["IO_BLOCK"]["FIO"]},
                        {"name": "SIO", "tagType": "Folder", "tags": folders["IO_BLOCK"]["SIO"]},
                        {"name": "DPM", "tagType": "Folder", "tags": folders["IO_BLOCK"]["DPM"]},
                    ],
                },
                {
                    "name": "Station",
                    "tagType": "Folder",
                    "tags": [
                        {"name": "JR", "tagType": "Folder", "tags": folders["Station"]["JR"]},
                        {"name": "Chute_JR", "tagType": "Folder", "tags": folders["Station"]["Chute_JR"]},
                        {"name": "SS", "tagType": "Folder", "tags": folders["Station"]["SS"]},
                        {"name": "EPC", "tagType": "Folder", "tags": folders["Station"]["EPC"]},
                        {"name": "EN", "tagType": "Folder", "tags": folders["Station"]["EN"]},
                    ],
                },
                {
                    "name": "Photoeye",
                    "tagType": "Folder",
                    "tags": [
                        {"name": "PE", "tagType": "Folder", "tags": folders["Photoeye"]["PE"]},
                        {"name": "TPE", "tagType": "Folder", "tags": folders["Photoeye"]["TPE"]},
                        {"name": "LRPE", "tagType": "Folder", "tags": folders["Photoeye"]["LRPE"]},
                    ],
                },
                {"name": "Chute", "tagType": "Folder", "tags": folders["Chute"]},
                {
                    "name": "Encoder",
                    "tagType": "Folder",
                    "tags": [
                        {"name": "ENSH", "tagType": "Folder", "tags": folders["Encoder"]["ENSH"]},
                        {"name": "ENW", "tagType": "Folder", "tags": folders["Encoder"]["ENW"]},
                    ],
                },
                    {
                        "name": "Beacon",
                        "tagType": "Folder",
                        "tags": folders["Beacon"],
                    },

                {
                    "name": "Conveyor",
                    "tagType": "Folder",
                    "tags": folders["Conveyor"],
                },
                {
                    "name": "VFD",
                    "tagType": "Folder",
                    "tags": folders["VFD"],
                },
                {
                    "name": "PRX",
                    "tagType": "Folder",
                    "tags": folders["PRX"],
                },
                {
                    "name": "SOL",
                    "tagType": "Folder",
                    "tags": folders["SOL"],
                },

            ],
        }

        mcm_match = re.match(r'^(MCM\d+)', folder_name)
        mcm_name = mcm_match.group(1) if mcm_match else folder_name

        output_json_path = os.path.join(os.path.dirname(__file__), f"{mcm_name}.json")

        if os.path.isfile(output_json_path):
            print(f"Existing file found for {mcm_name}, merging data from folder '{folder_name}'...")
            with open(output_json_path, "r", encoding="utf-8") as f:
                existing_data = json.load(f)
            merged_output = merge_tags(existing_data, output)
            with open(output_json_path, "w", encoding="utf-8") as f:
                json.dump(merged_output, f, indent=2)
            print(f"Merged tags JSON updated at: {output_json_path}")
        else:
            with open(output_json_path, "w", encoding="utf-8") as f:
                json.dump(output, f, indent=2)
            print(f"New tags JSON generated for {mcm_name} from folder '{folder_name}' at: {output_json_path}")

if __name__ == "__main__":
    main()
