text: /*
   *
   *    Copyright (c) 2021 Project CHIP Authors
   *
   *    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.
   */
  #pragma once
  
  #include "UDCClientState.h"
  #include <lib/core/CHIPError.h>
  #include <lib/support/CodeUtils.h>
  #include <system/TimeSource.h>
  
  namespace chip {
  namespace Protocols {
  namespace UserDirectedCommissioning {
  
  // UDC client state times out after 1 hour. This may need to be tweaked.
  constexpr const uint64_t kUDCClientTimeoutMs = 60 * 60 * 1000;
  
  /**
   * Handles a set of UDC Client Processing States.
   *
   * Intended for:
   *   - ignoring/dropping duplicate UDC messages for the same instance
   *   - tracking state of UDC work flow (see UDCClientProcessingState)
   *   - timing out failed/declined UDC Clients
   */
  template <size_t kMaxClientCount, Time::Source kTimeSource = Time::Source::kSystem>
  class UDCClients
  {
  public:
      /**
       * Allocates a new UDC client state object out of the internal resource pool.
       *
       * @param instanceName represents the UDS Client instance name
       * @param state [out] will contain the UDC Client state if one was available. May be null if no return value is desired.
       *
       * @note the newly created state will have an 'expiration' time set based on the current time source.
       *
       * @returns CHIP_NO_ERROR if state could be initialized. May fail if maximum UDC Client count
       *          has been reached (with CHIP_ERROR_NO_MEMORY).
       */
      CHECK_RETURN_VALUE
      CHIP_ERROR CreateNewUDCClientState(const char * instanceName, UDCClientState ** state)
      {
          const uint64_t currentTime = mTimeSource.GetCurrentMonotonicTimeMs();
  
          CHIP_ERROR err = CHIP_ERROR_NO_MEMORY;
  
          if (state)
          {
              *state = nullptr;
          }
  
          for (auto & stateiter : mStates)
          {
              if (!stateiter.IsInitialized(currentTime))
              {
                  stateiter.SetInstanceName(instanceName);
                  stateiter.SetExpirationTimeMs(currentTime + kUDCClientTimeoutMs);
                  stateiter.SetUDCClientProcessingState(UDCClientProcessingState::kDiscoveringNode);
  
                  if (state)
                  {
                      *state = &stateiter;
                  }
  
                  err = CHIP_NO_ERROR;
                  break;
              }
          }
  
          return err;
      }
  
      /**
       * Get a UDC Client state given a Peer address.
       *
       * @param address is the connection to find (based on address)
       *
       * @return the state found, nullptr if not found
       */
      CHECK_RETURN_VALUE
      UDCClientState * FindUDCClientState(const PeerAddress & address)
      {
          const uint64_t currentTime = mTimeSource.GetCurrentMonotonicTimeMs();
  
          UDCClientState * state = nullptr;
  
          for (auto & stateiter : mStates)
          {
              if (!stateiter.IsInitialized(currentTime))
              {
                  continue;
              }
              if (stateiter.GetPeerAddress() == address)
              {
                  state = &stateiter;
                  break;
              }
          }
          return state;
      }
  
      /**
       * Get a UDC Client state given an instance name.
       *
       * @param instanceName is the instance name to find (based upon instance name)
       *
       * @return the state found, nullptr if not found
       */
      CHECK_RETURN_VALUE
      UDCClientState * FindUDCClientState(const char * instanceName)
      {
          const uint64_t currentTime = mTimeSource.GetCurrentMonotonicTimeMs();
  
          UDCClientState * state = nullptr;
  
          for (auto & stateiter : mStates)
          {
              if (!stateiter.IsInitialized(currentTime))
              {
                  continue;
              }
  
              // TODO: check length of instanceName
              if (strncmp(stateiter.GetInstanceName(), instanceName, chip::Dnssd::kMaxInstanceNameSize + 1) == 0)
              {
                  state = &stateiter;
                  break;
              }
          }
          return state;
      }
  
      // Reset all states to kNotInitialized
      void ResetUDCClientStates()
      {
          for (auto & stateiter : mStates)
          {
              stateiter.Reset();
          }
      }
  
      /// Convenience method to mark a UDC Client state as active (non-expired)
      void MarkUDCClientActive(UDCClientState * state)
      {
          state->SetExpirationTimeMs(mTimeSource.GetCurrentMonotonicTimeMs() + kUDCClientTimeoutMs);
      }
  
  private:
      Time::TimeSource<kTimeSource> mTimeSource;
      UDCClientState mStates[kMaxClientCount];
  };
  
  } // namespace UserDirectedCommissioning
  } // namespace Protocols
  } // namespace chip
  