/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_net_Http3StreamTunnel_h
#define mozilla_net_Http3StreamTunnel_h

#include "Http3Stream.h"
#include "nsHttpConnection.h"
#include "SimpleBuffer.h"

namespace mozilla::net {

class Http3StreamTunnel;

class Http3TransportLayer final : public nsISocketTransport,
                                  public nsIInputStreamCallback,
                                  public nsIOutputStreamCallback {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSITRANSPORT
  NS_DECL_NSISOCKETTRANSPORT
  NS_DECL_NSIINPUTSTREAMCALLBACK
  NS_DECL_NSIOUTPUTSTREAMCALLBACK

  explicit Http3TransportLayer(Http3StreamTunnel* aStream);

  already_AddRefed<Http3StreamTunnel> GetStream();
  nsresult Condition() const { return mCondition; }
  nsIAsyncInputStream* GetInput();
  nsIAsyncOutputStream* GetOutput();
  nsresult CallToReadData();
  nsresult CallToWriteData();
  void OnStreamClosed(nsresult aReason);

 private:
  class InputStreamTunnel : public nsIAsyncInputStream {
   public:
    NS_DECL_THREADSAFE_ISUPPORTS
    NS_DECL_NSIINPUTSTREAM
    NS_DECL_NSIASYNCINPUTSTREAM

    explicit InputStreamTunnel(Http3TransportLayer* aTransport);

    nsresult OnSocketReady(nsresult condition);
    bool HasCallback() { return !!mCallback; }

   private:
    friend class Http3TransportLayer;
    virtual ~InputStreamTunnel() = default;

    // The lifetime of InputStreamTunnel and OutputStreamTunnel are bound to
    // Http3TransportLayer, so using |mTransport| as a raw pointer should be
    // safe.
    Http3TransportLayer* MOZ_OWNING_REF mTransport;
    nsCOMPtr<nsIInputStreamCallback> mCallback;
    nsresult mCondition{NS_OK};
  };
  class OutputStreamTunnel : public nsIAsyncOutputStream {
   public:
    NS_DECL_THREADSAFE_ISUPPORTS
    NS_DECL_NSIOUTPUTSTREAM
    NS_DECL_NSIASYNCOUTPUTSTREAM

    explicit OutputStreamTunnel(Http3TransportLayer* aTransport);

    nsresult OnSocketReady(nsresult condition);

   private:
    friend class Http3TransportLayer;
    virtual ~OutputStreamTunnel() = default;

    Http3TransportLayer* MOZ_OWNING_REF mTransport;
    nsCOMPtr<nsIOutputStreamCallback> mCallback;
    nsresult mCondition{NS_OK};
  };
  virtual ~Http3TransportLayer();

  RefPtr<Http3StreamTunnel> mStream;
  InputStreamTunnel mInput;
  OutputStreamTunnel mOutput;
  nsresult mCondition{NS_OK};
};

class Http3StreamTunnel final : public Http3Stream {
 public:
  explicit Http3StreamTunnel(nsAHttpTransaction* aTrans, Http3Session* aSession,
                             uint64_t aCurrentBrowserId);

  Http3WebTransportSession* GetHttp3WebTransportSession() override {
    return nullptr;
  }
  Http3WebTransportStream* GetHttp3WebTransportStream() override {
    return nullptr;
  }
  Http3Stream* GetHttp3Stream() override { return this; }
  Http3ConnectUDPStream* GetHttp3ConnectUDPStream() override { return nullptr; }
  Http3StreamTunnel* GetHttp3StreamTunnel() override { return this; }

  nsresult TryActivating() override;

  void Close(nsresult aResult) override;

  already_AddRefed<nsHttpConnection> CreateHttpConnection(
      nsIInterfaceRequestor* aCallbacks, PRIntervalTime aRtt,
      bool aIsExtendedCONNECT);

  void CleanupStream(nsresult aReason);

  void HasDataToWrite();
  void HasDataToRead();

  [[nodiscard]] nsresult ReadSegments() override;
  [[nodiscard]] nsresult WriteSegments() override;

  [[nodiscard]] nsresult OnWriteSegment(char* buf, uint32_t count,
                                        uint32_t* countWritten) override;

  bool Closed() const { return mClosed; }

 private:
  virtual ~Http3StreamTunnel();
  nsresult BufferInput();

  RefPtr<Http3TransportLayer> mTransport;
  SimpleBuffer mSimpleBuffer;
};

}  // namespace mozilla::net

#endif  // mozilla_net_Http3StreamTunnel_h
