/* -LICENSE-START-
** Copyright (c) 2013 Blackmagic Design
**
** Permission is hereby granted, free of charge, to any person or organization
** obtaining a copy of the software and accompanying documentation covered by
** this license (the "Software") to use, reproduce, display, distribute,
** execute, and transmit the Software, and to prepare derivative works of the
** Software, and to permit third-parties to whom the Software is furnished to
** do so, all subject to the following:
**
** The copyright notices in the Software and this entire statement, including
** the above license grant, this restriction and the following disclaimer,
** must be included in all copies of the Software, in whole or in part, and
** all derivative works of the Software, unless such copies or derivative
** works are solely in the form of machine-executable object code generated by
** a source language processor.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
** DEALINGS IN THE SOFTWARE.
** -LICENSE-END-
*/

#include <cstdlib>
#include <cstring>
#include <stdexcept>

#include "VideoFrame3D.h"

static inline bool CompareREFIID(const REFIID& ref1, const REFIID& ref2)
{
	return memcmp(&ref1, &ref2, sizeof(REFIID)) == 0;
}

static inline int OSAtomicIncrement32(volatile int *valPtr)
{
	// gcc atomic operation builtin
	return __sync_add_and_fetch(valPtr, 1);
}

static inline int OSAtomicDecrement32(volatile int *valPtr)
{
	// gcc atomic operation builtin
	return __sync_sub_and_fetch(valPtr, 1);
}

// IUnknown methods
HRESULT VideoFrame3D::QueryInterface(REFIID iid, LPVOID *ppv)
{
	if (CompareREFIID(iid, IID_IUnknown))
		*ppv = static_cast<IDeckLinkVideoFrame*>(this);
	else if (CompareREFIID(iid, IID_IDeckLinkVideoFrame))
		*ppv = static_cast<IDeckLinkVideoFrame*>(this);
	else if (CompareREFIID(iid, IID_IDeckLinkVideoFrame3DExtensions))
		*ppv = static_cast<IDeckLinkVideoFrame3DExtensions*>(this);
	else
	{
		*ppv = NULL;
		return E_NOINTERFACE;
	}

	AddRef();
	return S_OK;
}

ULONG VideoFrame3D::AddRef(void)
{
	return OSAtomicIncrement32(&m_refCount);
}

ULONG VideoFrame3D::Release(void)
{
	ULONG newRefValue = OSAtomicDecrement32(&m_refCount);
	if (!newRefValue)
		delete this;
	return newRefValue;
}

// IDeckLinkVideoFrame methods
long VideoFrame3D::GetWidth(void)
{
	return m_frameLeft->GetWidth();
}

long VideoFrame3D::GetHeight(void)
{
	return m_frameLeft->GetHeight();
}

long VideoFrame3D::GetRowBytes(void)
{
	return m_frameLeft->GetRowBytes();
}

BMDPixelFormat VideoFrame3D::GetPixelFormat(void)
{
	return m_frameLeft->GetPixelFormat();
}

BMDFrameFlags VideoFrame3D::GetFlags(void)
{
	return m_frameLeft->GetFlags();
}

HRESULT VideoFrame3D::GetBytes(void** buffer)
{
	return m_frameLeft->GetBytes(buffer);
}

HRESULT VideoFrame3D::GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)
{
	return m_frameLeft->GetTimecode(format, timecode);
}

HRESULT VideoFrame3D::GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary)
{
	return m_frameLeft->GetAncillaryData(ancillary);
}

BMDVideo3DPackingFormat VideoFrame3D::Get3DPackingFormat(void)
{
	return bmdVideo3DPackingLeftOnly;
}

HRESULT VideoFrame3D::GetFrameForRightEye(IDeckLinkVideoFrame** rightEyeFrame)
{
	*rightEyeFrame = NULL;

	if (m_frameRight)
		return m_frameRight->QueryInterface(IID_IDeckLinkVideoFrame, (void**)rightEyeFrame);
	else
		return E_FAIL;

}

VideoFrame3D::VideoFrame3D(IDeckLinkVideoFrame* left, IDeckLinkVideoFrame* right) :
	m_frameLeft(left),
	m_frameRight(right),
	m_refCount(1)
{
	if (!m_frameLeft)
		throw std::invalid_argument("at minimum a left frame must be defined");

	m_frameLeft->AddRef();

	if (m_frameRight)
		m_frameRight->AddRef();
}

VideoFrame3D::~VideoFrame3D()
{
	m_frameLeft->Release();
	m_frameLeft = NULL;

	if (m_frameRight)
		m_frameRight->Release();
	m_frameRight = NULL;
}
