import sys
import json
import subprocess

from os import getenv


# Calls a subprocess to find all IOCs containing an IOC.pvlist file. Then a dict object is constructed
#   consisting of (key=IOC Name, value={key}/iocInfo/IOC.pvlist)
def get_ioc_data():
    # Save path to $IOC_DATA for removal later and initialize ioc_data
    ioc_path = getenv('IOC_DATA')
    ioc_list = []

    # Call find and capture the ouput as a list of IOC directories. Save individual IOC names to ioc_data
    try:
        output = subprocess.check_output("find $IOC_DATA/*/iocInfo/ -iname IOC.pvlist",
                                         stderr=subprocess.DEVNULL, text=True, shell=True).split()
        for curr_dir in output:
            curr_dir = curr_dir.replace(ioc_path, '')
            ioc_list.append(curr_dir.split('/')[1])

    # If there is an error, then parse the output based on the returncode
    except subprocess.CalledProcessError as e:
        if e.returncode != 0:
            print("ERROR: Find command failed")
        else:
            print("ERROR: Unknown subprocess error occurred")

    # Populate the values of ioc_dict based on the iocs in ioc_list
    ioc_dict = dict()
    for ioc in ioc_list:
        with open(ioc_path + "/" + ioc + "/iocInfo/IOC.pvlist", 'r') as f:
            for line in f.readlines():
                pv = line.strip().rsplit(',', 1)[0]
                ioc_dict[pv] = ioc

    return ioc_dict


def main():
    # Load information stored in .json files (Device names, Area names, etc.)
    with open(sys.argv[1], 'r') as f:
        device_data = json.load(f)
    with open(sys.argv[2], 'r') as f:
        area_data = json.load(f)

    # Determine if we print out the error causing the PV to be invalid
    # Also determine if we need to include which IOC contains a given PV
    err_out = sys.argv[3] == '1'
    check_iocs = sys.argv[4] == '1'
    all_ioc_data = get_ioc_data()

    # Read input and parse into list of pv names (str)
    for pv in sys.stdin:
        pv = pv.strip()
        # Allows interrupt if PVs are input manually
        if 'exit' == pv.lower():
            break

        # Process each pv and split it at ':'
        fields = pv.split(":")
        err_code = []

        # Check if PV is invalid (<3 ':' | invalid device | invalid area). If so, save the
        #   error code to be accessed later.
        if len(fields) < 4:
            err_code.append("<3 :")
        else:
            if not (fields[1] in area_data["areas"] or fields[1] in area_data["cryo_areas"]):
                # Add exception for area B### in LCLS
                if not (sys.argv[2][12:16] == "lcls"
                        and 2 <= len(fields[1]) <= 4
                        and fields[1][0] == 'B'
                        and fields[1][1:].isnumeric()):
                    err_code.append(fields[1] + " not found")
            if not (fields[0] in device_data["devices"] or fields[0] in device_data["cryo_devices"]):
                err_code.append(fields[0] + "  not found")

        # If the PV is invalid, then print the PV. Include the IOC and/or Error if requested.
        if not err_code:
            continue
        output = pv
        ioc = all_ioc_data.get(pv, 'ERROR: Not in $IOC_DATA')
        if ioc == "pcds":
            continue
        if check_iocs:
            output = ioc + ',' + output
        if err_out:
            output += ',' + err_code[-1]
        print(output)


if __name__ == "__main__":
    main()
