/* 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 https://mozilla.org/MPL/2.0/. */

#include "RTCRtpScriptTransform.h"

#include "ErrorList.h"
#include "js/RootingAPI.h"
#include "jsapi/RTCTransformEventRunnable.h"
#include "libwebrtcglue/FrameTransformerProxy.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Logging.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RTCRtpScriptTransformBinding.h"
#include "mozilla/dom/Worker.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDebug.h"
#include "nsISupports.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"

namespace mozilla::dom {

LazyLogModule gScriptTransformLog("RTCRtpScriptTransform");

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCRtpScriptTransform, mWindow)
NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCRtpScriptTransform)
NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCRtpScriptTransform)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCRtpScriptTransform)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

already_AddRefed<RTCRtpScriptTransform> RTCRtpScriptTransform::Constructor(
    const GlobalObject& aGlobal, Worker& aWorker,
    JS::Handle<JS::Value> aOptions,
    const Optional<Sequence<JSObject*>>& aTransfer, ErrorResult& aRv) {
  nsCOMPtr<nsPIDOMWindowInner> ownerWindow =
      do_QueryInterface(aGlobal.GetAsSupports());
  if (NS_WARN_IF(!ownerWindow)) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  // The spec currently fails to describe what to do when the worker is closing
  // or closed; the following placeholder text can be found in the spec at:
  // https://w3c.github.io/webrtc-encoded-transform/#dom-rtcrtpscripttransform-rtcrtpscripttransform
  //
  // > FIXME: Describe error handling (worker closing flag true at
  // > RTCRtpScriptTransform creation time. And worker being terminated while
  // > transform is processing data).
  //
  // Because our worker runnables do not like to be pointed at a nonexistant
  // worker, we throw in this case.
  if (!aWorker.IsEligibleForMessaging()) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  auto newTransform = MakeRefPtr<RTCRtpScriptTransform>(ownerWindow);
  RefPtr runnable =
      MakeRefPtr<RTCTransformEventRunnable>(aWorker, &newTransform->GetProxy());

  if (aTransfer.WasPassed()) {
    aWorker.PostEventWithOptions(aGlobal.Context(), aOptions, aTransfer.Value(),
                                 runnable, aRv);
  } else {
    StructuredSerializeOptions transferOptions;
    aWorker.PostEventWithOptions(aGlobal.Context(), aOptions,
                                 transferOptions.mTransfer, runnable, aRv);
  }

  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }

  return newTransform.forget();
}

RTCRtpScriptTransform::RTCRtpScriptTransform(nsPIDOMWindowInner* aWindow)
    : mWindow(aWindow), mProxy(MakeRefPtr<FrameTransformerProxy>()) {}

RTCRtpScriptTransform::~RTCRtpScriptTransform() {
  mProxy->ReleaseScriptTransformer();
}

JSObject* RTCRtpScriptTransform::WrapObject(JSContext* aCx,
                                            JS::Handle<JSObject*> aGivenProto) {
  return RTCRtpScriptTransform_Binding::Wrap(aCx, this, aGivenProto);
}

}  // namespace mozilla::dom

#undef LOGTAG
