occupant_version.mjs

// module: True

/* Class to represent a OccupantVersion version*/

import { find_all_json_files } from "./file_helper.mjs";
import { Occupant, OccupantProduct, OccupantSupplier, OccupantPhysiology } from "./occupant.mjs";
import { JSPath } from "./path.mjs";

class OccupantVersion {
    /**
     * Class to store supported Occupant versions with getters for
     * product names based on supplier and product. It is not instantiable. It behaves like a singleton
     * where the files should only be imported once.
     *
     * if we want to reimport the json files we simply need to call OccupantVersion.importAllOccupantJSONFiles()
     * again and it will overwrite this._occupants property returned by OccupantVersion.occupants
     */

    /** Occupants from json files
     * @type {Occupant[]} */
    static get occupants() {
        if (!this._occupants) OccupantVersion.importAllOccupantJSONFiles();

        return this._occupants;
    }

    /**
     * This function scrapes the Occupant directory for the Occupant json files
     * and imports the data for them. It also checks that the JSON files are all valid and
     * skips them if they are not (printing out a warning message)
     */
    static importAllOccupantJSONFiles() {
        this._occupants = [];

        var occupants_directory = JSPath.GetOccupantsDirectory();

        try {
            //throw an error if it cannot find the occupants folder. This may be because user has moved it.
            if (!File.Exists(occupants_directory)) {
                throw new Error(`Could not find occupants directory here:\n${occupants_directory}`);
            }

            var file_list = find_all_json_files(occupants_directory);

            //throw an error if it cannot find the any occupant json files.
            if (file_list.length == 0) {
                throw new Error(`Failed to find any occupant data json files in ${occupants_directory}`);
            }
        } catch (error) {
            ErrorMessage(`${error}\nTerminate workflow`);
            Exit();
        }

        let names = []; //store the names of added occupants so far

        for (let filepath of file_list) {
            let occupant;

            try {
                if (/\.json$/i.test(filepath)) {
                    occupant = Occupant.FromJSONFile(filepath);

                    if (occupant) {
                        if (names.indexOf(occupant.name) == -1) {
                            this.occupants.push(occupant);
                            names.push(occupant.name);
                        } else
                            WarningMessage(
                                `Failed to parse occupant data from ${filepath} as it was not unique so skipping file`
                            );
                    } else WarningMessage(`Failed to parse occupant data from ${filepath} so skipping file`);

                    //TODO write check for occupant (or add check to FromJSONFile and throw errors)
                }
            } catch (error) {
                ErrorMessage(`${error} so skipping file ${filepath}`);
            }
        }

        //return occupants;
    }

    // /* Static methods */

    /**
     * Returns an array of all the Occupant Versions. Equivalent to OccupantVersion.occupants
     * @returns {Occupant[]}
     * @example
     * let occupants = OccupantVersion.GetAll();
     */
    static GetAll() {
        return OccupantVersion.occupants;
    }

    /**
     * Returns an array of all the Occupant Version names
     * @returns {string[]}
     * @example
     * let occupant_names = OccupantVersion.GetAllNames();
     */
    static GetAllNames() {
        let occupants = OccupantVersion.GetAll();
        let names = [];

        for (let occupant of occupants) {
            names.push(occupant.name);
        }

        return names;
    }

    /**
     * Returns the Occupant Version matching the name
     * @param {string} name
     * @returns {Occupant}
     * @example
     * let occupant = OccupantVersion.GetFromName("Humanetics SID IIs SBLD v.4.3.1");
     */
    static GetFromName(name) {
        let occupants = OccupantVersion.GetAll();

        for (let occupant of occupants) {
            if (occupant.name == name) {
                return occupant;
            }
        }

        throw new Error(`Unsupported version name ${name} in GetFromName`);
    }

    /**
     * Returns an array of all the Occupant Version names made by the supplier
     * @param {string} supplier
     * @returns {string[]} supplier names
     * @example
     * let supplier_names = OccupantVersion.GetOnlySupplier(OccupantSupplier.ATD);
     */
    static GetOnlySupplier(supplier) {
        let versions = OccupantVersion.GetAll();
        let names = [];

        if (!OccupantSupplier.Valid(supplier)) {
            throw new Error(`Invalid supplier ${supplier} in GetOnlySupplier`);
        }

        for (let version of versions) {
            if (version.supplier == supplier) {
                names.push(version.name);
            }
        }

        return names;
    }

    /**
     * The user data object in the workflow file
     * @typedef {Object} Filters
     * @property {string[]} suppliers list of valid suppliers
     * @property {string[]} products list of valid products
     * @property {string[]} physiologies list of valid physiologies
     */

    /**
     * Returns an object of the valid filters for supplier, product, and physiology of all the Occupant Version names that are passes
     * @param {string[]} version_names
     * @returns {Filters} of {suppliers:[], products:[], physiologies:[]}
     * @example
     * let filters = OccupantVersion.GetValidFilters(version_names);
     */
    static GetValidFilters(version_names) {
        let version;
        // "all" is always a valid filter
        let suppliers = ["all"];
        let products = ["all"];
        let physiologies = ["all"];
        for (let name of version_names) {
            version = OccupantVersion.GetFromName(name);
            suppliers.push(version.supplier);
            products.push(version.product);
            physiologies.push(version.physiology);
        }

        return {
            //set strips out duplicates and spread operator converts set back to array
            //note cannot use Set class as PRIMER uses Set class
            suppliers: suppliers, //[...new Set(suppliers)],
            products: products, //[...new Set(products)],
            physiologies: physiologies // [...new Set(physiologies)]
        };
    }

    /**
     * Returns an array of all the Occupant Version names of type product
     * @param {string} product
     * @returns {string[]}
     * @example
     * let product_names = OccupantVersion.GetOnlyProduct(OccupantProduct.HIII);
     */
    static GetOnlyProduct(product) {
        let versions = OccupantVersion.GetAll();
        let names = [];

        if (!OccupantProduct.Valid(product)) {
            throw new Error(`Invalid product ${product} in GetOnlyProduct`);
        }

        for (let version of versions) {
            if (version.product == product) {
                names.push(version.name);
            }
        }

        return names;
    }

    /**
     * Filter the versions that match the input params.
     * Note that passing 'ALL' (or invalid value) for any parameter means that all occupant versions will be considered
     * @param {string} supplier
     * @param {string} product
     * @param {string} physiology
     * @returns {string[]} array of occupant names
     * @example
     * all_50thPercentileMale_hybrid3_products = OccupantVersion.GetOnly('ALL', OccupantProduct.HIII, OccupantPhysiology.M50);
     */
    static GetOnly(supplier, product, physiology) {
        let versions = OccupantVersion.GetAll();
        let temp_versions = [];

        if (OccupantSupplier.Valid(supplier)) {
            for (let version of versions) {
                if (version.supplier == supplier) {
                    temp_versions.push(version);
                }
            }
            versions = temp_versions;
            temp_versions = [];
        }

        if (OccupantProduct.Valid(product)) {
            for (let version of versions) {
                if (version.product == product) {
                    temp_versions.push(version);
                }
            }
            versions = temp_versions;
            temp_versions = [];
        }

        if (OccupantPhysiology.Valid(physiology)) {
            for (let version of versions) {
                if (version.physiology == physiology) {
                    temp_versions.push(version);
                }
            }
            versions = temp_versions;
            temp_versions = [];
        }

        let names = [];
        for (let version of versions) {
            names.push(version.name);
        }

        return names;
    }

    /*could add getter to filter on physiology,
     *but at the momement there are not many so will stick with just supplier and product
     */
}

export { OccupantVersion };