/*
Copyright (C) 2017 Belledonne Communications SARL

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <belle-sip/object.h>
#include <linphone/linphonecore.h>
#include <linphone/linphone_tunnel.h>
#include <linphone/linphonecore_utils.h>
#include <linphone/wrapper_utils.h>
#include "linphone++/linphone.hh"
#include "tools.hh"

using namespace std;
using namespace {{{namespace}}};

{{#classes}}

{{#wrapperCbs}}
static {{{returnType}}} {{{cbName}}}({{{declArgs}}}) {
	{{#ismonolistenable}}
	std::shared_ptr<{{{cppListenerName}}}> listener = std::static_pointer_cast<{{{cppListenerName}}},Listener>(ListenableObject::getListenerFromObject((::belle_sip_object_t *){{{firstArgName}}}));
	{{#hasReturnValue}}return {{/hasReturnValue}}{{{cppMethodCallingLine}}};
	{{/ismonolistenable}}
	
	{{#ismultilistenable}}
	{{{cListenerName}}} *cbs = {{{currentCallbacksGetter}}}({{{firstArgName}}});
	std::list<std::shared_ptr<Listener> > &listeners = *(std::list<std::shared_ptr<Listener> > *){{{userDataGetter}}}(cbs);
	for(auto it=listeners.begin(); it!=listeners.end(); it++) {
		std::shared_ptr<{{{cppListenerName}}}> listener = std::static_pointer_cast<{{{cppListenerName}}},Listener>(*it);
		{{{cppMethodCallingLine}}};
	}
	{{/ismultilistenable}}
}
{{/wrapperCbs}}

{{#isNotListener}}
{{#isrefcountable}}
{{{namespace}}}::{{{className}}}::{{{className}}}(void *ptr, bool takeRef): {{{parentClassName}}}(ptr, takeRef) {
	{{#ismultilistenable}}
	mCallbacks = ({{{cListenerName}}} *)createCallbacks(&getListeners());
	{{{callbacksAdder}}}(({{{cClassName}}} *)mPrivPtr, ({{{cListenerName}}} *)mCallbacks);
	{{/ismultilistenable}}
}
{{/isrefcountable}}

{{#isnotrefcountable}}
{{{namespace}}}::{{{className}}}::{{{className}}}() {
	mPrivPtr = new {{{cClassName}}};
	memset(mPrivPtr, 0, sizeof({{{cClassName}}}));
}

{{{namespace}}}::{{{className}}}::{{{className}}}(const {{{className}}} &src) {
	mPrivPtr = new {{{cClassName}}}(*({{{cClassName}}} *)src.mPrivPtr);
}

{{{namespace}}}::{{{className}}}::{{{className}}}(const void *src) {
	mPrivPtr = new {{{cClassName}}}(*({{{cClassName}}} *)src);
}

{{{namespace}}}::{{{className}}}::~{{{className}}}() {
	delete ({{{cClassName}}} *)mPrivPtr;
}
{{/isnotrefcountable}}
{{/isNotListener}}

{{#ismonolistenable}}
void {{{namespace}}}::{{{className}}}::setListener(const std::shared_ptr<{{{listenerClassName}}}> & listener) {
	ListenableObject::setListener(std::static_pointer_cast<Listener>(listener));
	{{{cListenerName}}} *cbs = {{{cCbsGetter}}}(({{{cClassName}}} *)mPrivPtr);
	if (listener == nullptr) {
		{{#wrapperCbs}}
		{{{callbackSetter}}}(cbs, NULL);
		{{/wrapperCbs}}
	} else {
		{{#wrapperCbs}}
		{{{callbackSetter}}}(cbs, {{{cbName}}});
		{{/wrapperCbs}}
	}
}
{{/ismonolistenable}}

{{#ismultilistenable}}
{{{className}}}::~{{{className}}}() {
	{{{callbacksRemover}}}(({{{cClassName}}} *)mPrivPtr, ({{{cListenerName}}} *)mCallbacks);
	belle_sip_object_unref(mCallbacks);
}

void {{{className}}}::addListener(const std::shared_ptr<{{{listenerClassName}}}> &listener) {
	MultiListenableObject::addListener(std::static_pointer_cast<Listener,{{{listenerClassName}}}>(listener));
}

void {{{className}}}::removeListener(const std::shared_ptr<{{{listenerClassName}}}> &listener) {
	MultiListenableObject::removeListener(std::static_pointer_cast<Listener,{{{listenerClassName}}}>(listener));
}

void *{{{className}}}::createCallbacks(void *userData) {
	{{{cListenerName}}} *cbs = {{{listenerCreator}}}(linphone_factory_get());
	{{#wrapperCbs}}
	{{{callbackSetter}}}(cbs, {{{cbName}}});
	{{/wrapperCbs}}
	{{{userDataSetter}}}(cbs, userData);
	return cbs;
}
{{/ismultilistenable}}

{{#isfactory}}
std::shared_ptr<Core> Factory::createCore(const std::shared_ptr<CoreListener> & listener, const std::string & configPath, const std::string & factoryConfigPath) const {
	::LinphoneCoreCbs *cbs = NULL;
	std::list<std::shared_ptr<Listener> > listeners;
	if (listener != nullptr) {
		listeners.push_back(std::static_pointer_cast<Listener,CoreListener>(listener));
		cbs = (::LinphoneCoreCbs *)Core::createCallbacks(&listeners);
	}
	
	::LinphoneFactory *factory = linphone_factory_get();
	::LinphoneCore *core_ptr = linphone_factory_create_core(factory, cbs, StringUtilities::cppStringToC(configPath), StringUtilities::cppStringToC(factoryConfigPath));
	
	if (cbs != NULL) {
		linphone_core_remove_callbacks(core_ptr, cbs);
		linphone_core_cbs_unref(cbs);
	}
	
	std::shared_ptr<Core> cppCore = cPtrToSharedPtr<Core>((::belle_sip_object_t *)core_ptr, false);
	if (listener != nullptr) cppCore->addListener(listener);
	return cppCore;
}

std::shared_ptr<Core> Factory::createCoreWithConfig(const std::shared_ptr<CoreListener> & listener, const std::shared_ptr<Config> & config) const {
	::LinphoneCoreCbs *cbs = NULL;
	std::list<std::shared_ptr<Listener> > listeners;
	if (listener != nullptr) {
		listeners.push_back(std::static_pointer_cast<Listener,CoreListener>(listener));
		cbs = (::LinphoneCoreCbs *)Core::createCallbacks(&listeners);
	}
	
	::LinphoneFactory *factory = linphone_factory_get();
	::LinphoneCore *core_ptr = linphone_factory_create_core_with_config(factory, cbs, (::LinphoneConfig *)sharedPtrToCPtr(config));
	
	if (cbs != NULL) {
		linphone_core_remove_callbacks(core_ptr, cbs);
		linphone_core_cbs_unref(cbs);
	}
	
	std::shared_ptr<Core> cppCore = cPtrToSharedPtr<Core>((::belle_sip_object_t *)core_ptr, false);
	if (listener != nullptr) cppCore->addListener(listener);
	return cppCore;
}
{{/isfactory}}

{{#isVcard}}
std::shared_ptr<belcard::BelCard> &Vcard::getVcard() {
	return *(shared_ptr<belcard::BelCard> *)linphone_vcard_get_belcard((LinphoneVcard *)mPrivPtr);
}
{{/isVcard}}

{{#methods}}
{{{implPrototype}}} {
	{{{sourceCode}}}
}
{{/methods}}

{{#staticMethods}}
{{{implPrototype}}} {
	{{{sourceCode}}}
}
{{/staticMethods}}

{{/classes}}
