text: /***************************************************************************//**
   * @file
   * @brief For more information on the usage of this plugin see drlc-server.h
   *******************************************************************************
   * # License
   * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
   *******************************************************************************
   *
   * The licensor of this software is Silicon Laboratories Inc. Your use of this
   * software is governed by the terms of Silicon Labs Master Software License
   * Agreement (MSLA) available at
   * www.silabs.com/about-us/legal/master-software-license-agreement. This
   * software is distributed to you in Source Code format and is governed by the
   * sections of the MSLA applicable to Source Code.
   *
   ******************************************************************************/
  
  #include "../../include/af.h"
  #include "../../util/common.h"
  #include "drlc-server.h"
  
  #ifdef UC_BUILD
  #include "drlc-server-config.h"
  #include "zap-cluster-command-parser.h"
  #endif // UC_BUILD
  
  static EmberAfLoadControlEvent scheduledLoadControlEventTable[EMBER_AF_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT][EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE];
  // The following matrix is a map of event order that is sorted by startTime and issuerEventId
  // e.g. [ep][0] -> 2 (index of scheduledLoadControlEventTable)
  //      [ep][1] -> 0
  static uint8_t eventOrderToLoadControlIndex[EMBER_AF_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT][EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE];
  #define ORDER_NOT_SET     0xFF
  
  void emberAfDemandResponseLoadControlClusterServerInitCallback(uint8_t endpoint)
  {
    emAfClearScheduledLoadControlEvents(endpoint); //clear all events at init
  }
  
  #ifdef UC_BUILD
  
  bool emberAfDemandResponseLoadControlClusterGetScheduledEventsCallback(EmberAfClusterCommand *cmd)
  {
    sl_zcl_demand_response_and_load_control_cluster_get_scheduled_events_command_t cmd_data;
    uint8_t i, sent = 0;
    uint8_t ep = emberAfFindClusterServerEndpointIndex(emberAfCurrentEndpoint(),
                                                       ZCL_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_ID);
    uint8_t orderedIndex;
    EmberAfLoadControlEvent event;
  
    if (ep == 0xFF
        || zcl_decode_demand_response_and_load_control_cluster_get_scheduled_events_command(cmd, &cmd_data)
        != EMBER_ZCL_STATUS_SUCCESS) {
      return false;
    }
  
    // CCB 1297
    // startTime = 0 does NOT mean get all events from "now"
  
    // Go through our sorted table (by following the eventOrderToLoadControlIndex
    // map) and send out the scheduled events
    for (i = 0; i < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE; i++) {
      orderedIndex = eventOrderToLoadControlIndex[ep][i];
      // If no more events have been set (and thus, ordered), break
      if (ORDER_NOT_SET == orderedIndex) {
        break;
      }
  
      // CCB 1297: events with startTime = "now" are sent with startTime = 0, not
      // the current time that we internally set it to at the time of creation
      // We do, however, update the duration based on the elapsed time
      // emAfGetScheduledLoadControlEvent() normalizes the duration time
      if (EMBER_SUCCESS != emAfGetScheduledLoadControlEvent(
            emberAfCurrentEndpoint(),
            orderedIndex,
            &event)) {
        continue;
      }
  
      // check how many we have sent, if they have a positive number of events
      // they want returned and we have hit it we should exit.
      if (cmd_data.numberOfEvents != 0 && sent >= cmd_data.numberOfEvents) {
        break;
      }
  
      // If the event is inactive or its start time is before the requested start
      // time we ignore it.
      if (event.source[0] == 0xFF || event.startTime < cmd_data.startTime) {
        continue;
      }
  
      // CCB 1297: filter on issuerEventId if it was sent in command
      if ((cmd_data.issuerEventId != 0xFFFFFFFF)
          && (event.startTime == cmd_data.startTime)
          && (event.eventId < cmd_data.issuerEventId)) {
        continue;
      }
  
      // send the event
      emberAfFillCommandDemandResponseLoadControlClusterLoadControlEvent(event.eventId,
                                                                         event.deviceClass,
                                                                         event.utilityEnrollmentGroup,
                                                                         event.startTime,
                                                                         event.duration,
                                                                         event.criticalityLevel,
                                                                         event.coolingTempOffset,
                                                                         event.heatingTempOffset,
                                                                         event.coolingTempSetPoint,
                                                                         event.heatingTempSetPoint,
                                                                         event.avgLoadPercentage,
                                                                         event.dutyCycle,
                                                                         event.eventControl);
      emberAfSetCommandEndpoints(cmd->apsFrame->destinationEndpoint,
                                 cmd->apsFrame->sourceEndpoint);
      emberAfGetCommandApsFrame()->options |= EMBER_APS_OPTION_SOURCE_EUI64;
      emberAfSendCommandUnicast(EMBER_OUTGOING_DIRECT, cmd->source);
      sent++;  //record that we sent it. and continue
    }
  
    // Bug 13547:
    //   Only send a Default response if there were no events returned.
    //   The LCE messages are the "next" messages in the sequence so no
    //   default response is needed for that successful case.
    if (sent == 0) {
      emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_NOT_FOUND);
    }
  
    return true;
  }
  
  bool emberAfDemandResponseLoadControlClusterReportEventStatusCallback(EmberAfClusterCommand *cmd)
  {
  #ifdef SL_CATALOG_ZIGBEE_DEBUG_PRINT_PRESENT
    sl_zcl_demand_response_and_load_control_cluster_report_event_status_command_t cmd_data;
  
    if (zcl_decode_demand_response_and_load_control_cluster_report_event_status_command(cmd, &cmd_data)
        != EMBER_ZCL_STATUS_SUCCESS) {
      return false;
    }
  
    emberAfDemandResponseLoadControlClusterPrintln("= RX Event Status =");
    emberAfDemandResponseLoadControlClusterPrintln("  eid: %4x", cmd_data.issuerEventId);
    emberAfDemandResponseLoadControlClusterPrintln("   es: %x", cmd_data.eventStatus);
    emberAfDemandResponseLoadControlClusterPrintln("  est: T%4x", cmd_data.eventStatusTime);
    emberAfDemandResponseLoadControlClusterFlush();
    emberAfDemandResponseLoadControlClusterPrintln("  cla: %x", cmd_data.criticalityLevelApplied);
    emberAfDemandResponseLoadControlClusterFlush();
    emberAfDemandResponseLoadControlClusterPrintln("ctspa: %2x (%d)",
                                                   cmd_data.coolingTemperatureSetPointApplied,
                                                   cmd_data.coolingTemperatureSetPointApplied);
    emberAfDemandResponseLoadControlClusterPrintln("htspa: %2x (%d)",
                                                   cmd_data.heatingTemperatureSetPointApplied,
                                                   cmd_data.heatingTemperatureSetPointApplied);
    emberAfDemandResponseLoadControlClusterFlush();
    emberAfDemandResponseLoadControlClusterPrintln("  avg: %x",
                                                   cmd_data.averageLoadAdjustmentPercentageApplied);
    emberAfDemandResponseLoadControlClusterPrintln("   dc: %x", cmd_data.dutyCycleApplied);
    emberAfDemandResponseLoadControlClusterPrintln("   ec: %x", cmd_data.eventControl);
    emberAfDemandResponseLoadControlClusterFlush();
  #endif // SL_CATALOG_ZIGBEE_DEBUG_PRINT_PRESENT
  
    emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
    return true;
  }
  
  #else // !UC_BUILD
  
  bool emberAfDemandResponseLoadControlClusterGetScheduledEventsCallback(uint32_t startTime,
                                                                         uint8_t  numberOfEvents,
                                                                         uint32_t issuerEventId)
  {
    uint8_t i, sent = 0;
    uint8_t ep = emberAfFindClusterServerEndpointIndex(emberAfCurrentEndpoint(),
                                                       ZCL_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_ID);
    EmberAfClusterCommand *currentCommand = emberAfCurrentCommand();
    uint8_t orderedIndex;
    EmberAfLoadControlEvent event;
  
    if (ep == 0xFF) {
      return false;
    }
  
    // CCB 1297
    // startTime = 0 does NOT mean get all events from "now"
  
    // Go through our sorted table (by following the eventOrderToLoadControlIndex
    // map) and send out the scheduled events
    for (i = 0; i < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE; i++) {
      orderedIndex = eventOrderToLoadControlIndex[ep][i];
      // If no more events have been set (and thus, ordered), break
      if (ORDER_NOT_SET == orderedIndex) {
        break;
      }
  
      // CCB 1297: events with startTime = "now" are sent with startTime = 0, not
      // the current time that we internally set it to at the time of creation
      // We do, however, update the duration based on the elapsed time
      // emAfGetScheduledLoadControlEvent() normalizes the duration time
      if (EMBER_SUCCESS != emAfGetScheduledLoadControlEvent(
            emberAfCurrentEndpoint(),
            orderedIndex,
            &event)) {
        continue;
      }
  
      // check how many we have sent, if they have a positive number of events
      // they want returned and we have hit it we should exit.
      if (numberOfEvents != 0 && sent >= numberOfEvents) {
        break;
      }
  
      // If the event is inactive or its start time is before the requested start
      // time we ignore it.
      if (event.source[0] == 0xFF || event.startTime < startTime) {
        continue;
      }
  
      // CCB 1297: filter on issuerEventId if it was sent in command
      if ((issuerEventId != 0xFFFFFFFF)
          && (event.startTime == startTime)
          && (event.eventId < issuerEventId)) {
        continue;
      }
  
      // send the event
      emberAfFillCommandDemandResponseLoadControlClusterLoadControlEvent(event.eventId,
                                                                         event.deviceClass,
                                                                         event.utilityEnrollmentGroup,
                                                                         event.startTime,
                                                                         event.duration,
                                                                         event.criticalityLevel,
                                                                         event.coolingTempOffset,
                                                                         event.heatingTempOffset,
                                                                         event.coolingTempSetPoint,
                                                                         event.heatingTempSetPoint,
                                                                         event.avgLoadPercentage,
                                                                         event.dutyCycle,
                                                                         event.eventControl);
      emberAfSetCommandEndpoints(currentCommand->apsFrame->destinationEndpoint,
                                 currentCommand->apsFrame->sourceEndpoint);
      emberAfGetCommandApsFrame()->options |= EMBER_APS_OPTION_SOURCE_EUI64;
      emberAfSendCommandUnicast(EMBER_OUTGOING_DIRECT, currentCommand->source);
      sent++;  //record that we sent it. and continue
    }
  
    // Bug 13547:
    //   Only send a Default response if there were no events returned.
    //   The LCE messages are the "next" messages in the sequence so no
    //   default response is needed for that successful case.
    if (sent == 0) {
      emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_NOT_FOUND);
    }
  
    return true;
  }
  
  bool emberAfDemandResponseLoadControlClusterReportEventStatusCallback(uint32_t issuerEventId,
                                                                        uint8_t eventStatus,
                                                                        uint32_t eventStatusTime,
                                                                        uint8_t criticalityLevelApplied,
                                                                        uint16_t coolingTemperatureSetPointApplied,
                                                                        uint16_t heatingTemperatureSetPointApplied,
                                                                        int8_t averageLoadAdjustmentPercentageApplied,
                                                                        uint8_t dutyCycleApplied,
                                                                        uint8_t eventControl,
                                                                        uint8_t signatureType,
                                                                        uint8_t* signature)
  {
    emberAfDemandResponseLoadControlClusterPrintln("= RX Event Status =");
    emberAfDemandResponseLoadControlClusterPrintln("  eid: %4x", issuerEventId);
    emberAfDemandResponseLoadControlClusterPrintln("   es: %x", eventStatus);
    emberAfDemandResponseLoadControlClusterPrintln("  est: T%4x", eventStatusTime);
    emberAfDemandResponseLoadControlClusterFlush();
    emberAfDemandResponseLoadControlClusterPrintln("  cla: %x", criticalityLevelApplied);
    emberAfDemandResponseLoadControlClusterFlush();
    emberAfDemandResponseLoadControlClusterPrintln("ctspa: %2x (%d)",
                                                   coolingTemperatureSetPointApplied,
                                                   coolingTemperatureSetPointApplied);
    emberAfDemandResponseLoadControlClusterPrintln("htspa: %2x (%d)",
                                                   heatingTemperatureSetPointApplied,
                                                   heatingTemperatureSetPointApplied);
    emberAfDemandResponseLoadControlClusterFlush();
    emberAfDemandResponseLoadControlClusterPrintln("  avg: %x",
                                                   averageLoadAdjustmentPercentageApplied);
    emberAfDemandResponseLoadControlClusterPrintln("   dc: %x", dutyCycleApplied);
    emberAfDemandResponseLoadControlClusterPrintln("   ec: %x", eventControl);
    emberAfDemandResponseLoadControlClusterFlush();
  
    emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
    return true;
  }
  
  #endif // UC_BUILD
  
  void emAfClearScheduledLoadControlEvents(uint8_t endpoint)
  {
    uint8_t i;
    uint8_t ep = emberAfFindClusterServerEndpointIndex(endpoint, ZCL_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_ID);
  
    if (ep == 0xFF) {
      return;
    }
  
    for (i = 0; i < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE; i++) {
      EmberAfLoadControlEvent *event = &scheduledLoadControlEventTable[ep][i];
      event->source[0] = 0xFF; // event inactive if first byte of source is 0xFF
    }
  
    clearEventOrderToLoadControlIndex(ep);
  }
  
  EmberStatus emAfGetScheduledLoadControlEvent(uint8_t endpoint, uint8_t index, EmberAfLoadControlEvent *event)
  {
    uint8_t ep = emberAfFindClusterServerEndpointIndex(endpoint, ZCL_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_ID);
  
    if (ep == 0xFF) {
      return EMBER_INVALID_ENDPOINT;
    }
  
    if (index < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE) {
      MEMMOVE(event,
              &scheduledLoadControlEventTable[ep][index],
              sizeof(EmberAfLoadControlEvent));
      if (event->source[1] == 0x01 // start now?
          && (emberAfGetCurrentTime()
              < event->startTime + (uint32_t)event->duration * 60)) {
        event->duration = (event->duration
                           - (emberAfGetCurrentTime() - event->startTime) / 60);
        event->startTime = 0x0000;
      }
      return EMBER_SUCCESS;
    }
    emberAfDemandResponseLoadControlClusterPrintln("slce index out of range");
    return EMBER_INDEX_OUT_OF_RANGE;
  }
  
  EmberStatus emAfSetScheduledLoadControlEvent(uint8_t endpoint, uint8_t index, const EmberAfLoadControlEvent *event)
  {
    uint8_t ep = emberAfFindClusterServerEndpointIndex(endpoint, ZCL_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_ID);
  
    if (ep == 0xFF) {
      return EMBER_INVALID_ENDPOINT;
    }
  
    if (index < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE) {
      bool newEvent = (scheduledLoadControlEventTable[ep][index].source[0] == 0xFF);
      MEMMOVE(&scheduledLoadControlEventTable[ep][index],
              event,
              sizeof(EmberAfLoadControlEvent));
      if (scheduledLoadControlEventTable[ep][index].startTime == 0x0000) {
        scheduledLoadControlEventTable[ep][index].startTime = emberAfGetCurrentTime();
        scheduledLoadControlEventTable[ep][index].source[1] = 0x01;
      } else {
        scheduledLoadControlEventTable[ep][index].source[1] = 0x00;
      }
      updateEventOrderToLoadControlIndex(ep, index, true, newEvent);
      return EMBER_SUCCESS;
    }
    emberAfDemandResponseLoadControlClusterPrintln("slce index out of range");
    return EMBER_INDEX_OUT_OF_RANGE;
  }
  
  void emAfPluginDrlcServerPrintInfo(uint8_t endpoint)
  {
  #if ((defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER)) || defined(UC_BUILD))
    uint8_t i;
    uint8_t ep = emberAfFindClusterServerEndpointIndex(endpoint, ZCL_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER_ID);
  
    if (ep == 0xFF) {
      return;
    }
  
    for (i = 0; i < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE; i++) {
      EmberAfLoadControlEvent *lce = &scheduledLoadControlEventTable[ep][i];
      emberAfDemandResponseLoadControlClusterFlush();
      emberAfDemandResponseLoadControlClusterPrintln("= LCE %d =", i);
      emberAfDemandResponseLoadControlClusterPrintln("eid: 0x%4x", lce->eventId);
      emberAfDemandResponseLoadControlClusterPrintln("src: 0x%x%x", lce->source[1], lce->source[0]);
      emberAfDemandResponseLoadControlClusterPrintln("sep: 0x%x", lce->sourceEndpoint);
      emberAfDemandResponseLoadControlClusterFlush();
      emberAfDemandResponseLoadControlClusterPrintln("dep: 0x%x", lce->destinationEndpoint);
      emberAfDemandResponseLoadControlClusterPrintln("dev: 0x%2x", lce->deviceClass);
      emberAfDemandResponseLoadControlClusterPrintln("ueg: 0x%x", lce->utilityEnrollmentGroup);
      emberAfDemandResponseLoadControlClusterPrintln(" st: 0x%4x", lce->startTime);
      emberAfDemandResponseLoadControlClusterFlush();
      emberAfDemandResponseLoadControlClusterPrintln("dur: 0x%2x", lce->duration);
      emberAfDemandResponseLoadControlClusterPrintln(" cl: 0x%x", lce->criticalityLevel);
      emberAfDemandResponseLoadControlClusterPrintln("cto: 0x%x", lce->coolingTempOffset);
      emberAfDemandResponseLoadControlClusterPrintln("hto: 0x%x", lce->heatingTempOffset);
      emberAfDemandResponseLoadControlClusterFlush();
      emberAfDemandResponseLoadControlClusterPrintln("cts: 0x%2x", lce->coolingTempSetPoint);
      emberAfDemandResponseLoadControlClusterPrintln("hts: 0x%2x", lce->heatingTempSetPoint);
      emberAfDemandResponseLoadControlClusterPrintln("alp: 0x%x", lce->avgLoadPercentage);
      emberAfDemandResponseLoadControlClusterPrintln(" dc: 0x%x", lce->dutyCycle);
      emberAfDemandResponseLoadControlClusterFlush();
      emberAfDemandResponseLoadControlClusterPrintln(" ev: 0x%x", lce->eventControl);
      emberAfDemandResponseLoadControlClusterPrintln(" sr: 0x%2x", lce->startRand);
      emberAfDemandResponseLoadControlClusterPrintln(" dr: 0x%2x", lce->durationRand);
      emberAfDemandResponseLoadControlClusterPrintln(" oc: 0x%x", lce->optionControl);
      emberAfDemandResponseLoadControlClusterFlush();
    }
    emberAfDemandResponseLoadControlClusterPrintln("Table size: %d",
                                                   EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE);
  #endif //defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_DEMAND_RESPONSE_LOAD_CONTROL_CLUSTER)
  }
  
  void emAfPluginDrlcServerSlceMessage(EmberNodeId nodeId,
                                       uint8_t srcEndpoint,
                                       uint8_t dstEndpoint,
                                       uint8_t index)
  {
    EmberAfLoadControlEvent event;
    EmberStatus status;
    status = emAfGetScheduledLoadControlEvent(srcEndpoint, index, &event);
  
    if (status != EMBER_SUCCESS) {
      emberAfDemandResponseLoadControlClusterPrintln("send slce fail: 0x%x", status);
      return;
    }
  
    emberAfFillCommandDemandResponseLoadControlClusterLoadControlEvent(event.eventId,
                                                                       event.deviceClass,
                                                                       event.utilityEnrollmentGroup,
                                                                       event.startTime,
                                                                       event.duration,
                                                                       event.criticalityLevel,
                                                                       event.coolingTempOffset,
                                                                       event.heatingTempOffset,
                                                                       event.coolingTempSetPoint,
                                                                       event.heatingTempSetPoint,
                                                                       event.avgLoadPercentage,
                                                                       event.dutyCycle,
                                                                       event.eventControl);
  
    emberAfSetCommandEndpoints(srcEndpoint, dstEndpoint);
    emberAfSendCommandUnicast(EMBER_OUTGOING_DIRECT, nodeId);
  }
  
  // This internal function checks where the load event control should be placed
  // in a startTime- and issuerEventId-sorted array
  // This function does not check if the load control event is already in the
  // ordered array, so calling it with an existing entry will return a new index
  // position, for which the caller must handle appropriately
  // E.g. a LCE has start time 5 and issuer event ID 2 and lies at index 4 in the
  // eventOrderToLoadControlIndex array. The user changes the start time to 9 and
  // issuer event ID to 1, calls this function, and receives index 10. It is up to
  // the caller to move the values at indices 5-10 to indices 4-9 and then set
  // eventOrderToLoadControlIndex[10] to the LCE's index in the
  // scheduledLoadControlEventTable array
  uint8_t findPlacementInEventOrderToLoadControlIndex(uint8_t ep, uint8_t index)
  {
    uint8_t i;
    uint8_t currentIndex;
  
    for (i = 0; (i < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE) && (ORDER_NOT_SET != eventOrderToLoadControlIndex[ep][i]); i++) {
      currentIndex = eventOrderToLoadControlIndex[ep][i];
      if ((scheduledLoadControlEventTable[ep][index].startTime < scheduledLoadControlEventTable[ep][currentIndex].startTime)
          || ((scheduledLoadControlEventTable[ep][index].startTime == scheduledLoadControlEventTable[ep][currentIndex].startTime)
              && (scheduledLoadControlEventTable[ep][index].eventId < scheduledLoadControlEventTable[ep][currentIndex].eventId))) {
        break;
      }
    }
  
    return i;
  }
  
  void updateEventOrderToLoadControlIndex(uint8_t ep,
                                          uint8_t index,
                                          bool eventAdded,
                                          bool isNewEvent)
  {
    uint8_t i;
    uint8_t currentOrderIndex;
    uint8_t newOrderIndex;
  
    if (eventAdded) {
      // A brand new load control event was added to the table
      if (isNewEvent) {
        // First find the ordered placement of this new event
        newOrderIndex = findPlacementInEventOrderToLoadControlIndex(ep, index);
        if (EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE == newOrderIndex) {
          return;
        }
  
        // Now push everyone forward one spot from the found index
        for (i = EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE - 1; i > newOrderIndex; i--) {
          eventOrderToLoadControlIndex[ep][i] = eventOrderToLoadControlIndex[ep][i - 1];
        }
  
        // Finally, set the mapping for the newly added event
        eventOrderToLoadControlIndex[ep][newOrderIndex] = index;
      }
      // An existing load control event was modified
      else {
        // First record the current ordered index and calculate the new index
        for (currentOrderIndex = 0; currentOrderIndex < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE; currentOrderIndex++) {
          if (eventOrderToLoadControlIndex[ep][currentOrderIndex] == index) {
            break;
          }
        }
  
        if (EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE == currentOrderIndex) {
          return;
        }
  
        newOrderIndex = findPlacementInEventOrderToLoadControlIndex(ep, index);
        // EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE is a perfectly
        // valid return. It means the new event must go last in the ordered array
  
        // If the event must go last and it already is last, done
        if ((EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE == newOrderIndex)
            && (EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE == currentOrderIndex + 1)) {
          return;
        }
        // else, if the new index is not the final index and it's calculated to be
        // be the next index, then this effectively is the same position
        // findPlacementInEventOrderToLoadControlIndex will never return the same
        // index, since the comparative operators won't reach that result
        else if (newOrderIndex == currentOrderIndex + 1) {
          return;
        }
  
        if (EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE == newOrderIndex) {
          newOrderIndex--;
        }
  
        // else, move the indices' values and set the new index
        if (currentOrderIndex < newOrderIndex) {
          // e.g. from 5 to 8, move 6-8 down one
          for (i = currentOrderIndex + 1; i <= newOrderIndex; i++) {
            eventOrderToLoadControlIndex[ep][i - 1] = eventOrderToLoadControlIndex[ep][i];
          }
        } else {
          // e.g. from 8 to 5, move 5-7 up one
          for (i = currentOrderIndex; i > newOrderIndex; i--) {
            eventOrderToLoadControlIndex[ep][i] = eventOrderToLoadControlIndex[ep][i - 1];
          }
        }
  
        eventOrderToLoadControlIndex[ep][newOrderIndex] = index;
      }
    } else {
      // An existing load control event cannot currently be removed
    }
  }
  
  void clearEventOrderToLoadControlIndex(uint8_t ep)
  {
    uint8_t i;
    for (i = 0; i < EMBER_AF_PLUGIN_DRLC_SERVER_SCHEDULED_EVENT_TABLE_SIZE; i++) {
      eventOrderToLoadControlIndex[ep][i] = 0xFF;
    }
  }
  
  #ifdef UC_BUILD
  
  uint32_t emberAfDemandResponseLoadControlClusterServerCommandParse(sl_service_opcode_t opcode,
                                                                     sl_service_function_context_t *context)
  {
    (void)opcode;
  
    EmberAfClusterCommand *cmd = (EmberAfClusterCommand *)context->data;
    bool wasHandled = false;
  
    if (!cmd->mfgSpecific) {
      switch (cmd->commandId) {
        case ZCL_REPORT_EVENT_STATUS_COMMAND_ID:
        {
          wasHandled = emberAfDemandResponseLoadControlClusterGetScheduledEventsCallback(cmd);
          break;
        }
        case ZCL_GET_SCHEDULED_EVENTS_COMMAND_ID:
        {
          wasHandled = emberAfDemandResponseLoadControlClusterReportEventStatusCallback(cmd);
          break;
        }
        default:
          break;
      }
    }
  
    return ((wasHandled)
            ? EMBER_ZCL_STATUS_SUCCESS
            : EMBER_ZCL_STATUS_UNSUP_COMMAND);
  }
  
  #endif // UC_BUILD
  