/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "chre/util/duplicate_message_detector.h"

#include "chre/platform/system_time.h"

#include <cstdint>

namespace chre {

Optional<chreError> DuplicateMessageDetector::findOrAdd(
    uint32_t messageSequenceNumber, uint16_t hostEndpoint,
    bool *outIsDuplicate) {
  DuplicateMessageDetector::ReliableMessageRecord *record =
      findLocked(messageSequenceNumber, hostEndpoint);
  if (outIsDuplicate != nullptr) {
    *outIsDuplicate = record != nullptr;
  }

  if (record == nullptr) {
    record = addLocked(messageSequenceNumber, hostEndpoint);
    if (record == nullptr) {
      LOG_OOM();
      if (outIsDuplicate != nullptr) {
        *outIsDuplicate = true;
      }
      return CHRE_ERROR_NO_MEMORY;
    }
  }
  return record->error;
}

bool DuplicateMessageDetector::findAndSetError(uint32_t messageSequenceNumber,
                                               uint16_t hostEndpoint,
                                               chreError error) {
  DuplicateMessageDetector::ReliableMessageRecord *record =
      findLocked(messageSequenceNumber, hostEndpoint);
  if (record == nullptr) {
    return false;
  }

  record->error = error;
  return true;
}

void DuplicateMessageDetector::removeOldEntries() {
  Nanoseconds now = SystemTime::getMonotonicTime();
  while (!mReliableMessageRecordQueue.empty()) {
    ReliableMessageRecord &record = mReliableMessageRecordQueue.top();
    if (record.timestamp + kTimeout <= now) {
      mReliableMessageRecordQueue.pop();
    } else {
      break;
    }
  }
}

DuplicateMessageDetector::ReliableMessageRecord*
    DuplicateMessageDetector::addLocked(
        uint32_t messageSequenceNumber,
        uint16_t hostEndpoint) {
  bool success = mReliableMessageRecordQueue.push(
      ReliableMessageRecord{
          .timestamp = SystemTime::getMonotonicTime(),
          .messageSequenceNumber = messageSequenceNumber,
          .hostEndpoint = hostEndpoint,
          .error = Optional<chreError>()});
  return success
      ? findLocked(messageSequenceNumber, hostEndpoint, /* reverse= */ true)
      : nullptr;
}

DuplicateMessageDetector::ReliableMessageRecord*
  DuplicateMessageDetector::findLocked(uint32_t messageSequenceNumber,
                                       uint16_t hostEndpoint,
                                       bool reverse) {
  for (size_t i = 0; i < mReliableMessageRecordQueue.size(); ++i) {
    size_t index = reverse ? mReliableMessageRecordQueue.size() - i - 1 : i;
    ReliableMessageRecord &record = mReliableMessageRecordQueue[index];
    if (record.messageSequenceNumber == messageSequenceNumber &&
        record.hostEndpoint == hostEndpoint) {
      return &record;
    }
  }
  return nullptr;
}

}  // namespace chre
