#include <Platform_Lib/SubtitleParser/SRTParser.h>
#include <Platform_Lib/FontRenderer/render.h>
#include <FlowEvent.h>
#include <string.h>
#include <Util.h>

#define SUPPORT_TYPE HTML
#define PRELOADING_PERIOD 300

#ifndef IS_SET
    #define IS_SET(x,y)     ((x) & (y))
    #define ClearFlag(x,y)  ((x) &= ~(y))
    #define SetFlag(x,y)    ((x) |= (y))
#endif

bool SRTParser :: Open(char *filename) {
	char buffer[2];
	fp = fopen(filename, "rb");
	if(fp == NULL) {
		SetFlag(m_state, SRT_PARSER_STATE_FAIL);
		return false;
	}

	fread(buffer, 2, sizeof(char), fp);

	if((buffer[0] & 0xff) == 0xFF && (buffer[1] & 0xff) == 0xFE)
		m_charset = UTF16_LE;
	else if((buffer[0] & 0xff) == 0xFE && (buffer[1] & 0xff) == 0xFF)
		m_charset = UTF16_BE;
	else {
		rewind(fp);
		m_charset = SBCS;
	}

	m_serialNo = 0;
	m_state = 0;
	m_startTimeMs = 0;
	m_stopTimeMs = 0;
	m_lastSeekTimeMs = -1;
	return true;
}

bool SRTParser :: Close() {
	if(fp != NULL)
		fclose(fp);
	return true;
}

int SRTParser :: GetSubtitle(int timeMs, SUBTITLE_STYLE_SHEET *stylesheet, char **text,void *SubBuf) {
	if(stylesheet->SubFrom ==SUBTITLE_INTERNAL) { // begin of internal sub
		EventSpicFrame *pFrame;

		/* style sheet initialization */
		stylesheet->type = SUPPORT_TYPE;
			
		pFrame = (EventSpicFrame *)SubBuf;
		m_startTimeMs = pFrame->startTime;
		m_stopTimeMs = pFrame->startTime + pFrame->duration;
		memcpy(m_current_text,pFrame->payload,pFrame->size);
		m_current_text[pFrame->size]='\r';
		m_current_text[pFrame->size+1]='\n';
		m_current_text[pFrame->size+2]='\0';
		
		if((timeMs + PRELOADING_PERIOD) >= m_startTimeMs && timeMs < m_stopTimeMs) {
			*text = m_current_text;
			return (m_stopTimeMs - timeMs);
		}
		else 
			return -1;
	}
	
	else { // begin of external sub
		if(IS_SET(m_state, SRT_PARSER_STATE_FAIL))
			return -1;

		/* style sheet initialization */
		stylesheet->type = SUPPORT_TYPE;

		/* check current one */
			/* (1) [SUB_TIME START] <NAVTIME> [SUB_TIME STOP] */
		if((timeMs + PRELOADING_PERIOD) >= m_startTimeMs && timeMs < m_stopTimeMs) {
			m_lastSeekTimeMs = -1;
			*text = m_current_text;

			return (m_stopTimeMs - timeMs);
		}
			/* (2) <NAVTIME> [SUB_TIME START] [SUB_TIME STOP] */
		else if(timeMs <= m_startTimeMs) {
			return SubtitleSeek(timeMs, stylesheet, text, NULL);
		}
			/* (3) [SUB_TIME START] [SUB_TIME STOP] <NAVTIME> */
		while(m_stopTimeMs == 0 || timeMs >= m_stopTimeMs) {
			if(RetrieveNextEntry() == -1)
				return -1;
		}

		if((timeMs + PRELOADING_PERIOD) >= m_startTimeMs && timeMs <= m_stopTimeMs) {
			m_lastSeekTimeMs = -1;
			*text = m_current_text;

			return (m_stopTimeMs - timeMs);
		}
		else {
			m_lastSeekTimeMs = timeMs;
			return -1;
		}
	} //end external sub
}


int SRTParser :: SubtitleSeek(int timeMs, SUBTITLE_STYLE_SHEET *stylesheet, char **text,void *SubBuf) {
	if(IS_SET(m_state, SRT_PARSER_STATE_FAIL))
		return -1;

	ClearFlag(m_state, SRT_PARSER_STATE_ERROR);

	/* redundant seek eliminiation */
		/* [last seek] [target] [m_stopTimeMs] */
	if(m_lastSeekTimeMs != -1 && timeMs > m_lastSeekTimeMs && timeMs < m_stopTimeMs) {
		m_lastSeekTimeMs = timeMs;
		return -1;
	}

	m_lastSeekTimeMs = timeMs;

	/* style sheet initialization */
	stylesheet->type = SUPPORT_TYPE;

	/* rewind */
	if(m_charset == UTF16_LE || m_charset == UTF16_BE)
		fseek(fp, 2, SEEK_SET);
	else
		rewind(fp);

	m_startTimeMs = 0;
	m_stopTimeMs = 0;

	while(m_stopTimeMs == 0 || timeMs >= m_stopTimeMs) {
		if(RetrieveNextEntry() == -1)
			return -1;
	}

	if((timeMs + PRELOADING_PERIOD) >= m_startTimeMs && timeMs < m_stopTimeMs) {
		*text = m_current_text;
		return (m_stopTimeMs - timeMs);
	}
	else
		return -1;
}

char * SRTParser :: myfgets(char *s, int size, FILE *stream, char charset) {
	char textBuffer[size+1];

	if(charset == SBCS)
		return fgets(s, size, stream);
	else if(ufgets(textBuffer, size, stream, charset) != NULL) {
		// convert to UTF8
		FontRenderer::charset_convert(charset, UTF8, textBuffer, s, size);
		return s;
	}
	else
		return NULL;
}

int SRTParser :: RetrieveNextEntry(void) {
	fpos_t pos;
	int startH, startM, startS, startMs;
	int stopH, stopM, stopS, stopMs;
	int serialNo;
	int origSerialNo = m_serialNo;

	char textBuffer[SRT_BUFFER_LENGTH+1];
	size_t textLength;

tryagain:
	// [1] get serial number first
	do {
		if(myfgets(textBuffer, SRT_BUFFER_LENGTH, fp, m_charset) == NULL) /* eof/error */
			return -1;
	} while(textBuffer[0] == '\n' || (textBuffer[0] == '\r' && textBuffer[1] == '\n'));

	if(sscanf(textBuffer, "%d", &m_serialNo) != 1) {
		MoveToNextEntry();
		goto tryagain;
	}

	// [2] get time code
	myfgets(textBuffer, SRT_BUFFER_LENGTH, fp, m_charset);
	if(sscanf(textBuffer, "%d:%d:%d,%d --> %d:%d:%d,%d", &startH, &startM, &startS, &startMs, &stopH, &stopM, &stopS, &stopMs) != 8) {
		MoveToNextEntry();
		goto tryagain;
	}

	m_startTimeMs = ((startH * 60 + startM) * 60 + startS) * 1000 + startMs;
	m_stopTimeMs = ((stopH * 60 + stopM) * 60 + stopS) * 1000 + stopMs;

	// [3] get subtitle until newline is detected
	m_current_text[0] = '\0';

	while(1) {
		if(myfgets(textBuffer, SRT_BUFFER_LENGTH, fp, m_charset) == NULL) { /* eof/error */ 
			if(m_current_text[0] == '\0')
				return -1;
			else
				return 0;
		}
		else if(textBuffer[0] == '\n' || (textBuffer[0] == '\r' && textBuffer[1] == '\n'))
			break;

		if(strlen(textBuffer) + strlen(m_current_text) <= SRT_BUFFER_LENGTH)
			strcat(m_current_text, textBuffer);
	}
	// [3.1] trim terminating '\n' and/or '\r\n'
	textLength = strlen(m_current_text);
	if(m_current_text[textLength-1] == '\n') {
		if(m_current_text[textLength-2] == '\r')
			m_current_text[textLength-2] = '\0';
		else
			m_current_text[textLength-1] = '\0';
	}

	// [4] retrieve timestamp of next entry to validate current entry
	fgetpos(fp, &pos);

	do {
		if(myfgets(textBuffer, SRT_BUFFER_LENGTH, fp, m_charset) == NULL) /* eof/error */
			return 0;
	} while(textBuffer[0] == '\n' || (textBuffer[0] == '\r' && textBuffer[1] == '\n'));

	if(sscanf(textBuffer, "%d", &serialNo) != 1) {
		MoveToNextEntry();
		return 0;
	}
	else if(serialNo == origSerialNo) {
		m_serialNo = origSerialNo;
		fsetpos(fp, &pos);
		goto tryagain;
	}
	else if(serialNo < origSerialNo) {
		MoveToNextEntry();
		return 0;
	}

	if(myfgets(textBuffer, SRT_BUFFER_LENGTH, fp, m_charset) == NULL) {
		MoveToNextEntry();
		return 0;
	}

	if(sscanf(textBuffer, "%d:%d:%d,%d --> %d:%d:%d,%d", &startH, &startM, &startS, &startMs, &stopH, &stopM, &stopS, &stopMs) == 8) {
		int startTimeMs, stopTimeMs;

		startTimeMs = ((startH * 60 + startM) * 60 + startS) * 1000 + startMs;
		stopTimeMs = ((stopH * 60 + stopM) * 60 + stopS) * 1000 + stopMs;

		if(startTimeMs < m_startTimeMs || stopTimeMs < m_stopTimeMs) {
			MoveToNextEntry();
			return 0;
		}
	}

	fsetpos(fp, &pos);
	return 0;
}

void
SRTParser::MoveToNextEntry(void) {
	char textBuffer[SRT_BUFFER_LENGTH+1];

	while(1) {
		if(myfgets(textBuffer, SRT_BUFFER_LENGTH, fp, m_charset) == NULL) /* eof/error */
			return;
		else if(textBuffer[0] == '\n' || (textBuffer[0] == '\r' && textBuffer[1] == '\n'))
			return;
	}
}


