// FileProps.cpp : implementation file
//

#include "stdafx.h"
#include "osmo4.h"
#include "FileProps.h"
#include "MainFrm.h"

/*ISO 639 languages*/
#include <gpac/iso639.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CFileProps dialog


CFileProps::CFileProps(CWnd* pParent /*=NULL*/)
	: CDialog(CFileProps::IDD, pParent)
{
	//{{AFX_DATA_INIT(CFileProps)
	//}}AFX_DATA_INIT
}


void CFileProps::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CFileProps)
	DDX_Control(pDX, IDC_VIEWSEL, m_ViewSel);
	DDX_Control(pDX, IDC_ODINFO, m_ODInfo);
	DDX_Control(pDX, IDC_ODTREE, m_ODTree);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CFileProps, CDialog)
	//{{AFX_MSG_MAP(CFileProps)
	ON_NOTIFY(TVN_SELCHANGED, IDC_ODTREE, OnSelchangedOdtree)
	ON_BN_CLICKED(IDC_WORLD, OnWorld)
	ON_BN_CLICKED(IDC_VIEWSG, OnViewsg)
	ON_WM_TIMER()
	ON_WM_CLOSE()
	ON_WM_DESTROY()
	ON_NOTIFY(TCN_SELCHANGE, IDC_VIEWSEL, OnSelchangeViewsel)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFileProps message handlers


#define FP_TIMER_ID	20

BOOL CFileProps::OnInitDialog()
{
	CDialog::OnInitDialog();

	char sText[5000];
	sprintf(sText, "%s Properties", ((CMainFrame*)GetApp()->m_pMainWnd)->m_pPlayList->GetDisplayName());

	SetWindowText(sText);
	current_odm = NULL;

	m_ViewSel.InsertItem(0, "General");
	m_ViewSel.InsertItem(1, "Streams");
	m_ViewSel.InsertItem(2, "Playback");
	m_ViewSel.InsertItem(3, "Network");

	m_ODTree.SetIndent(0);
	RewriteODTree();
	SetTimer(FP_TIMER_ID, 500, NULL);

	return TRUE;
}


void CFileProps::WriteInlineTree(GF_ObjectManager *root_od, HTREEITEM parent)
{
	Osmo4 *gpac = GetApp();

	/*browse all ODs*/
	u32 count = gf_term_get_object_count(gpac->m_term, root_od);

	for (u32 i=0; i<count; i++) {
		GF_ObjectManager *odm = gf_term_get_object(gpac->m_term, root_od, i);
		if (!odm) return;
		HTREEITEM item = m_ODTree.InsertItem("Object Descriptor", 0, 0, parent);
		m_ODTree.SetItemData(item, (DWORD) odm);
		/*if inline propagate*/
		switch (gf_term_object_subscene_type(gpac->m_term, odm)) {
		case 1:
			m_ODTree.SetItemText(item, "Root Scene");
			WriteInlineTree(odm, item);
			break;
		case 2:
			m_ODTree.SetItemText(item, "Inline Scene");
			WriteInlineTree(odm, item);
			break;
		case 3:
			m_ODTree.SetItemText(item, "Extern Proto Lib");
			WriteInlineTree(odm, item);
			break;
		default:
			break;
		}
	}
}

void CFileProps::RewriteODTree()
{
	Osmo4 *gpac = GetApp();

	m_ODTree.DeleteAllItems();

	GF_ObjectManager *root_odm = gf_term_get_root_object(gpac->m_term);
	if (!root_odm) return;

	HTREEITEM root = m_ODTree.InsertItem("Root OD", 0, 0);
	m_ODTree.SetItemData(root, (DWORD) root_odm);

	m_ODTree.SetItemText(root, "Root Scene");
	WriteInlineTree(root_odm, root);
}

void CFileProps::OnSelchangedOdtree(NMHDR* pNMHDR, LRESULT* pResult)
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	*pResult = 0;

	HTREEITEM item = m_ODTree.GetSelectedItem();
	GF_ObjectManager *odm = (GF_ObjectManager *) m_ODTree.GetItemData(item);
	if (!odm) return;

	SetInfo(odm);
}


void CFileProps::OnClose()
{
	KillTimer(FP_TIMER_ID);
	DestroyWindow();
}

void CFileProps::OnDestroy()
{
	CDialog::OnDestroy();
	delete this;
	((CMainFrame *)GetApp()->m_pMainWnd)->m_pProps = NULL;
}

void CFileProps::OnSelchangeViewsel(NMHDR* pNMHDR, LRESULT* pResult)
{
	SetInfo(current_odm);
	*pResult = 0;
}

void CFileProps::SetInfo(GF_ObjectManager *odm)
{
	current_odm = odm;
	switch (m_ViewSel.GetCurSel()) {
	case 3:
		SetNetworkInfo();
		break;
	case 2:
		SetDecoderInfo();
		break;
	case 1:
		SetStreamsInfo();
		break;
	default:
		SetGeneralInfo();
		break;
	}
}

void CFileProps::OnTimer(UINT_PTR nIDEvent)
{
	if (nIDEvent == FP_TIMER_ID) {
		switch (m_ViewSel.GetCurSel()) {
		case 3:
			SetNetworkInfo();
			break;
		case 2:
			SetDecoderInfo();
			break;
		}
	}

	CDialog::OnTimer(nIDEvent);
}

void CFileProps::SetGeneralInfo()
{
	char info[10000];
	char buf[1000];
	GF_MediaInfo odi;
	GF_ObjectManager *odm;
	u32 h, m, s, i, j;

	Osmo4 *gpac = GetApp();
	odm = current_odm;

	strcpy(info, "");
	if (!odm || gf_term_get_object_info(gpac->m_term, odm, &odi) != GF_OK) return;

	if (!odi.od) {
		strcat(info, odi.service_url);
		m_ODInfo.SetWindowText(info);
		return;
	}
	sprintf(buf, "%sObject Descriptor ID %d\r\n", (odi.has_profiles) ? "Initial " : "", odi.od->objectDescriptorID);
	strcat(info, buf);
	if (odi.duration) {
		h = (u32) (odi.duration / 3600);
		m = (u32) (odi.duration / 60) - h*60;
		s = (u32) (odi.duration) - h*3600 - m*60;
		sprintf(buf, "Duration %02d:%02d:%02d\r\n", h, m, s);
		strcat(info, buf);
	} else {
		strcat(info, "Unknown duration\r\n");
	}
	if (odi.owns_service) {
		strcat(info, "Service Handler: ");
		strcat(info, odi.service_handler);
		strcat(info, "\r\n");
		strcat(info, "Service URL: ");
		strcat(info, odi.service_url);
		strcat(info, "\r\n");
	}

	if (odi.od->URLString) {
		strcat(info, "Remote OD - URL: ");
		strcat(info, odi.od->URLString);
		strcat(info, "\r\n");
	}
	/*get OD content info*/
	if (odi.codec_name) {
		switch (odi.od_type) {
		case GF_STREAM_VISUAL:
			sprintf(buf, "Video Object: Width %d - Height %d\r\n", odi.width, odi.height);
			strcat(info, buf);
			strcat(info, "Media Codec ");
			strcat(info, odi.codec_name);
			strcat(info, "\r\n");
			if (odi.par) {
				sprintf(buf, "Pixel Aspect Ratio: %d:%d\r\n", (odi.par>>16)&0xFF, (odi.par)&0xFF);
				strcat(info, buf);
			}
			break;
		case GF_STREAM_AUDIO:
			sprintf(buf, "Audio Object: Sample Rate %d - %d channels\r\n", odi.sample_rate, odi.num_channels);
			strcat(info, buf);
			strcat(info, "Media Codec ");
			strcat(info, odi.codec_name);
			strcat(info, "\r\n");
			break;
		case GF_STREAM_PRIVATE_SCENE:
		case GF_STREAM_SCENE:
			if (odi.width && odi.height) {
				sprintf(buf, "Scene Description: Width %d - Height %d\r\n", odi.width, odi.height);
			} else {
				sprintf(buf, "Scene Description: No size specified\r\n");
			}
			strcat(info, buf);
			strcat(info, "Scene Codec ");
			strcat(info, odi.codec_name);
			strcat(info, "\r\n");
			break;
		case GF_STREAM_TEXT:
			if (odi.width && odi.height) {
				sprintf(buf, "Text Object: Width %d - Height %d\r\n", odi.width, odi.height);
			} else {
				sprintf(buf, "Text Object: No size specified\r\n");
			}
			strcat(info, buf);
			strcat(info, "Text Codec ");
			strcat(info, odi.codec_name);
			strcat(info, "\r\n");
			break;
		}
	}
	if (odi.protection) {
		strcat(info, "Encrypted Media");
		if (odi.protection==2) strcat(info, " NOT UNLOCKED");
		strcat(info, "\r\n");
	}

	if (!gf_list_count(odi.od->OCIDescriptors)) {
		m_ODInfo.SetWindowText(info);
		return;
	}

	strcat(info, "\r\nObject Content Information:\r\n");

	/*check OCI (not everything interests us) - FIXME: support for unicode*/
	for (i=0; i<gf_list_count(odi.od->OCIDescriptors); i++) {
		GF_Descriptor *desc = (GF_Descriptor *) gf_list_get(odi.od->OCIDescriptors, i);
		switch (desc->tag) {
		case GF_ODF_SEGMENT_TAG:
		{
			GF_Segment *sd = (GF_Segment *) desc;
			strcat(info, "\r\nSegment Descriptor:\r\n");
			sprintf(buf, "Name: %s - start time %g sec - duration %g sec\r\n", sd->SegmentName, sd->startTime, sd->Duration);
			strcat(info, buf);
		}
		break;
		case GF_ODF_CC_NAME_TAG:
		{
			GF_CC_Name *ccn = (GF_CC_Name *)desc;
			strcat(info, "\r\nContent Creators:\r\n");
			for (j=0; j<gf_list_count(ccn->ContentCreators); j++) {
				GF_ContentCreatorInfo *ci = (GF_ContentCreatorInfo *) gf_list_get(ccn->ContentCreators, j);
				if (!ci->isUTF8) continue;
				strcat(info, "\t");
				strcat(info, ci->contentCreatorName);
				strcat(info, "\r\n");
			}
		}
		break;

		case GF_ODF_SHORT_TEXT_TAG:
		{
			GF_ShortTextual *std = (GF_ShortTextual *)desc;
			strcat(info, "\r\n");
			strcat(info, std->eventName);
			strcat(info, ": ");
			strcat(info, std->eventText);
			strcat(info, "\r\n");
		}
		break;
		/*todo*/
		case GF_ODF_CC_DATE_TAG:
			break;
		default:
			break;
		}

	}

	m_ODInfo.SetWindowText(info);
}

void CFileProps::OnWorld()
{
	CString wit;
	const char *str;
	GF_List *descs;
	Osmo4 *gpac = GetApp();

	descs = gf_list_new();
	str = gf_term_get_world_info(gpac->m_term, current_odm, descs);
	if (!str) {
		MessageBox("No World Info available", "Sorry!");
		return;
	}

	wit = "";
	for (u32 i=0; i<gf_list_count(descs); i++) {
		const char *d = (const char *) gf_list_get(descs, i);
		wit += d;
		wit += "\n";
	}
	MessageBox(wit, str);
	gf_list_del(descs);
}

void CFileProps::OnViewsg()
{
	char szOutRadname[GF_MAX_PATH], *pFilename=NULL;
	Osmo4 *gpac = GetApp();

	strcpy(szOutRadname, gpac->szUserPath);
	if ( szOutRadname[strlen(szOutRadname)-1] != '\\'
	        && szOutRadname[strlen(szOutRadname)-1] != '/')
		strcat(szOutRadname, "\\");
	strcat(szOutRadname, "scene_dump");

	GF_Err e = gf_term_dump_scene(gpac->m_term, (char *) szOutRadname, &pFilename, gpac->m_ViewXMTA, GF_FALSE, current_odm);

	if (e) {
		MessageBox(gf_error_to_string(e), "Error while dumping");
	} else {
		ShellExecute(NULL, "open", pFilename, NULL, NULL, SW_SHOWNORMAL);
	}
	if (pFilename) gf_free(pFilename);
}

void CFileProps::SetDecoderInfo()
{
	GF_MediaInfo odi;
	char buf[1000], info[2000];
	u32 h, m, s;
	Osmo4 *gpac = GetApp();

	sprintf(info, "");
	m_ODInfo.SetWindowText("");

	if (!current_odm || gf_term_get_object_info(gpac->m_term, current_odm, &odi)) return;
	if (!odi.od) return;

	strcat(info, "Status: ");
	switch (odi.status) {
	case 0:
	case 1:
	case 2:
		h = (u32) (odi.current_time / 3600);
		m = (u32) (odi.current_time / 60) - h*60;
		s = (u32) (odi.current_time) - h*3600 - m*60;
		sprintf(buf, "%s\r\nObject Time: %02d:%02d:%02d\r\n", (odi.status==0) ? "Stopped" : (odi.status==1) ? "Playing" : "Paused", h, m, s);
		strcat(info, buf);
		break;
	case 3:
		strcat(info, "Not Setup");
		m_ODInfo.SetWindowText(info);
		return;
	default:
		strcat(info, "Setup Failed");
		m_ODInfo.SetWindowText(info);
		return;
	}
	/*get clock drift*/
	sprintf(buf, "Clock drift: %d ms\r\n", odi.clock_drift);
	strcat(info, buf);
	/*get buffering*/
	if (odi.buffer>=0) {
		sprintf(buf, "Buffering Time: %d ms\r\n", odi.buffer);
		strcat(info, buf);
	} else if (odi.buffer==-1) {
		strcat(info, "Not buffering\r\n");
	} else {
		strcat(info, "Not Playing\r\n");
	}
	/*get DB occupation*/
	if (odi.buffer>=0) {
		sprintf(buf, "Decoding Buffer: %d Access Units\r\n", odi.db_unit_count);
		strcat(info, buf);
	}
	/*get CB occupation*/
	if (odi.cb_max_count) {
		sprintf(buf, "Composition Memory: %d/%d Units\r\n", odi.cb_unit_count, odi.cb_max_count);
		strcat(info, buf);
	}

	Float avg_dec_time = 0;
	if (odi.nb_dec_frames) {
		avg_dec_time = (Float) odi.total_dec_time;
		avg_dec_time /= odi.nb_dec_frames;
	}
	sprintf(buf, "Bitrate over last second: %d kbps\r\nMax bitrate over one second: %d kbps\r\nAverage Decoding Time %.2f ms (%d max)\r\nTotal decoded frames %d - %d dropped\r\n",
	        (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time, odi.nb_dec_frames, odi.nb_dropped);
	strcat(info, buf);

	m_ODInfo.SetWindowText(info);
}


void CFileProps::SetStreamsInfo()
{
	u32 i, count;
	char info[10000];
	char buf[1000];
	GF_MediaInfo odi;
	GF_ObjectManager *odm;
	Bool is_media;

	Osmo4 *gpac = GetApp();
	odm = current_odm;

	strcpy(info, "");
	m_ODInfo.SetWindowText("");

	if (!odm || gf_term_get_object_info(gpac->m_term, odm, &odi) != GF_OK) return;
	if (!odi.od) return;


	if (odi.has_profiles) {
		strcat(info, "MPEG-4 Profiles and Levels:\r\n");
		sprintf(buf, "\tOD Profile@Level %d\r\n", odi.OD_pl);
		strcat(info, buf);
		sprintf(buf, "\tScene Profile@Level %d\r\n", odi.scene_pl);
		strcat(info, buf);
		sprintf(buf, "\tGraphics Profile@Level %d\r\n", odi.graphics_pl);
		strcat(info, buf);
		sprintf(buf, "\tAudio Profile@Level %d\r\n", odi.audio_pl);
		strcat(info, buf);
		sprintf(buf, "\tVisual Profile@Level %d\r\n", odi.visual_pl);
		strcat(info, buf);
		sprintf(buf, "\tInline Content Profiled %s\r\n", odi.inline_pl ? "yes" : "no");
		strcat(info, buf);
		strcat(info, "\r\n");
	}
	is_media = GF_FALSE;
	count = gf_list_count(odi.od->ESDescriptors);

	for (i=0; i<count; i++) {
		GF_ESD *esd = (GF_ESD *) gf_list_get(odi.od->ESDescriptors, i);

		sprintf(buf, "\t** Stream ID %d - Clock ID %d **\r\n", esd->ESID, esd->OCRESID);
		strcat(info, buf);
		if (esd->dependsOnESID) {
			sprintf(buf, "Depends on Stream ID %d for decoding\r\n", esd->dependsOnESID);
			strcat(info, buf);
		}
		sprintf(buf, "%s\r\n", gf_esd_get_textual_description(esd));
		strcat(info, buf);

		sprintf(buf, "Buffer Size %d\r\nAverage Bitrate %d bps\r\nMaximum Bitrate %d bps\r\n", esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate, esd->decoderConfig->maxBitrate);
		strcat(info, buf);
		if (esd->slConfig->predefined==SLPredef_SkipSL) {
			sprintf(buf, "Not using MPEG-4 Synchronization Layer\r\n");
		} else {
			sprintf(buf, "Stream Clock Resolution %d\r\n", esd->slConfig->timestampResolution);
		}
		strcat(info, buf);
		if (esd->URLString) {
			sprintf(buf, "Stream Location: %s\r\n", esd->URLString);
			strcat(info, buf);
		}

		/*check language*/
		if (esd->langDesc) {
			s32 lang_idx;
			char lan[4];
			lan[0] = esd->langDesc->langCode>>16;
			lan[1] = (esd->langDesc->langCode>>8)&0xFF;
			lan[2] = (esd->langDesc->langCode)&0xFF;
			lan[3] = 0;

			lang_idx = gf_lang_find(lan);
			if (lang_idx>=0) {
				sprintf(buf, "Stream Language: %s\r\n", gf_lang_get_name(lang_idx) );
				strcat(info, buf);
			}
		}
		strcat(info, "\r\n");
	}

	m_ODInfo.SetWindowText(info);
}

void CFileProps::SetNetworkInfo()
{
	char info[10000];
	char buf[10000];
	u32 id;
	NetStatCommand com;
	GF_MediaInfo odi;
	u32 d_enum, nb_streams;
	GF_Err e;
	GF_ObjectManager *odm;
	Osmo4 *gpac = GetApp();
	odm = current_odm;

	strcpy(info, "");
	m_ODInfo.SetWindowText("");

	if (!odm || gf_term_get_object_info(gpac->m_term, odm, &odi) != GF_OK) return;
	if (!odi.od) return;

	if (odi.owns_service) {
		const char *url, *path;
		u32 done, total, bps;
		strcpy(info, "Current Downloads in service:\r\n");
		d_enum = 0;
		while (gf_term_get_download_info(gpac->m_term, odm, &d_enum, &url, &path, &done, &total, &bps)) {
			if (total && done) {
				sprintf(buf, "%s %s: %d / %d bytes (%.2f %%) - %.2f kBps\r\n", url, path, done, total, (100.0f*done)/total, ((Float)bps)/1024);
			} else {
				sprintf(buf, "%s %s: %.2f kbps\r\n", url, path, ((Float)bps*8)/1024);
			}
			strcat(info, buf);
		}
		if (!d_enum) strcpy(info, "No Downloads in service\r\n");
		strcat(info, "\r\n");
	}

	d_enum = 0;
	nb_streams = 0;
	while (gf_term_get_channel_net_info(gpac->m_term, odm, &d_enum, &id, &com, &e)) {
		if (e) continue;
		if (!com.bw_down && !com.bw_up) continue;
		nb_streams ++;

		sprintf(buf, "Stream ID %d statistics:\r\n", id);
		strcat(info, buf);
		if (com.multiplex_port) {
			sprintf(buf, "\tMultiplex Port %d - multiplex ID %d\r\n", com.multiplex_port, com.port);
		} else {
			sprintf(buf, "\tPort %d\r\n", com.port);
		}
		strcat(info, buf);
		sprintf(buf, "\tPacket Loss Percentage: %.4f\r\n", com.pck_loss_percentage);
		strcat(info, buf);
		sprintf(buf, "\tDown Bandwidth: %.3f kbps\r\n", ((Float)com.bw_down) / 1024);
		strcat(info, buf);
		if (com.bw_up) {
			sprintf(buf, "\tUp Bandwidth: %d bps\r\n", com.bw_up);
			strcat(info, buf);
		}
		if (com.ctrl_port) {
			if (com.multiplex_port) {
				sprintf(buf, "\tControl Multiplex Port: %d - Control Multiplex ID %d\r\n", com.multiplex_port, com.ctrl_port);
			} else {
				sprintf(buf, "\tControl Port: %d\r\n", com.ctrl_port);
			}
			strcat(info, buf);
			sprintf(buf, "\tControl Down Bandwidth: %d bps\r\n", com.ctrl_bw_down);
			strcat(info, buf);
			sprintf(buf, "\tControl Up Bandwidth: %d bps\r\n", com.ctrl_bw_up);
			strcat(info, buf);
		}
		strcat(info, "\r\n");
	}
	if (!nb_streams) strcat(info, "No network streams in this object\r\n");

	m_ODInfo.SetWindowText(info);
}
