#include "V4Service.h"
#include <gpac/m4_decoder.h>

#define V4_PRIVATE_SCENE_OTI 0xFF

extern "C" {

V4Channel *V4Service::V4_GetChannel(V4Service *v4service, LPNETCHANNEL ch)
{
	u32 i;
	for (i=0; i<ChainGetCount(v4service->channels); i++) {
		V4Channel *v4c = (V4Channel *)ChainGetEntry(v4service->channels, i);
		if (v4c->ch && v4c->ch==ch) return v4c;
	}
	return NULL;
}

Bool V4Service::V4_RemoveChannel(V4Service *v4service, LPNETCHANNEL ch)
{
	u32 i;
	for (i=0; i<ChainGetCount(v4service->channels); i++) {
		V4Channel *v4c = (V4Channel *)ChainGetEntry(v4service->channels, i);
		if (v4c->ch && v4c->ch==ch) {
			ChainDeleteEntry(v4service->channels, i);
			free(v4c);
			return 1;
		}
	}
	return 0;
}

static Bool V4_CanHandleURL(NetClientPlugin *plug, const char *url)
{
	return 1;
}

static M4Err V4_ConnectService (NetClientPlugin *plug, LPNETSERVICE serv, const char *url)
{
	V4Service *v4serv = (V4Service *)plug->priv;
	v4serv->SetService(serv);
	NM_OnConnect(v4serv->GetService(), NULL, M4OK);
	return M4OK;
}

static M4Err V4_CloseService(NetClientPlugin *plug, Bool immediate_shutdown)
{
	V4Service *v4serv = (V4Service *)plug->priv;
	NM_OnDisconnect(v4serv->GetService(), NULL, M4OK);
	return M4OK;
}

static M4Err V4_Get_MPEG4_IOD(NetClientPlugin *plug, u32 expect_type, const char *sub_url, char **raw_iod, u32 *raw_iod_size)
{
	V4Service *v4service= (V4Service *) plug->priv;
	ESDescriptor *esd;
	InitialObjectDescriptor *iod = (InitialObjectDescriptor *) OD_NewDescriptor(InitialObjectDescriptor_Tag);
	iod->scene_profileAndLevel = 1;
	iod->graphics_profileAndLevel = 1;
	iod->OD_profileAndLevel = 1;
	iod->audio_profileAndLevel = 0xFE;
	iod->visual_profileAndLevel = 0xFE;
	iod->objectDescriptorID = 1;

	esd = OD_NewESDescriptor(0);
	esd->slConfig->timestampResolution = 1000;
	esd->slConfig->useTimestampsFlag = 1;
	esd->ESID = 0xFFFE;
	esd->decoderConfig->streamType = M4ST_PRIVATE_SCENE;
	esd->decoderConfig->objectTypeIndication = 0x01; //V4_PRIVATE_SCENE_OTI; 
	if (v4service->GetPath()) {
		esd->decoderConfig->decoderSpecificInfo->dataLength = strlen(v4service->GetPath()) + 1;
		esd->decoderConfig->decoderSpecificInfo->data = strdup(v4service->GetPath());
	} 
	ChainAddEntry(iod->ESDescriptors, esd);
	OD_EncDesc((Descriptor *)iod, raw_iod, raw_iod_size);
	OD_DeleteDescriptor((Descriptor **) &iod);
	return M4OK;
}

static M4Err V4_ConnectChannel(NetClientPlugin *plug, LPNETCHANNEL channel, const char *url, Bool upstream)
{
	u32 ESID;
	V4Service *v4serv = (V4Service *)plug->priv;
	
	sscanf(url, "ES_ID=%d", &ESID);
	if (!ESID) {
		NM_OnConnect(v4serv->GetService(), channel, M4ChannelNotFound);
	} else {
		V4Channel *v4c = (V4Channel *)malloc(sizeof(V4Channel));
		memset(v4c, 0, sizeof(V4Channel));
		v4c->ch = channel;
		v4c->ESID = ESID;
		ChainAddEntry(v4serv->GetChannels(), v4c);
		NM_OnConnect(v4serv->GetService(), channel, M4OK);
	}
	return M4OK;
}

static M4Err V4_DisconnectChannel(NetClientPlugin *plug, LPNETCHANNEL channel)
{
	V4Service *v4serv = (V4Service *)plug->priv;
	Bool had_ch;

	had_ch = v4serv->V4_RemoveChannel(v4serv, channel);
	NM_OnDisconnect(v4serv->GetService(), channel, had_ch ? M4OK : M4ChannelNotFound);
	return M4OK;
}

static M4Err V4_ServiceCommand(NetClientPlugin *plug, NetworkCommand *com)
{
	V4Service *v4serv = (V4Service *)plug->priv;
	V4Channel *v4c;

	if (!com->base.on_channel) return M4OK;

	v4c = v4serv->V4_GetChannel(v4serv, com->base.on_channel);
	if (!v4c) return M4ChannelNotFound;

	switch (com->command_type) {
	case CHAN_SET_PULL: return M4OK;
	case CHAN_INTERACTIVE: return M4OK;
	case CHAN_SET_PADDING: return M4NotSupported;
	case CHAN_BUFFER:
		com->buffer.max = com->buffer.min = 0;
		return M4OK;
	case CHAN_DURATION:
		/*this plugin is not made for updates, use undefined duration*/
		com->duration.duration = 0;
		return M4OK;
	case CHAN_PLAY:
		v4c->start = (u32) (1000 * com->play.start_range);
		v4c->end = (u32) (1000 * com->play.end_range);
		return M4OK;
	case CHAN_STOP:
		return M4OK;
	case CHAN_CONFIG: return M4OK;
	case CHAN_GET_DSI:
		com->get_dsi.dsi = NULL;
		com->get_dsi.dsi_len = 0;
		return M4OK;
	}
	return M4OK;
}

static M4Err V4_ChannelGetSLP(NetClientPlugin *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, struct tagSLHeader *out_sl_hdr, Bool *sl_compressed, M4Err *out_reception_status, Bool *is_new_data)
{
	V4Channel *v4c;
	V4Service *v4serv = (V4Service *)plug->priv;
	v4c = v4serv->V4_GetChannel(v4serv, channel);
	if (!v4c) return M4ChannelNotFound;

	memset(out_sl_hdr, 0, sizeof(struct tagSLHeader));
	out_sl_hdr->compositionTimeStampFlag = 1;
	out_sl_hdr->compositionTimeStamp = v4c->start;
	out_sl_hdr->accessUnitStartFlag = 1;
	*sl_compressed = 0;
	*out_reception_status = M4OK;
	*is_new_data = 1;
	return M4OK;	return M4OK;
}

static M4Err V4_ChannelReleaseSLP(NetClientPlugin *plug, LPNETCHANNEL channel)
{
	return M4OK;
}

static Bool V4_CanHandleURLInService(NetClientPlugin *plug, const char *url)
{
	return 0;
}

} // extern "C"

V4Service::V4Service(const char *path) 
{

	m_pNetClient = (NetClientPlugin *)malloc(sizeof(NetClientPlugin));
	m_pNetClient->priv = this;

	m_pNetClient->CanHandleURL			= V4_CanHandleURL;

	m_pNetClient->ConnectService		= V4_ConnectService;
	m_pNetClient->CloseService			= V4_CloseService;
	
	m_pNetClient->ConnectChannel		= V4_ConnectChannel;
	m_pNetClient->DisconnectChannel		= V4_DisconnectChannel;
	
	m_pNetClient->Get_MPEG4_IOD			= V4_Get_MPEG4_IOD;
	m_pNetClient->ServiceCommand		= V4_ServiceCommand;

	m_pNetClient->ChannelGetSLP			= V4_ChannelGetSLP;
	m_pNetClient->ChannelReleaseSLP		= V4_ChannelReleaseSLP;

	m_pNetClient->CanHandleURLInService = V4_CanHandleURLInService;

	channels = NewChain();
	if (path) m_path = strdup(path);
}

V4Service::~V4Service() 
{
	if (m_path) free(m_path);
	DeleteChain(channels);
	free(m_pNetClient);
}