/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "GMPVideoPlaneImpl.h"
#include "mozilla/gmp/GMPTypes.h"
#include "GMPVideoHost.h"
#include "GMPSharedMemManager.h"

namespace mozilla {
namespace gmp {

GMPPlaneImpl::GMPPlaneImpl(GMPVideoHostImpl* aHost)
: mSize(0),
  mStride(0),
  mHost(aHost)
{
  MOZ_ASSERT(mHost);
  mHost->PlaneCreated(this);
}

GMPPlaneImpl::GMPPlaneImpl(const GMPPlaneData& aPlaneData, GMPVideoHostImpl* aHost)
: mBuffer(aPlaneData.mBuffer()),
  mSize(aPlaneData.mSize()),
  mStride(aPlaneData.mStride()),
  mHost(aHost)
{
  MOZ_ASSERT(mHost);
  mHost->PlaneCreated(this);
}

GMPPlaneImpl::~GMPPlaneImpl()
{
  DestroyBuffer();
  if (mHost) {
    mHost->PlaneDestroyed(this);
  }
}

void
GMPPlaneImpl::DoneWithAPI()
{
  DestroyBuffer();

  // Do this after destroying the buffer because destruction
  // involves deallocation, which requires a host.
  mHost = nullptr;
}

void
GMPPlaneImpl::ActorDestroyed()
{
  // Simply clear out Shmem reference, do not attempt to
  // properly free it. It has already been freed.
  mBuffer = ipc::Shmem();
  // No more host.
  mHost = nullptr;
}

bool
GMPPlaneImpl::InitPlaneData(GMPPlaneData& aPlaneData)
{
  aPlaneData.mBuffer() = mBuffer;
  aPlaneData.mSize() = mSize;
  aPlaneData.mStride() = mStride;

  // This method is called right before Shmem is sent to another process.
  // We need to effectively zero out our member copy so that we don't
  // try to delete memory we don't own later.
  mBuffer = ipc::Shmem();

  return true;
}

GMPErr
GMPPlaneImpl::MaybeResize(int32_t aNewSize) {
  if (aNewSize <= AllocatedSize()) {
    return GMPNoErr;
  }

  if (!mHost) {
    return GMPGenericErr;
  }

  ipc::Shmem new_mem;
  if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMem::kGMPFrameData, aNewSize,
                                            ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
      !new_mem.get<uint8_t>()) {
    return GMPAllocErr;
  }

  if (mBuffer.IsReadable()) {
    memcpy(new_mem.get<uint8_t>(), Buffer(), mSize);
  }

  DestroyBuffer();

  mBuffer = new_mem;

  return GMPNoErr;
}

void
GMPPlaneImpl::DestroyBuffer()
{
  if (mHost && mBuffer.IsWritable()) {
    mHost->SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPFrameData, mBuffer);
  }
  mBuffer = ipc::Shmem();
}

GMPErr
GMPPlaneImpl::CreateEmptyPlane(int32_t aAllocatedSize, int32_t aStride, int32_t aPlaneSize)
{
  if (aAllocatedSize < 1 || aStride < 1 || aPlaneSize < 1) {
    return GMPGenericErr;
  }

  GMPErr err = MaybeResize(aAllocatedSize);
  if (err != GMPNoErr) {
    return err;
  }

  mSize = aPlaneSize;
  mStride = aStride;

  return GMPNoErr;
}

GMPErr
GMPPlaneImpl::Copy(const GMPPlane& aPlane)
{
  auto& planeimpl = static_cast<const GMPPlaneImpl&>(aPlane);

  GMPErr err = MaybeResize(planeimpl.mSize);
  if (err != GMPNoErr) {
    return err;
  }

  if (planeimpl.Buffer() && planeimpl.mSize > 0) {
    memcpy(Buffer(), planeimpl.Buffer(), mSize);
  }

  mSize = planeimpl.mSize;
  mStride = planeimpl.mStride;

  return GMPNoErr;
}

GMPErr
GMPPlaneImpl::Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer)
{
  GMPErr err = MaybeResize(aSize);
  if (err != GMPNoErr) {
    return err;
  }

  if (aBuffer && aSize > 0) {
    memcpy(Buffer(), aBuffer, aSize);
  }

  mSize = aSize;
  mStride = aStride;

  return GMPNoErr;
}

void
GMPPlaneImpl::Swap(GMPPlane& aPlane)
{
  auto& planeimpl = static_cast<GMPPlaneImpl&>(aPlane);

  std::swap(mStride, planeimpl.mStride);
  std::swap(mSize, planeimpl.mSize);
  std::swap(mBuffer, planeimpl.mBuffer);
}

int32_t
GMPPlaneImpl::AllocatedSize() const
{
  if (mBuffer.IsWritable()) {
    return mBuffer.Size<uint8_t>();
  }
  return 0;
}

void
GMPPlaneImpl::ResetSize()
{
  mSize = 0;
}

bool
GMPPlaneImpl::IsZeroSize() const
{
  return (mSize == 0);
}

int32_t
GMPPlaneImpl::Stride() const
{
  return mStride;
}

const uint8_t*
GMPPlaneImpl::Buffer() const
{
  return mBuffer.get<uint8_t>();
}

uint8_t*
GMPPlaneImpl::Buffer()
{
  return mBuffer.get<uint8_t>();
}

void
GMPPlaneImpl::Destroy()
{
  delete this;
}

} // namespace gmp
} // namespace mozilla
