/* 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_dom_SerialPortParent_h
#define mozilla_dom_SerialPortParent_h

#include "mozilla/dom/PSerialPortParent.h"
#include "mozilla/dom/SerialPlatformService.h"
#include "mozilla/ipc/DataPipe.h"
#include "nsIAsyncInputStream.h"
#include "nsISupports.h"

namespace mozilla::dom {

class SerialDeviceChangeProxy;

namespace webserial {
class SerialPortWritePump;
}  // namespace webserial

// Parent-side actor for a serial port. Bound to the SerialPlatformService IO
// thread so const that I/O operations can be performed directly without
// dispatching to a separate thread. Data flows through DataPipes; control
// messages (Drain, Flush, Close) use IPC. Note that non-const members are only
// safe to access on the SerialPlatformService's IOThread.
class SerialPortParent final : public PSerialPortParent {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SerialPortParent, override)

  mozilla::ipc::IPCResult RecvOpen(const IPCSerialOptions& aOptions,
                                   OpenResolver&& aResolver);
  mozilla::ipc::IPCResult RecvClose(CloseResolver&& aResolver);
  mozilla::ipc::IPCResult RecvSetSignals(const IPCSerialOutputSignals& aSignals,
                                         SetSignalsResolver&& aResolver);
  mozilla::ipc::IPCResult RecvGetSignals(GetSignalsResolver&& aResolver);
  mozilla::ipc::IPCResult RecvDrain(DrainResolver&& aResolver);
  mozilla::ipc::IPCResult RecvFlush(bool aReceive, FlushResolver&& aResolver);
  mozilla::ipc::IPCResult RecvAttachReadPipe(
      const RefPtr<mozilla::ipc::DataPipeSender>& aReadPipeSender);
  mozilla::ipc::IPCResult RecvAttachWritePipe(
      const RefPtr<mozilla::ipc::DataPipeReceiver>& aWritePipeReceiver);
  mozilla::ipc::IPCResult RecvUpdateSharingState(bool aConnected);
  mozilla::ipc::IPCResult RecvClone(
      mozilla::ipc::Endpoint<PSerialPortParent>&& aEndpoint);

  void ActorDestroy(ActorDestroyReason aWhy) override;

  bool PortIdMatches(const nsAString& aPortId) const {
    return mPortId == aPortId;
  }

  // Called by SerialManagerParent when device connection state changes.
  void NotifyConnected();
  void NotifyDisconnected();

  SerialPortParent(const nsString& aPortId, uint64_t aBrowserId,
                   SerialDeviceChangeProxy* aProxy = nullptr);

 private:
  ~SerialPortParent();

  // This should only be called if the port is closing.
  void StopPumpsBeforeClose();

  void StopReadPump();
  void DestroyPlatformReader();
  void StopWritePump();
  void NotifySharingStateChanged(bool aConnected);
  void StartReadPump(
      already_AddRefed<mozilla::ipc::DataPipeSender> aReadPipeSender);
  void StartWritePump(
      already_AddRefed<mozilla::ipc::DataPipeReceiver> aWritePipeReceiver);

  const nsString mPortId;
  const uint64_t mBrowserId;
  bool mIsOpen = false;
  // Whether we have forwarded a connected=true sharing state notification
  // without a corresponding connected=false. Used by ActorDestroy to send the
  // missing disconnect so the browser sharing indicator count stays in sync.
  bool mSharingConnected = false;
  uint32_t mPipeCapacity = 0;

  // DataPipe endpoints held by the parent: the read pump writes device data
  // to mReadPipeSender, and the write pump reads JS data from
  // mWritePipeReceiver.
  RefPtr<mozilla::ipc::DataPipeSender> mReadPipeSender;
  RefPtr<mozilla::ipc::DataPipeReceiver> mWritePipeReceiver;

  nsCOMPtr<nsIAsyncInputStream> mPlatformInputStream;
  // Opaque context from NS_AsyncCopy used to cancel the in-flight copy from
  // mPlatformInputStream to mReadPipeSender without closing
  // mPlatformInputStream.
  nsCOMPtr<nsISupports> mReadCopierCtx;

  RefPtr<webserial::SerialPortWritePump> mWritePump;

  RefPtr<SerialDeviceChangeProxy> mDeviceChangeProxy;

  // Cloned actors sharing the same underlying port. When we get a
  // connect/disconnect notification, we forward it to all clones too.
  nsTArray<RefPtr<SerialPortParent>> mClones;
};

}  // namespace mozilla::dom

#endif  // mozilla_dom_SerialPortParent_h
