// ChildRegistrationUtils.js
class ChildRegistrationUtils {
  constructor(mainClass) {
    this.mainClass = mainClass; // Reference to the main class
    this.logger = mainClass.logger;
  }

  async registerChild(child, positionVsParent) {

    this.logger.debug(`Registering child: ${child.id} with position=${positionVsParent}`);
    const { softwareType } = child.config.functionality;
    const { name, id, unit } = child.config.general;
    const { category = "", type = "" } = child.config.asset || {};
    console.log(`Registering child: ${name}, id: ${id}, softwareType: ${softwareType}, category: ${category}, type: ${type}, positionVsParent: ${positionVsParent}` );
    const emitter = child.emitter;

    //define position vs parent in child
    child.positionVsParent = positionVsParent;
    child.parent = this.mainClass;

    if (!this.mainClass.child) this.mainClass.child = {};
    if (!this.mainClass.child[softwareType])
      this.mainClass.child[softwareType] = {};
    if (!this.mainClass.child[softwareType][category])
      this.mainClass.child[softwareType][category] = {};
    if (!this.mainClass.child[softwareType][category][type])
      this.mainClass.child[softwareType][category][type] = {};

    // Use an array to handle multiple categories
    if (!Array.isArray(this.mainClass.child[softwareType][category][type])) {
      this.mainClass.child[softwareType][category][type] = [];
    }

    // Push the new child to the array of the mainclass so we can track the childs
    this.mainClass.child[softwareType][category][type].push({
      name,
      id,
      unit,
      emitter,
    });

    //then connect the child depending on the type type etc..
    this.connectChild(
      id,
      softwareType,
      emitter,
      category,
      child,
      type,
      positionVsParent
    );
  }

  connectChild(
    id,
    softwareType,
    emitter,
    category,
    child,
    type,
    positionVsParent
  ) {
    this.logger.debug(
      `Connecting child id=${id}: desc=${softwareType}, category=${category},type=${type}, position=${positionVsParent}`
    );

    switch (softwareType) {
      case "measurement":
        this.logger.debug(
          `Registering measurement child: ${id} with category=${category}`
        );
        this.connectMeasurement(child, type, positionVsParent);
        break;

      case "machine":
        this.logger.debug(`Registering complete machine child: ${id}`);
        this.connectMachine(child);
        break;

      case "valve":
        this.logger.debug(`Registering complete valve child: ${id}`);
        this.connectValve(child);
        break;

      case "machineGroup":
        this.logger.debug(`Registering complete machineGroup child: ${id}`);
        this.connectMachineGroup(child);
        break;

      case "actuator":
        this.logger.debug(`Registering linear actuator child: ${id}`);
        this.connectActuator(child,positionVsParent);
        break;

      default:
        this.logger.error(`Child registration unrecognized desc: ${desc}`);
        this.logger.error(`Unrecognized softwareType: ${softwareType}`);
    }
  }

  connectMeasurement(child, type, position) {
    this.logger.debug(
      `Connecting measurement child: ${type} with position=${position}`
    );

    // Check if type is valid
    if (!type) {
      this.logger.error(`Invalid type for measurement: ${type}`);
      return;
    }

    // initialize the measurement to a number - logging each step for debugging
    try {
      this.logger.debug(
        `Initializing measurement: ${type}, position: ${position} value: 0`
      );
      const typeResult = this.mainClass.measurements.type(type);
      const variantResult = typeResult.variant("measured");
      const positionResult = variantResult.position(position);
      positionResult.value(0);

      this.logger.debug(
        `Subscribing on mAbs event for measurement: ${type}, position: ${position}`
      );
      // Listen for the mAbs event and update the measurement

      this.logger.debug(
        `Successfully initialized measurement: ${type}, position: ${position}`
      );
    } catch (error) {
      this.logger.error(`Failed to initialize measurement: ${error.message}`);
      return;
    }

    //testing new emitter strategy
    child.measurements.emitter.on("newValue", (data) => {
      this.logger.warn(
        `Value change event received for measurement: ${type}, position: ${position}, value: ${data.value}`
      );
    });

    child.emitter.on("mAbs", (value) => {
      // Use the same method chaining approach that worked during initialization
      this.mainClass.measurements
        .type(type)
        .variant("measured")
        .position(position)
        .value(value);
      this.mainClass.updateMeasurement("measured", type, value, position);
      //this.logger.debug(`--------->>>>>>>>>Updated measurement: ${type}, value: ${value}, position: ${position}`);
    });
    
  }

  connectMachine(machine) {
    if (!machine) {
      this.logger.error("Invalid machine provided.");
      return;
    }

    const machineId = Object.keys(this.mainClass.machines).length + 1;
    this.mainClass.machines[machineId] = machine;

    this.logger.info(
      `Setting up pressureChange listener for machine ${machineId}`
    );

    machine.emitter.on("pressureChange", () =>
      this.mainClass.handlePressureChange(machine)
    );

    //update of child triggers the handler
    this.mainClass.handleChildChange();

    this.logger.info(`Machine ${machineId} registered successfully.`);
  }

  connectValve(valve) {
    if (!valve) {
      this.logger.warn("Invalid valve provided.");
      return;
    }
    const valveId = Object.keys(this.mainClass.valves).length + 1;
    this.mainClass.valves[valveId] = valve; // Gooit valve object in de valves attribute met valve objects

    valve.state.emitter.on("positionChange", (data) => {
      //ValveGroupController abboneren op klepstand verandering
      this.mainClass.logger.debug(`Position change of valve detected: ${data}`);
      this.mainClass.calcValveFlows();
    }); //bepaal nieuwe flow per valve
    valve.emitter.on("deltaPChange", () => {
      this.mainClass.logger.debug("DeltaP change of valve detected");
      this.mainClass.calcMaxDeltaP();
    }); //bepaal nieuwe max deltaP

    this.logger.info(`Valve ${valveId} registered successfully.`);
  }

  connectMachineGroup(machineGroup) {
    if (!machineGroup) {
        this.logger.warn("Invalid machineGroup provided.");
        return;
    }

    try {
    const machineGroupId = Object.keys(this.mainClass.machineGroups).length + 1;
    this.mainClass.machineGroups[machineGroupId] = machineGroup; 
    } catch (error) {
      this.logger.warn(`Skip machinegroup connnection: ${error.message}`);
    }

    machineGroup.emitter.on("totalFlowChange", (data) => {
      this.mainClass.logger.debug('Total flow change of machineGroup detected');
      this.mainClass.handleInput("parent", "totalFlowChange", data)}); //Geef nieuwe totale flow door aan valveGrouControl

    this.logger.info(`MachineGroup ${machineGroup.config.general.name} registered successfully.`);
    }

  connectActuator(actuator, positionVsParent) {
    if (!actuator) {
      this.logger.warn("Invalid actuator provided.");
      return;
    }

    //Special case gateGroupControl
    if (
      this.mainClass.config.functionality.softwareType == "gateGroupControl"
    ) {
      if (Object.keys(this.mainClass.actuators).length < 2) {
        if (positionVsParent == "downstream") {
          this.mainClass.actuators[0] = actuator;
        }

        if (positionVsParent == "upstream") {
          this.mainClass.actuators[1] = actuator;
        }
        //define emitters
        actuator.state.emitter.on("positionChange", (data) => {
          this.mainClass.logger.debug(`Position change of actuator detected: ${data}`);
          this.mainClass.eventUpdate();
        }); 

        //define emitters
        actuator.state.emitter.on("stateChange", (data) => {
          this.mainClass.logger.debug(`State change of actuator detected: ${data}`);
          this.mainClass.eventUpdate();
        }); 

      } else {
        this.logger.error(
          "Too many actuators registered. Only two are allowed."
        );
      }
    }
  }

  //wanneer hij deze ontvangt is deltaP van een van de valves veranderd (kan ook zijn niet child zijn, maar dat maakt niet uit)
}

module.exports = ChildRegistrationUtils;
