/*
 *  Copyright 2009 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_
#define WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_

#include <memory>
#include <string>
#include <vector>

#include "webrtc/base/bind.h"
#include "webrtc/base/sslfingerprint.h"
#include "webrtc/base/thread.h"
#include "webrtc/p2p/base/fakedtlstransport.h"
#include "webrtc/p2p/base/fakeicetransport.h"
#include "webrtc/p2p/base/transportcontroller.h"

namespace cricket {

// Fake TransportController class, which can be passed into a WebRtcSession
// object for test purposes. Can be connected to other FakeTransportControllers
// via Connect().
//
// This fake is unusual in that for the most part, it's implemented with the
// real TransportController code, but with fake TransportChannels underneath.
class FakeTransportController : public TransportController {
 public:
  FakeTransportController()
      : TransportController(rtc::Thread::Current(),
                            rtc::Thread::Current(),
                            nullptr) {}

  explicit FakeTransportController(bool redetermine_role_on_ice_restart)
      : TransportController(rtc::Thread::Current(),
                            rtc::Thread::Current(),
                            nullptr,
                            redetermine_role_on_ice_restart) {}

  explicit FakeTransportController(IceRole role)
      : TransportController(rtc::Thread::Current(),
                            rtc::Thread::Current(),
                            nullptr) {
    SetIceRole(role);
  }

  explicit FakeTransportController(rtc::Thread* network_thread)
      : TransportController(rtc::Thread::Current(), network_thread, nullptr) {}

  FakeTransportController(rtc::Thread* network_thread, IceRole role)
      : TransportController(rtc::Thread::Current(), network_thread, nullptr) {
    SetIceRole(role);
  }

  FakeDtlsTransport* GetFakeDtlsTransport_n(const std::string& transport_name,
                                            int component) {
    return static_cast<FakeDtlsTransport*>(
        get_channel_for_testing(transport_name, component));
  }

  // Simulate the exchange of transport descriptions, and the gathering and
  // exchange of ICE candidates.
  void Connect(FakeTransportController* dest) {
    for (const std::string& transport_name : transport_names_for_testing()) {
      std::unique_ptr<rtc::SSLFingerprint> local_fingerprint;
      std::unique_ptr<rtc::SSLFingerprint> remote_fingerprint;
      if (certificate_for_testing()) {
        local_fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(
            certificate_for_testing()));
      }
      if (dest->certificate_for_testing()) {
        remote_fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(
            dest->certificate_for_testing()));
      }
      TransportDescription local_desc(
          std::vector<std::string>(),
          rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH),
          rtc::CreateRandomString(cricket::ICE_PWD_LENGTH),
          cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE,
          local_fingerprint.get());
      TransportDescription remote_desc(
          std::vector<std::string>(),
          rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH),
          rtc::CreateRandomString(cricket::ICE_PWD_LENGTH),
          cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE,
          remote_fingerprint.get());
      std::string err;
      SetLocalTransportDescription(transport_name, local_desc,
                                   cricket::CA_OFFER, &err);
      dest->SetRemoteTransportDescription(transport_name, local_desc,
                                          cricket::CA_OFFER, &err);
      dest->SetLocalTransportDescription(transport_name, remote_desc,
                                         cricket::CA_ANSWER, &err);
      SetRemoteTransportDescription(transport_name, remote_desc,
                                    cricket::CA_ANSWER, &err);
    }
    MaybeStartGathering();
    dest->MaybeStartGathering();
    network_thread()->Invoke<void>(
        RTC_FROM_HERE,
        rtc::Bind(&FakeTransportController::SetChannelDestinations_n, this,
                  dest));
  }

  void DestroyRtcpTransport(const std::string& transport_name) {
    DestroyDtlsTransport_n(transport_name,
                           cricket::ICE_CANDIDATE_COMPONENT_RTCP);
  }

 protected:
  IceTransportInternal* CreateIceTransportChannel_n(
      const std::string& transport_name,
      int component) override {
    return new FakeIceTransport(transport_name, component);
  }

  DtlsTransportInternal* CreateDtlsTransportChannel_n(
      const std::string& transport_name,
      int component,
      IceTransportInternal* ice) override {
    return new FakeDtlsTransport(static_cast<FakeIceTransport*>(ice));
  }

 private:
  void SetChannelDestinations_n(FakeTransportController* dest) {
    for (DtlsTransportInternal* tc : channels_for_testing()) {
      FakeDtlsTransport* local = static_cast<FakeDtlsTransport*>(tc);
      FakeDtlsTransport* remote = dest->GetFakeDtlsTransport_n(
          local->transport_name(), local->component());
      if (remote) {
        bool asymmetric = false;
        local->SetDestination(remote, asymmetric);
      }
    }
  }
};

}  // namespace cricket

#endif  // WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_
