/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * 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.
 **********************************************************************/

#include "audiocall.h"
#include <QFuture>
#include <QFutureWatcher>
#include <util/sll/functional.h>
#include <util/sll/slotclosure.h>
#include <util/sll/futures.h>
#include "threadexceptions.h"
#include "audiocalldevice.h"
#include "toxcontact.h"

namespace LeechCraft
{
namespace Azoth
{
namespace Sarin
{
	AudioCall::AudioCall (const ToxContact *contact, CallManager *callMgr, Direction dir)
	: SourceId_ { contact->GetEntryID () }
	, SourcePubkey_ { contact->GetPubKey () }
	, Dir_ { dir }
	, CallMgr_ { callMgr }
	{
		connect (CallMgr_,
				SIGNAL (transferStarting (int32_t)),
				this,
				SLOT (handleTransferStarting (int32_t)));
	}

	AudioCall::AudioCall (const ToxContact *contact, CallManager *callMgr)
	: AudioCall { contact, callMgr, DOut }
	{
		InitiateCall ();
	}

	AudioCall::AudioCall (int32_t callIdx, const ToxContact *contact, CallManager *callMgr)
	: AudioCall { contact, callMgr, DIn }
	{
		CallIdx_ = callIdx;
	}

	IMediaCall::Direction AudioCall::GetDirection () const
	{
		return Dir_;
	}

	QString AudioCall::GetSourceID () const
	{
		return SourceId_;
	}

	void AudioCall::Accept ()
	{
		if (Dir_ == DOut)
			return;

		try
		{
			CallMgr_->AcceptCall (CallIdx_).result ();
		}
		catch (const std::exception& e)
		{
			qWarning () << Q_FUNC_INFO
					<< e.what ();

			emit stateChanged (IMediaCall::SFinished);
		}
	}

	void AudioCall::Hangup ()
	{
	}

	QIODevice* AudioCall::GetAudioDevice ()
	{
		return Device_.get ();
	}

	QAudioFormat AudioCall::GetAudioFormat ()
	{
		return Fmt_;
	}

	QIODevice* AudioCall::GetVideoDevice ()
	{
		return nullptr;
	}

	void AudioCall::InitiateCall ()
	{
		auto watcher = new QFutureWatcher<CallManager::InitiateResult> { this };
		new Util::SlotClosure<Util::DeleteLaterPolicy>
		{
			[watcher, this]
			{
				watcher->deleteLater ();
				HandleInitiateResult (watcher->future ());
			},
			watcher,
			SIGNAL (finished ()),
			watcher
		};
		watcher->setFuture (CallMgr_->InitiateCall (SourcePubkey_.toUtf8 ()));
	}

	void AudioCall::HandleInitiateResult (const QFuture<CallManager::InitiateResult>& future)
	{
		try
		{
			const auto& result = future.result ();
			CallIdx_ = result.CallIndex_;

			emit stateChanged (SConnecting);
		}
		catch (const ThreadException& ex)
		{
			qWarning () << Q_FUNC_INFO
					<< "got exception:"
					<< ex.what ();
			emit stateChanged (SFinished);
		}
		catch (const CallInitiateException& ex)
		{
			qWarning () << Q_FUNC_INFO
					<< "error initiating call:"
					<< ex.what ();
			emit stateChanged (SFinished);
		}
	}

	namespace
	{
#ifdef ENABLE_MEDIACALLS
		QAudioFormat AvCSettings2Format (const ToxAvCSettings& settings)
		{
			QAudioFormat fmt;
			fmt.setChannelCount (settings.audio_channels);
			fmt.setSampleRate (settings.audio_sample_rate);
			fmt.setSampleSize (16);
			fmt.setByteOrder (QSysInfo::ByteOrder == QSysInfo::BigEndian ?
					QAudioFormat::BigEndian :
					QAudioFormat::LittleEndian);
			fmt.setCodec ("audio/pcm");
			fmt.setSampleType (QAudioFormat::SignedInt);
			return fmt;
		}
#else
		QAudioFormat AvCSettings2Format (const ToxAvCSettings&)
		{
			return {};
		}
#endif
	}

	void AudioCall::MoveToActiveState (const ToxAvCSettings& settings)
	{
		Device_ = std::make_shared<AudioCallDevice> (CallIdx_, CallMgr_);
		Device_->open (QIODevice::ReadWrite);

		Fmt_ = AvCSettings2Format (settings);

		emit stateChanged (SActive);
		emit audioModeChanged (QIODevice::ReadWrite);
	}

	void AudioCall::handleTransferStarting (int32_t callIdx)
	{
		if (callIdx != CallIdx_)
			return;

		qDebug () << Q_FUNC_INFO;
		Util::ExecuteFuture ([this, callIdx] { return CallMgr_->QueryCodec (callIdx); },
				Util::BindMemFn (&AudioCall::MoveToActiveState, this),
				this);
	}
}
}
}
