#include <Platform_Lib/SubtitleParser/SubViewerParser.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 SubViewerParser :: Open(char *filename) {
	char textBuffer[SUBVIEWER_BUFFER_LENGTH+1];
	char buffer[2];
	bool bIdentify = true;

	fp = fopen(filename, "rb");

	if(fp == NULL) {
		SetFlag(m_state, SUBVIEWER_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;
	}

	// trying to identify and filter out header information
	if(myfgets(textBuffer, SUBVIEWER_BUFFER_LENGTH, fp, m_charset) == NULL) /* eof/error */
		bIdentify = false;

	if(bIdentify == true && strncmp(textBuffer, "[INFORMATION]", strlen("[INFORMATION]")) != 0) // check for [INFORMATION]
		bIdentify = false;

	if(bIdentify == true) {
		fpos_t currPos;

		while(1) {
			int startH, startM, startS, startMs;
			int stopH, stopM, stopS, stopMs;

			fgetpos(fp, &currPos);

			if(myfgets(textBuffer, SUBVIEWER_BUFFER_LENGTH, fp, m_charset) == NULL) {
				bIdentify = false;
				break;
			}

			if(sscanf(textBuffer, "%d:%d:%d.%d,%d:%d:%d.%d", &startH, &startM, &startS, &startMs, &stopH, &stopM, &stopS, &stopMs) == 8) {
				m_beginOffset = currPos;
				break;
			}
		}
	}

	if(bIdentify == false) {
		SetFlag(m_state, SUBVIEWER_PARSER_STATE_FAIL);

		fclose(fp);
		return false;
	}
	else {
		m_state = 0;
		m_startTimeMs = 0;
		m_stopTimeMs = 0;
		m_lastSeekTimeMs = -1;

		return true;
	}
}

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

int SubViewerParser :: 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, SUBVIEWER_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 SubViewerParser :: SubtitleSeek(int timeMs, SUBTITLE_STYLE_SHEET *stylesheet, char **text,void *SubBuf) {
	if(IS_SET(m_state, SUBVIEWER_PARSER_STATE_FAIL))
		return -1;

	ClearFlag(m_state, SUBVIEWER_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 */
	fsetpos(fp, &m_beginOffset);

	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 * SubViewerParser :: 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 SubViewerParser :: RetrieveNextEntry(void) {
	fpos_t pos;
	int startH, startM, startS, startMs;
	int stopH, stopM, stopS, stopMs;

	char textBuffer[SUBVIEWER_BUFFER_LENGTH+1];
	size_t textLength;

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

	// [1] get time code
	if(sscanf(textBuffer, "%d:%d:%d.%d,%d:%d:%d.%d", &startH, &startM, &startS, &startMs, &stopH, &stopM, &stopS, &stopMs) != 8) {
		SetFlag(m_state, SUBVIEWER_PARSER_STATE_ERROR);
		return -1;
	}

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

	// [3] get subtitle until newline is detected
	m_current_text[0] = '\0';
	do {
		if(myfgets(textBuffer, SUBVIEWER_BUFFER_LENGTH, fp, m_charset) == NULL) /* eof/error */
			return -1;
		if(strlen(textBuffer) + strlen(m_current_text) <= SUBVIEWER_BUFFER_LENGTH)
			strcat(m_current_text, textBuffer);
	} while(textBuffer[0] != '\n' && (textBuffer[0] != '\r' && textBuffer[1] != '\n'));

	// filter out &nbsp & replace [br] to \n
	if(strncmp(m_current_text, "&nbsp", strlen("&nbsp")) == 0)
		m_current_text[0] = '\0';
	else
		ReplaceTags(m_current_text, "[br]", "\n");

	// [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, SUBVIEWER_BUFFER_LENGTH, fp, m_charset) == NULL) /* eof/error */
			return 0;
	} while(textBuffer[0] == '\n' || (textBuffer[0] == '\r' && textBuffer[1] == '\n'));

	if(myfgets(textBuffer, SUBVIEWER_BUFFER_LENGTH, fp, m_charset) == NULL)
		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 * 10;
		stopTimeMs = ((stopH * 60 + stopM) * 60 + stopS) * 1000 + stopMs * 10;

		if(startTimeMs < m_startTimeMs || stopTimeMs < m_stopTimeMs) {
			fsetpos(fp, &pos);
			goto tryagain;
		}
	}

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

void SubViewerParser :: ReplaceTags(char *input, char *needle, char *pattern) {
	char buffer[SUBVIEWER_BUFFER_LENGTH];
	char *pivot1, *pivot2;

	size_t needleSize = strlen(needle);

	if(needleSize < strlen(pattern))
		return;
	else if(strlen(input) > SUBVIEWER_BUFFER_LENGTH)
		return;

	buffer[0] = '\0';
	pivot1 = &(input[0]);

	while(*pivot1 != '\0') {
		if((pivot2 = strstr(pivot1, needle)) != NULL) {
			if(pivot2 != pivot1) {
				size_t copySize = pivot2 - pivot1;

				strncat(buffer, pivot1, copySize);
			}

			strcat(buffer, pattern);
			pivot1 = pivot2 + needleSize;
		}
		else {
			strcat(buffer, pivot1);
			break;
		}
	}

	strcpy(input, buffer);
}

