// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
 
+import { ACElectricUtils, DCElectricUtils } from '../utils/ElectricUtils';
 import {
   AvailabilityType,
   BootNotificationRequest,
   ConfigurationKey,
 } from '../types/ChargingStationOcppConfiguration';
 import ChargingStationTemplate, {
+  AmpereUnits,
   CurrentType,
   PowerUnits,
   Voltage,
       : defaultVoltageOut;
   }
 
+  public getMaximumConfiguredPower(): number | undefined {
+    let maximumConfiguredPower =
+      (this.stationInfo['maxPower'] as number) ?? this.stationInfo.maximumPower;
+    if (this.getAmperageLimitation() < this.stationInfo.maximumAmperage) {
+      maximumConfiguredPower =
+        this.getCurrentOutType() === CurrentType.AC
+          ? ACElectricUtils.powerTotal(
+              this.getNumberOfPhases(),
+              this.getVoltageOut(),
+              this.getAmperageLimitation()
+            )
+          : DCElectricUtils.power(this.getVoltageOut(), this.getAmperageLimitation());
+    }
+    return maximumConfiguredPower;
+  }
+
   public getTransactionIdTag(transactionId: number): string | undefined {
     for (const connectorId of this.connectors.keys()) {
       if (connectorId > 0 && this.getConnectorStatus(connectorId).transactionId === transactionId) {
     if (!Utils.isEmptyArray(stationInfo.power)) {
       stationInfo.power = stationInfo.power as number[];
       const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationInfo.power.length);
-      stationInfo.maxPower =
+      stationInfo.maximumPower =
         stationInfo.powerUnit === PowerUnits.KILO_WATT
           ? stationInfo.power[powerArrayRandomIndex] * 1000
           : stationInfo.power[powerArrayRandomIndex];
     } else {
       stationInfo.power = stationInfo.power as number;
-      stationInfo.maxPower =
+      stationInfo.maximumPower =
         stationInfo.powerUnit === PowerUnits.KILO_WATT
           ? stationInfo.power * 1000
           : stationInfo.power;
     this.bootNotificationRequest = this.createBootNotificationRequest(this.stationInfo);
     this.ocppConfiguration = this.getOcppConfiguration();
     delete this.stationInfo.Configuration;
-    this.saveStationInfo();
     // Build connectors if needed
     const maxConnectors = this.getMaxNumberOfConnectors();
     if (maxConnectors <= 0) {
         }
       }
     }
+    // The connectors attribute need to be initialized
+    this.stationInfo.maximumAmperage = this.getMaximumAmperage();
+    this.saveStationInfo();
     // Avoid duplication of connectors related information in RAM
     delete this.stationInfo.Connectors;
     // Initialize transaction attributes on connectors
     ) {
       this.deleteConfigurationKey(this.getSupervisionUrlOcppKey(), { save: false });
     }
+    if (
+      this.stationInfo.amperageLimitationOcppKey &&
+      !this.getConfigurationKey(this.stationInfo.amperageLimitationOcppKey)
+    ) {
+      this.addConfigurationKey(
+        this.stationInfo.amperageLimitationOcppKey,
+        (this.stationInfo.maximumAmperage * this.getAmperageLimitationUnitDivider()).toString()
+      );
+    }
     if (!this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)) {
       this.addConfigurationKey(
         StandardParametersKey.SupportedFeatureProfiles,
     return maxConnectors;
   }
 
+  private getMaximumAmperage(): number | undefined {
+    switch (this.getCurrentOutType()) {
+      case CurrentType.AC:
+        return ACElectricUtils.amperagePerPhaseFromPower(
+          this.getNumberOfPhases(),
+          (this.stationInfo['maxPower'] as number) ??
+            this.stationInfo.maximumPower / this.getNumberOfConnectors(),
+          this.getVoltageOut()
+        );
+      case CurrentType.DC:
+        return DCElectricUtils.amperage(this.stationInfo.maximumPower, this.getVoltageOut());
+    }
+  }
+
+  private getAmperageLimitationUnitDivider(): number {
+    let unitDivider = 1;
+    switch (this.stationInfo.amperageLimitationUnit) {
+      case AmpereUnits.DECI_AMPERE:
+        unitDivider = 10;
+        break;
+      case AmpereUnits.CENTI_AMPERE:
+        unitDivider = 100;
+        break;
+      case AmpereUnits.MILLI_AMPERE:
+        unitDivider = 1000;
+        break;
+    }
+    return unitDivider;
+  }
+
+  private getAmperageLimitation(): number | undefined {
+    if (
+      this.stationInfo.amperageLimitationOcppKey &&
+      this.getConfigurationKey(this.stationInfo.amperageLimitationOcppKey)
+    ) {
+      return (
+        Utils.convertToInt(
+          this.getConfigurationKey(this.stationInfo.amperageLimitationOcppKey).value
+        ) / this.getAmperageLimitationUnitDivider()
+      );
+    }
+  }
+
   private async startMessageSequence(): Promise<void> {
     if (this.stationInfo.autoRegister) {
       await this.ocppRequestService.sendMessageHandler<BootNotificationResponse>(
 
       } measurand value`;
       const powerMeasurandValues = {} as MeasurandValues;
       const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1;
-      const maxPower = Math.round(
-        chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider
+      const maximumPower = Math.round(
+        chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider
       );
-      const maxPowerPerPhase = Math.round(
-        chargingStation.stationInfo.maxPower /
+      const maximumPowerPerPhase = Math.round(
+        chargingStation.getMaximumConfiguredPower() /
           chargingStation.stationInfo.powerDivider /
           chargingStation.getNumberOfPhases()
       );
             powerMeasurandValues.L1 =
               phase1FluctuatedValue ??
               defaultFluctuatedPowerPerPhase ??
-              Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider);
+              Utils.getRandomFloatRounded(maximumPowerPerPhase / unitDivider);
             powerMeasurandValues.L2 =
               phase2FluctuatedValue ??
               defaultFluctuatedPowerPerPhase ??
-              Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider);
+              Utils.getRandomFloatRounded(maximumPowerPerPhase / unitDivider);
             powerMeasurandValues.L3 =
               phase3FluctuatedValue ??
               defaultFluctuatedPowerPerPhase ??
-              Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider);
+              Utils.getRandomFloatRounded(maximumPowerPerPhase / unitDivider);
           } else {
             powerMeasurandValues.L1 = powerSampledValueTemplate.value
               ? Utils.getRandomFloatFluctuatedRounded(
                   powerSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT
                 )
-              : Utils.getRandomFloatRounded(maxPower / unitDivider);
+              : Utils.getRandomFloatRounded(maximumPower / unitDivider);
             powerMeasurandValues.L2 = 0;
             powerMeasurandValues.L3 = 0;
           }
                 powerSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
               )
-            : Utils.getRandomFloatRounded(maxPower / unitDivider);
+            : Utils.getRandomFloatRounded(maximumPower / unitDivider);
           break;
         default:
           logger.error(errMsg);
         )
       );
       const sampledValuesIndex = meterValue.sampledValue.length - 1;
-      const maxPowerRounded = Utils.roundTo(maxPower / unitDivider, 2);
+      const maxPowerRounded = Utils.roundTo(maximumPower / unitDivider, 2);
       if (
         Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPowerRounded ||
         debug
           )
         );
         const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
-        const maxPowerPerPhaseRounded = Utils.roundTo(maxPowerPerPhase / unitDivider, 2);
+        const maxPowerPerPhaseRounded = Utils.roundTo(maximumPowerPerPhase / unitDivider, 2);
         if (
           Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
             maxPowerPerPhaseRounded ||
         OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
       } measurand value`;
       const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
-      let maxAmperage: number;
+      let maximumAmperage: number;
       switch (chargingStation.getCurrentOutType()) {
         case CurrentType.AC:
-          maxAmperage = ACElectricUtils.amperagePerPhaseFromPower(
+          maximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
             chargingStation.getNumberOfPhases(),
-            chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider,
+            chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider,
             chargingStation.getVoltageOut()
           );
           if (chargingStation.getNumberOfPhases() === 3) {
             currentMeasurandValues.L1 =
               phase1FluctuatedValue ??
               defaultFluctuatedAmperagePerPhase ??
-              Utils.getRandomFloatRounded(maxAmperage);
+              Utils.getRandomFloatRounded(maximumAmperage);
             currentMeasurandValues.L2 =
               phase2FluctuatedValue ??
               defaultFluctuatedAmperagePerPhase ??
-              Utils.getRandomFloatRounded(maxAmperage);
+              Utils.getRandomFloatRounded(maximumAmperage);
             currentMeasurandValues.L3 =
               phase3FluctuatedValue ??
               defaultFluctuatedAmperagePerPhase ??
-              Utils.getRandomFloatRounded(maxAmperage);
+              Utils.getRandomFloatRounded(maximumAmperage);
           } else {
             currentMeasurandValues.L1 = currentSampledValueTemplate.value
               ? Utils.getRandomFloatFluctuatedRounded(
                   currentSampledValueTemplate.fluctuationPercent ??
                     Constants.DEFAULT_FLUCTUATION_PERCENT
                 )
-              : Utils.getRandomFloatRounded(maxAmperage);
+              : Utils.getRandomFloatRounded(maximumAmperage);
             currentMeasurandValues.L2 = 0;
             currentMeasurandValues.L3 = 0;
           }
           );
           break;
         case CurrentType.DC:
-          maxAmperage = DCElectricUtils.amperage(
-            chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider,
+          maximumAmperage = DCElectricUtils.amperage(
+            chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider,
             chargingStation.getVoltageOut()
           );
           currentMeasurandValues.allPhases = currentSampledValueTemplate.value
                 currentSampledValueTemplate.fluctuationPercent ??
                   Constants.DEFAULT_FLUCTUATION_PERCENT
               )
-            : Utils.getRandomFloatRounded(maxAmperage);
+            : Utils.getRandomFloatRounded(maximumAmperage);
           break;
         default:
           logger.error(errMsg);
       );
       const sampledValuesIndex = meterValue.sampledValue.length - 1;
       if (
-        Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage ||
+        Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maximumAmperage ||
         debug
       ) {
         logger.error(
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
           }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
             meterValue.sampledValue[sampledValuesIndex].value
-          }/${maxAmperage}`
+          }/${maximumAmperage}`
         );
       }
       for (
         const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
         if (
           Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
-            maxAmperage ||
+            maximumAmperage ||
           debug
         ) {
           logger.error(
               meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
             }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
               meterValue.sampledValue[sampledValuesPerPhaseIndex].value
-            }/${maxAmperage}`
+            }/${maximumAmperage}`
           );
         }
       }
       );
       const unitDivider =
         energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
-      const maxEnergyRounded = Utils.roundTo(
-        ((chargingStation.stationInfo.maxPower / chargingStation.stationInfo.powerDivider) *
+      const maximumEnergyRounded = Utils.roundTo(
+        ((chargingStation.getMaximumConfiguredPower() / chargingStation.stationInfo.powerDivider) *
           interval) /
           (3600 * 1000),
         2
             parseInt(energySampledValueTemplate.value),
             energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
           )
-        : Utils.getRandomFloatRounded(maxEnergyRounded);
+        : Utils.getRandomFloatRounded(maximumEnergyRounded);
       // Persist previous value on connector
       if (
         connector &&
         )
       );
       const sampledValuesIndex = meterValue.sampledValue.length - 1;
-      if (energyValueRounded > maxEnergyRounded || debug) {
+      if (energyValueRounded > maximumEnergyRounded || debug) {
         logger.error(
           `${chargingStation.logPrefix()} MeterValues measurand ${
             meterValue.sampledValue[sampledValuesIndex].measurand ??
             OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
           }: connectorId ${connectorId}, transaction ${
             connector.transactionId
-          }, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(
+          }, value: ${energyValueRounded}/${maximumEnergyRounded}, duration: ${Utils.roundTo(
             interval / (3600 * 1000),
             4
           )}h`