/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "RemoteMediaDataDecoder.h"

#include "RemoteDecoderChild.h"
#include "RemoteDecoderManagerChild.h"

namespace mozilla {

#ifdef LOG
#  undef LOG
#endif  // LOG
#define LOG(arg, ...)                                                  \
  DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, __func__, \
            ##__VA_ARGS__)

RemoteMediaDataDecoder::RemoteMediaDataDecoder(RemoteDecoderChild* aChild)
    : mChild(aChild) {
  LOG("%p is created", this);
}

RemoteMediaDataDecoder::~RemoteMediaDataDecoder() {
  if (mChild) {
    // Shutdown didn't get called. This can happen if the creation of the
    // decoder got interrupted while pending.
    nsCOMPtr<nsISerialEventTarget> thread =
        RemoteDecoderManagerChild::GetManagerThread();
    MOZ_ASSERT(thread);
    thread->Dispatch(NS_NewRunnableFunction(
        "RemoteMediaDataDecoderShutdown", [child = std::move(mChild), thread] {
          child->Shutdown()->Then(
              thread, __func__,
              [child](const ShutdownPromise::ResolveOrRejectValue& aValue) {
                child->DestroyIPDL();
              });
        }));
  }
  LOG("%p is released", this);
}

RefPtr<MediaDataDecoder::InitPromise> RemoteMediaDataDecoder::Init() {
  RefPtr<RemoteMediaDataDecoder> self = this;
  return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
                     [self]() { return self->mChild->Init(); })
      ->Then(
          RemoteDecoderManagerChild::GetManagerThread(), __func__,
          [self, this](TrackType aTrack) {
            // If shutdown has started in the meantime shutdown promise may
            // be resloved before this task. In this case mChild will be null
            // and the init promise has to be canceled.
            if (!mChild) {
              return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED,
                                                  __func__);
            }
            mDescription = mChild->GetDescriptionName();
            mProcessName = mChild->GetProcessName();
            mCodecName = mChild->GetCodecName();
            mIsHardwareAccelerated =
                mChild->IsHardwareAccelerated(mHardwareAcceleratedReason);
            mConversion = mChild->NeedsConversion();
            LOG("%p RemoteDecoderChild has been initialized - description: %s, "
                "process: %s, codec: %s",
                this, mDescription.get(), mProcessName.get(), mCodecName.get());
            return InitPromise::CreateAndResolve(aTrack, __func__);
          },
          [self](const MediaResult& aError) {
            return InitPromise::CreateAndReject(aError, __func__);
          });
}

RefPtr<MediaDataDecoder::DecodePromise> RemoteMediaDataDecoder::Decode(
    MediaRawData* aSample) {
  RefPtr<RemoteMediaDataDecoder> self = this;
  RefPtr<MediaRawData> sample = aSample;
  return InvokeAsync(
      RemoteDecoderManagerChild::GetManagerThread(), __func__,
      [self, sample]() {
        return self->mChild->Decode(nsTArray<RefPtr<MediaRawData>>{sample});
      });
}

RefPtr<MediaDataDecoder::DecodePromise> RemoteMediaDataDecoder::DecodeBatch(
    nsTArray<RefPtr<MediaRawData>>&& aSamples) {
  RefPtr<RemoteMediaDataDecoder> self = this;
  return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
                     [self, samples = std::move(aSamples)]() {
                       return self->mChild->Decode(samples);
                     });
}

RefPtr<MediaDataDecoder::FlushPromise> RemoteMediaDataDecoder::Flush() {
  RefPtr<RemoteMediaDataDecoder> self = this;
  return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
                     [self]() { return self->mChild->Flush(); });
}

RefPtr<MediaDataDecoder::DecodePromise> RemoteMediaDataDecoder::Drain() {
  RefPtr<RemoteMediaDataDecoder> self = this;
  return InvokeAsync(RemoteDecoderManagerChild::GetManagerThread(), __func__,
                     [self]() { return self->mChild->Drain(); });
}

RefPtr<ShutdownPromise> RemoteMediaDataDecoder::Shutdown() {
  RefPtr<RemoteMediaDataDecoder> self = this;
  return InvokeAsync(
      RemoteDecoderManagerChild::GetManagerThread(), __func__, [self]() {
        RefPtr<ShutdownPromise> p = self->mChild->Shutdown();

        // We're about to be destroyed and drop our ref to
        // *DecoderChild. Make sure we put a ref into the
        // task queue for the *DecoderChild thread to keep
        // it alive until we send the delete message.
        p->Then(RemoteDecoderManagerChild::GetManagerThread(), __func__,
                [child = std::move(self->mChild)](
                    const ShutdownPromise::ResolveOrRejectValue& aValue) {
                  MOZ_ASSERT(aValue.IsResolve());
                  child->DestroyIPDL();
                  return ShutdownPromise::CreateAndResolveOrReject(aValue,
                                                                   __func__);
                });
        return p;
      });
}

bool RemoteMediaDataDecoder::IsHardwareAccelerated(
    nsACString& aFailureReason) const {
  aFailureReason = mHardwareAcceleratedReason;
  return mIsHardwareAccelerated;
}

void RemoteMediaDataDecoder::SetSeekThreshold(const media::TimeUnit& aTime) {
  RefPtr<RemoteMediaDataDecoder> self = this;
  media::TimeUnit time = aTime;
  RemoteDecoderManagerChild::GetManagerThread()->Dispatch(
      NS_NewRunnableFunction("dom::RemoteMediaDataDecoder::SetSeekThreshold",
                             [=]() {
                               MOZ_ASSERT(self->mChild);
                               self->mChild->SetSeekThreshold(time);
                             }),
      NS_DISPATCH_NORMAL);
}

MediaDataDecoder::ConversionRequired RemoteMediaDataDecoder::NeedsConversion()
    const {
  return mConversion;
}

nsCString RemoteMediaDataDecoder::GetDescriptionName() const {
  return mDescription;
}

nsCString RemoteMediaDataDecoder::GetProcessName() const {
  return mProcessName;
}

nsCString RemoteMediaDataDecoder::GetCodecName() const { return mCodecName; }

#undef LOG

}  // namespace mozilla
