/*
 * libjingle
 * Copyright 2004--2005, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// A Transport manages a set of named channels of the same type.
//
// Subclasses choose the appropriate class to instantiate for each channel;
// however, this base class keeps track of the channels by name, watches their
// state changes (in order to update the manager's state), and forwards
// requests to begin connecting or to reset to each of the channels.
//
// On Threading:  Transport performs work on both the signaling and worker
// threads.  For subclasses, the rule is that all signaling related calls will
// be made on the signaling thread and all channel related calls (including
// signaling for a channel) will be made on the worker thread.  When
// information needs to be sent between the two threads, this class should do
// the work (e.g., OnRemoteCandidate).
//
// Note: Subclasses must call DestroyChannels() in their own constructors.
// It is not possible to do so here because the subclass constructor will
// already have run.

#ifndef TALK_P2P_BASE_TRANSPORT_H_
#define TALK_P2P_BASE_TRANSPORT_H_

#include <map>
#include <string>
#include <vector>
#include "talk/p2p/base/candidate.h"
#include "talk/p2p/base/constants.h"
#include "talk/p2p/base/sessiondescription.h"
#include "talk/p2p/base/transportinfo.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/messagequeue.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/sslstreamadapter.h"

namespace rtc {
class Thread;
}

namespace buzz {
class QName;
class XmlElement;
}

namespace cricket {

struct ParseError;
struct WriteError;
class CandidateTranslator;
class PortAllocator;
class SessionManager;
class Session;
class TransportChannel;
class TransportChannelImpl;

typedef std::vector<buzz::XmlElement*> XmlElements;
typedef std::vector<Candidate> Candidates;

// Used to parse and serialize (write) transport candidates.  For
// convenience of old code, Transports will implement TransportParser.
// Parse/Write seems better than Serialize/Deserialize or
// Create/Translate.
class TransportParser {
 public:
  // The incoming Translator value may be null, in which case
  // ParseCandidates should return false if there are candidates to
  // parse (indicating a failure to parse).  If the Translator is null
  // and there are no candidates to parse, then return true,
  // indicating a successful parse of 0 candidates.

  // Parse or write a transport description, including ICE credentials and
  // any DTLS fingerprint. Since only Jingle has transport descriptions, these
  // functions are only used when serializing to Jingle.
  virtual bool ParseTransportDescription(const buzz::XmlElement* elem,
                                         const CandidateTranslator* translator,
                                         TransportDescription* tdesc,
                                         ParseError* error) = 0;
  virtual bool WriteTransportDescription(const TransportDescription& tdesc,
                                         const CandidateTranslator* translator,
                                         buzz::XmlElement** tdesc_elem,
                                         WriteError* error) = 0;


  // Parse a single candidate. This must be used when parsing Gingle
  // candidates, since there is no enclosing transport description.
  virtual bool ParseGingleCandidate(const buzz::XmlElement* elem,
                                    const CandidateTranslator* translator,
                                    Candidate* candidates,
                                    ParseError* error) = 0;
  virtual bool WriteGingleCandidate(const Candidate& candidate,
                                    const CandidateTranslator* translator,
                                    buzz::XmlElement** candidate_elem,
                                    WriteError* error) = 0;

  // Helper function to parse an element describing an address.  This
  // retrieves the IP and port from the given element and verifies
  // that they look like plausible values.
  bool ParseAddress(const buzz::XmlElement* elem,
                    const buzz::QName& address_name,
                    const buzz::QName& port_name,
                    rtc::SocketAddress* address,
                    ParseError* error);

  virtual ~TransportParser() {}
};

// For "writable" and "readable", we need to differentiate between
// none, all, and some.
enum TransportState {
  TRANSPORT_STATE_NONE = 0,
  TRANSPORT_STATE_SOME,
  TRANSPORT_STATE_ALL
};

// Stats that we can return about the connections for a transport channel.
// TODO(hta): Rename to ConnectionStats
struct ConnectionInfo {
  ConnectionInfo()
      : best_connection(false),
        writable(false),
        readable(false),
        timeout(false),
        new_connection(false),
        rtt(0),
        sent_total_bytes(0),
        sent_bytes_second(0),
        recv_total_bytes(0),
        recv_bytes_second(0),
        key(NULL) {}

  bool best_connection;        // Is this the best connection we have?
  bool writable;               // Has this connection received a STUN response?
  bool readable;               // Has this connection received a STUN request?
  bool timeout;                // Has this connection timed out?
  bool new_connection;         // Is this a newly created connection?
  size_t rtt;                  // The STUN RTT for this connection.
  size_t sent_total_bytes;     // Total bytes sent on this connection.
  size_t sent_bytes_second;    // Bps over the last measurement interval.
  size_t recv_total_bytes;     // Total bytes received on this connection.
  size_t recv_bytes_second;    // Bps over the last measurement interval.
  Candidate local_candidate;   // The local candidate for this connection.
  Candidate remote_candidate;  // The remote candidate for this connection.
  void* key;                   // A static value that identifies this conn.
};

// Information about all the connections of a channel.
typedef std::vector<ConnectionInfo> ConnectionInfos;

// Information about a specific channel
struct TransportChannelStats {
  int component;
  ConnectionInfos connection_infos;
};

// Information about all the channels of a transport.
// TODO(hta): Consider if a simple vector is as good as a map.
typedef std::vector<TransportChannelStats> TransportChannelStatsList;

// Information about the stats of a transport.
struct TransportStats {
  std::string content_name;
  TransportChannelStatsList channel_stats;
};

bool BadTransportDescription(const std::string& desc, std::string* err_desc);

bool IceCredentialsChanged(const std::string& old_ufrag,
                           const std::string& old_pwd,
                           const std::string& new_ufrag,
                           const std::string& new_pwd);

class Transport : public rtc::MessageHandler,
                  public sigslot::has_slots<> {
 public:
  Transport(rtc::Thread* signaling_thread,
            rtc::Thread* worker_thread,
            const std::string& content_name,
            const std::string& type,
            PortAllocator* allocator);
  virtual ~Transport();

  // Returns the signaling thread. The app talks to Transport on this thread.
  rtc::Thread* signaling_thread() { return signaling_thread_; }
  // Returns the worker thread. The actual networking is done on this thread.
  rtc::Thread* worker_thread() { return worker_thread_; }

  // Returns the content_name of this transport.
  const std::string& content_name() const { return content_name_; }
  // Returns the type of this transport.
  const std::string& type() const { return type_; }

  // Returns the port allocator object for this transport.
  PortAllocator* port_allocator() { return allocator_; }

  // Returns the readable and states of this manager.  These bits are the ORs
  // of the corresponding bits on the managed channels.  Each time one of these
  // states changes, a signal is raised.
  // TODO: Replace uses of readable() and writable() with
  // any_channels_readable() and any_channels_writable().
  bool readable() const { return any_channels_readable(); }
  bool writable() const { return any_channels_writable(); }
  bool was_writable() const { return was_writable_; }
  bool any_channels_readable() const {
    return (readable_ == TRANSPORT_STATE_SOME ||
            readable_ == TRANSPORT_STATE_ALL);
  }
  bool any_channels_writable() const {
    return (writable_ == TRANSPORT_STATE_SOME ||
            writable_ == TRANSPORT_STATE_ALL);
  }
  bool all_channels_readable() const {
    return (readable_ == TRANSPORT_STATE_ALL);
  }
  bool all_channels_writable() const {
    return (writable_ == TRANSPORT_STATE_ALL);
  }
  sigslot::signal1<Transport*> SignalReadableState;
  sigslot::signal1<Transport*> SignalWritableState;
  sigslot::signal1<Transport*> SignalCompleted;
  sigslot::signal1<Transport*> SignalFailed;

  // Returns whether the client has requested the channels to connect.
  bool connect_requested() const { return connect_requested_; }

  void SetIceRole(IceRole role);
  IceRole ice_role() const { return ice_role_; }

  void SetIceTiebreaker(uint64 IceTiebreaker) { tiebreaker_ = IceTiebreaker; }
  uint64 IceTiebreaker() { return tiebreaker_; }

  // Must be called before applying local session description.
  void SetIdentity(rtc::SSLIdentity* identity);

  // Get a copy of the local identity provided by SetIdentity.
  bool GetIdentity(rtc::SSLIdentity** identity);

  // Get a copy of the remote certificate in use by the specified channel.
  bool GetRemoteCertificate(rtc::SSLCertificate** cert);

  TransportProtocol protocol() const { return protocol_; }

  // Create, destroy, and lookup the channels of this type by their components.
  TransportChannelImpl* CreateChannel(int component);
  // Note: GetChannel may lead to race conditions, since the mutex is not held
  // after the pointer is returned.
  TransportChannelImpl* GetChannel(int component);
  // Note: HasChannel does not lead to race conditions, unlike GetChannel.
  bool HasChannel(int component) {
    return (NULL != GetChannel(component));
  }
  bool HasChannels();
  void DestroyChannel(int component);

  // Set the local TransportDescription to be used by TransportChannels.
  // This should be called before ConnectChannels().
  bool SetLocalTransportDescription(const TransportDescription& description,
                                    ContentAction action,
                                    std::string* error_desc);

  // Set the remote TransportDescription to be used by TransportChannels.
  bool SetRemoteTransportDescription(const TransportDescription& description,
                                     ContentAction action,
                                     std::string* error_desc);

  // Tells all current and future channels to start connecting.  When the first
  // channel begins connecting, the following signal is raised.
  void ConnectChannels();
  sigslot::signal1<Transport*> SignalConnecting;

  // Resets all of the channels back to their initial state.  They are no
  // longer connecting.
  void ResetChannels();

  // Destroys every channel created so far.
  void DestroyAllChannels();

  bool GetStats(TransportStats* stats);

  // Before any stanza is sent, the manager will request signaling.  Once
  // signaling is available, the client should call OnSignalingReady.  Once
  // this occurs, the transport (or its channels) can send any waiting stanzas.
  // OnSignalingReady invokes OnTransportSignalingReady and then forwards this
  // signal to each channel.
  sigslot::signal1<Transport*> SignalRequestSignaling;
  void OnSignalingReady();

  // Handles sending of ready candidates and receiving of remote candidates.
  sigslot::signal2<Transport*,
                   const std::vector<Candidate>&> SignalCandidatesReady;

  sigslot::signal1<Transport*> SignalCandidatesAllocationDone;
  void OnRemoteCandidates(const std::vector<Candidate>& candidates);

  // If candidate is not acceptable, returns false and sets error.
  // Call this before calling OnRemoteCandidates.
  virtual bool VerifyCandidate(const Candidate& candidate,
                               std::string* error);

  // Signals when the best connection for a channel changes.
  sigslot::signal3<Transport*,
                   int,  // component
                   const Candidate&> SignalRouteChange;

  // A transport message has generated an transport-specific error.  The
  // stanza that caused the error is available in session_msg.  If false is
  // returned, the error is considered unrecoverable, and the session is
  // terminated.
  // TODO(juberti): Remove these obsolete functions once Session no longer
  // references them.
  virtual void OnTransportError(const buzz::XmlElement* error) {}
  sigslot::signal6<Transport*, const buzz::XmlElement*, const buzz::QName&,
                   const std::string&, const std::string&,
                   const buzz::XmlElement*>
      SignalTransportError;

  // Forwards the signal from TransportChannel to BaseSession.
  sigslot::signal0<> SignalRoleConflict;

  virtual bool GetSslRole(rtc::SSLRole* ssl_role) const;

 protected:
  // These are called by Create/DestroyChannel above in order to create or
  // destroy the appropriate type of channel.
  virtual TransportChannelImpl* CreateTransportChannel(int component) = 0;
  virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0;

  // Informs the subclass that we received the signaling ready message.
  virtual void OnTransportSignalingReady() {}

  // The current local transport description, for use by derived classes
  // when performing transport description negotiation.
  const TransportDescription* local_description() const {
    return local_description_.get();
  }

  // The current remote transport description, for use by derived classes
  // when performing transport description negotiation.
  const TransportDescription* remote_description() const {
    return remote_description_.get();
  }

  virtual void SetIdentity_w(rtc::SSLIdentity* identity) {}

  virtual bool GetIdentity_w(rtc::SSLIdentity** identity) {
    return false;
  }

  // Pushes down the transport parameters from the local description, such
  // as the ICE ufrag and pwd.
  // Derived classes can override, but must call the base as well.
  virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
                                                std::string* error_desc);

  // Pushes down remote ice credentials from the remote description to the
  // transport channel.
  virtual bool ApplyRemoteTransportDescription_w(TransportChannelImpl* ch,
                                                 std::string* error_desc);

  // Negotiates the transport parameters based on the current local and remote
  // transport description, such at the version of ICE to use, and whether DTLS
  // should be activated.
  // Derived classes can negotiate their specific parameters here, but must call
  // the base as well.
  virtual bool NegotiateTransportDescription_w(ContentAction local_role,
                                               std::string* error_desc);

  // Pushes down the transport parameters obtained via negotiation.
  // Derived classes can set their specific parameters here, but must call the
  // base as well.
  virtual bool ApplyNegotiatedTransportDescription_w(
      TransportChannelImpl* channel, std::string* error_desc);

  virtual bool GetSslRole_w(rtc::SSLRole* ssl_role) const {
    return false;
  }

 private:
  struct ChannelMapEntry {
    ChannelMapEntry() : impl_(NULL), candidates_allocated_(false), ref_(0) {}
    explicit ChannelMapEntry(TransportChannelImpl *impl)
        : impl_(impl),
          candidates_allocated_(false),
          ref_(0) {
    }

    void AddRef() { ++ref_; }
    void DecRef() {
      ASSERT(ref_ > 0);
      --ref_;
    }
    int ref() const { return ref_; }

    TransportChannelImpl* get() const { return impl_; }
    TransportChannelImpl* operator->() const  { return impl_; }
    void set_candidates_allocated(bool status) {
      candidates_allocated_ = status;
    }
    bool candidates_allocated() const { return candidates_allocated_; }

  private:
    TransportChannelImpl *impl_;
    bool candidates_allocated_;
    int ref_;
  };

  // Candidate component => ChannelMapEntry
  typedef std::map<int, ChannelMapEntry> ChannelMap;

  // Called when the state of a channel changes.
  void OnChannelReadableState(TransportChannel* channel);
  void OnChannelWritableState(TransportChannel* channel);

  // Called when a channel requests signaling.
  void OnChannelRequestSignaling(TransportChannelImpl* channel);

  // Called when a candidate is ready from remote peer.
  void OnRemoteCandidate(const Candidate& candidate);
  // Called when a candidate is ready from channel.
  void OnChannelCandidateReady(TransportChannelImpl* channel,
                               const Candidate& candidate);
  void OnChannelRouteChange(TransportChannel* channel,
                            const Candidate& remote_candidate);
  void OnChannelCandidatesAllocationDone(TransportChannelImpl* channel);
  // Called when there is ICE role change.
  void OnRoleConflict(TransportChannelImpl* channel);
  // Called when the channel removes a connection.
  void OnChannelConnectionRemoved(TransportChannelImpl* channel);

  // Dispatches messages to the appropriate handler (below).
  void OnMessage(rtc::Message* msg);

  // These are versions of the above methods that are called only on a
  // particular thread (s = signaling, w = worker).  The above methods post or
  // send a message to invoke this version.
  TransportChannelImpl* CreateChannel_w(int component);
  void DestroyChannel_w(int component);
  void ConnectChannels_w();
  void ResetChannels_w();
  void DestroyAllChannels_w();
  void OnRemoteCandidate_w(const Candidate& candidate);
  void OnChannelReadableState_s();
  void OnChannelWritableState_s();
  void OnChannelRequestSignaling_s(int component);
  void OnConnecting_s();
  void OnChannelRouteChange_s(const TransportChannel* channel,
                              const Candidate& remote_candidate);
  void OnChannelCandidatesAllocationDone_s();

  // Helper function that invokes the given function on every channel.
  typedef void (TransportChannelImpl::* TransportChannelFunc)();
  void CallChannels_w(TransportChannelFunc func);

  // Computes the OR of the channel's read or write state (argument picks).
  TransportState GetTransportState_s(bool read);

  void OnChannelCandidateReady_s();

  void SetIceRole_w(IceRole role);
  void SetRemoteIceMode_w(IceMode mode);
  bool SetLocalTransportDescription_w(const TransportDescription& desc,
                                      ContentAction action,
                                      std::string* error_desc);
  bool SetRemoteTransportDescription_w(const TransportDescription& desc,
                                       ContentAction action,
                                       std::string* error_desc);
  bool GetStats_w(TransportStats* infos);
  bool GetRemoteCertificate_w(rtc::SSLCertificate** cert);

  // Sends SignalCompleted if we are now in that state.
  void MaybeCompleted_w();

  rtc::Thread* signaling_thread_;
  rtc::Thread* worker_thread_;
  std::string content_name_;
  std::string type_;
  PortAllocator* allocator_;
  bool destroyed_;
  TransportState readable_;
  TransportState writable_;
  bool was_writable_;
  bool connect_requested_;
  IceRole ice_role_;
  uint64 tiebreaker_;
  TransportProtocol protocol_;
  IceMode remote_ice_mode_;
  rtc::scoped_ptr<TransportDescription> local_description_;
  rtc::scoped_ptr<TransportDescription> remote_description_;

  ChannelMap channels_;
  // Buffers the ready_candidates so that SignalCanidatesReady can
  // provide them in multiples.
  std::vector<Candidate> ready_candidates_;
  // Protects changes to channels and messages
  rtc::CriticalSection crit_;

  DISALLOW_EVIL_CONSTRUCTORS(Transport);
};

// Extract a TransportProtocol from a TransportDescription.
TransportProtocol TransportProtocolFromDescription(
    const TransportDescription* desc);

}  // namespace cricket

#endif  // TALK_P2P_BASE_TRANSPORT_H_
