#include <Platform_Lib/SubtitleParser/SSAParser.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<Platform_Lib/FontRenderer/render.h>
#include<Application/AppClass/SetupClass.h>
#include<Application/AppClass/setupdef.h>
#include<FlowEvent.h>
#include<Util.h>
//#define DEBUG
#ifdef DEBUG
#define DBGPRINT(fmt,args...)  printf(fmt,## args)
#else
#define DBGPRINT(fmt,args...) 
#endif
#define overlap_display
#define MKVCOLONCNT 8
char* SSAParser::simple_strchr(char* text,char find)
{
	int offset;
	assert(text!=0);
	//if(m_charset_encoding == SBCS){
	if(1){
		while(*text != '\0'){
			if(*text == find)
				return text;
			else {
				FontRenderer::get_char_code(text, UTF8, &offset);
				//printf("%d,%c\n",offset,*text);
				text+=offset;
				}
			}	
	}
	else{//BIG5 or GBK
		while(*text != '\0'){
			if(*text == find)
				return text;			
			if(*text < 0 ){//not single byte charset
				text += 2;
			}
			else
				text++;		
		}
	}
	return 0;//find nothing here
}

bool SSAParser :: Open(char *filename) {
        bool res = true;
        m_fp = fopen(filename, "rb");
        if(m_fp == NULL) {
   		DBGPRINT("file open error!\n");
               return false;
        }   

	switch((SetupClass::GetInstance())->GetTextSubtitleEncoding()){
		case TEXT_ENCODING_UTF8:
			DBGPRINT("Encoding:UTF8\n");
			m_file_encoding = UTF8;
			break;
		case TEXT_ENCODING_GBK:
			DBGPRINT("Encoding:GBK\n");
			m_file_encoding = DBCS_GBK;
			break;
		case TEXT_ENCODING_BIG5:
			DBGPRINT("Encoding:BIG5\n");
			m_file_encoding = DBCS_BIG5;
			break;
		default:
			m_file_encoding = UTF8;
	}
	
	unsigned char tmp[3];
	fread(tmp, 2, sizeof(char), m_fp);

	if((tmp[0] & 0xff) == 0xFF && (tmp[1] & 0xff) == 0xFE)
		m_charset_encoding = UTF16_LE;
	else if((tmp[0] & 0xff) == 0xFE && (tmp[1] & 0xff) == 0xFF)
		m_charset_encoding = UTF16_BE;
	else {
		rewind(m_fp);
		m_charset_encoding = SBCS;
	}
	
        m_startTimeMs = 0;
        m_stopTimeMs = 0;
	 m_pDialoguestart = m_pDialogueend = 0;
	 res = scanScriptInfo();
	 m_pDialoguestartidx=m_pDialogueendidx=m_pDialoguestart;
        return res;//instead of return scanScriptInfo();, we always return true to avoid an exception throw
}

char* SSAParser::ParseDialogue(char* dialogue)
{
	char* tp=dialogue+strlen("Dialogue: ");
	
	//DBGPRINT("%s\n",tp);
	int count=0;
	if(strncmp(tp,"Marked=",strlen("Marked="))==0)
	    sscanf(tp, "Marked=%d,",&m_format.markedlayer);
	else
	    sscanf(tp, "%d,",&m_format.markedlayer);
	int preseeklen;
	int tmp;
	char* tfp;
	//char tmptext[512];
	while((tp=strchr(tp, ','))!=NULL){
		tp++;
		count++;
		switch(count){
			case 1:
				sscanf(tp, "%d:%d:%d.%d,",&m_format.startH,&m_format.startM,&m_format.startS,&m_format.startMS);
				break;
			case 2:
				sscanf(tp, "%d:%d:%d.%d,",&m_format.endH,&m_format.endM,&m_format.endS,&m_format.endMS);
				break;
			case 3:
				preseeklen=strlen(tp);
				tfp=strchr(tp, ',');
				tmp=preseeklen-strlen(tfp)+1;
				//m_format.Style=new char[tmp];
				strncpy(m_format.Style,tp,tmp-1);
				m_format.Style[tmp-1]='\0';
				break;
			case 4:
				preseeklen=strlen(tp);
				if((*tp==' ')||(*tp==',')){
					//m_format.Name=0;
				}
				else{
				tfp=strchr(tp, ',');
				tmp=preseeklen-strlen(tfp)+1;
				//m_format.Name=new char[tmp];
				strncpy(m_format.Name,tp,tmp-1);
				m_format.Name[tmp-1]='\0';
					}
				preseeklen=strlen(tp);
				break;
			case 5:
				sscanf(tp, "%d,",&m_format.MarginL);
				break;
			case 6:
				sscanf(tp, "%d,",&m_format.MarginR);
				break;
			case 7:
				sscanf(tp, "%d,",&m_format.MarginV);
				break;
			case 8:
				preseeklen=strlen(tp);
				tfp=strchr(tp, ',');
				tmp=preseeklen-strlen(tfp)+1;
				//m_format.Effect=new char[tmp];
				strncpy(m_format.Effect,tp,tmp-1);
				m_format.Effect[tmp-1]='\0';
				break;
			case 9:
				break;
			}
		if(count==9)break;
		}
	return tp;

}
void SSAParser::printpoisition()
{
    SSA_Dialogue* iterator;
    iterator = m_pDialoguestartidx;
    if(iterator==0){
        DBGPRINT("start->0");
        return;
    }
    DBGPRINT("start:start:%d;stop:%d;text:%s",iterator->starttime,iterator->stoptime,iterator->text);
    iterator = m_pDialogueendidx;
    if(iterator==0){
        DBGPRINT("end->0");
        return;
    }
    DBGPRINT("end:start:%d;stop:%d;text:%s",iterator->starttime,iterator->stoptime,iterator->text);
}
void SSAParser::printlist()
{
    SSA_Dialogue* iterator;
    iterator = m_pDialoguestart;
    while(iterator!=0){
        if(iterator->text){
            DBGPRINT("subtitle:start:%d;stop:%d;text:%s",iterator->starttime,iterator->stoptime,iterator->text);
        }
        iterator = iterator->next;
        if(iterator == m_pDialogueend){
            DBGPRINT("end...\n");
        }
    }
}
void SSAParser::Addtolist(SSA_Dialogue* pssaDialogue)// add in order of start time
{
    SSA_Dialogue* iterator;
    if(pssaDialogue == 0)
        return;
    if(m_pDialogueend == 0){//init the start and the end
        m_pDialoguestart = pssaDialogue;
        m_pDialogueend = pssaDialogue;
        return;
    }
    else{
        iterator = m_pDialogueend;
        while(iterator->starttime>pssaDialogue->starttime){
            iterator = iterator->last;
            if(iterator == 0){
                m_pDialoguestart->last = pssaDialogue;
                pssaDialogue->next = m_pDialoguestart;
                m_pDialoguestart = pssaDialogue;
                return;
            }
        }
        pssaDialogue->next = iterator->next ;
        pssaDialogue->last = iterator;
        iterator->next = pssaDialogue;
        if(pssaDialogue->next != 0)//middle or the end?
            pssaDialogue->next->last = pssaDialogue;//middle
        else
            m_pDialogueend = pssaDialogue;//end
        return;
    }
}
bool SSAParser::scanScriptInfo()
{
	char* text;
	char* tmp;
	bool headerexists = false;
	bool res = false;
	SSA_Dialogue* pssaDialogue;
	/*init dialogue list*/
	while(1){
		if(m_charset_encoding != SBCS){
			char in[SSA_BUFFER_LENGTH];
			if((tmp=ufgets(in, SSA_BUFFER_LENGTH, m_fp, m_charset_encoding))==NULL)
				break;
			FontRenderer::charset_convert(m_charset_encoding, UTF8,in , m_TextBuffer, SSA_BUFFER_LENGTH);
		}
		else{
			if((tmp=fgets(m_TextBuffer,SSA_BUFFER_LENGTH,m_fp))==NULL)
				break;
		}
		if(strncmp(m_TextBuffer,"Dialogue:",strlen("Dialogue:"))==0){
			text=ParseDialogue(m_TextBuffer);
			if(strlen(text)>0){
			    if(res == false){
			        if(headerexists)
			           res = true;
			   }
			    pssaDialogue = (SSA_Dialogue*)malloc(sizeof(SSA_Dialogue));
			    memset(pssaDialogue,0,sizeof(SSA_Dialogue));
			    pssaDialogue->pos = ftell(m_fp);
			    pssaDialogue->starttime = ((m_format.startH * 60 + m_format.startM) * 60 + m_format.startS) * 1000+m_format.startMS;
			    pssaDialogue->stoptime = ((m_format.endH * 60 + m_format.endM) * 60 + m_format.endS) * 1000+m_format.endMS;
			    pssaDialogue->text = (char*)malloc(strlen(text)+1);
			    memcpy(pssaDialogue->text,text,strlen(text));
			    pssaDialogue->text[strlen(text)] = '\0';
			    Addtolist(pssaDialogue);
			}
		}
		else if(strncmp(m_TextBuffer,"[Script Info]",strlen("[Script Info]"))==0)
		    headerexists = true;
	}
	//printlist();
	return res;
}

void SSAParser::processtext(char* text,bool append)
{
	if(text==0)return;
	char* tmp=text;
	char buffer[SSA_BUFFER_LENGTH];
	int bufferlen=0;
	int marks[512],i=0;
	memset(marks,0,512*sizeof(int));
	memset(buffer,0,SSA_BUFFER_LENGTH);
	 //\N --> '\n';
	while((tmp=simple_strchr(tmp, '\\'))!=NULL){
		if(tmp[1]=='N'){
			tmp[1]='\r';
			tmp[0]='\n';
			}
		tmp++;
		}
	tmp=text;
	DBGPRINT("PTbegin:%s\n",tmp);
	while((tmp=simple_strchr(tmp, '{'))!=NULL){
			DBGPRINT("---%s\n",tmp);
			marks[i++]=tmp-text;
			if((tmp=simple_strchr(tmp, '}'))==NULL)
			{
			    DBGPRINT("{} does not match!\n");
			    marks[--i]=0;//encoding error , break;
			    break;
			}
			marks[i++]=tmp-text;
		}
		i=0;
		//the first one may be 0, so check two
		
		while((marks[i])||(marks[i+1])){
			DBGPRINT("%d\n",marks[i]);
			i++;
			}//the DBGPRINT macro may not work if one sentence
		
		//m_format begin

		//buffer[0]='\0';
		tmp=text;
	        //no "{" in text ,just copy to buffer
		if(marks[1]==0){
	                strcpy(buffer,tmp);
	                bufferlen=strlen(buffer);
	        }  
		else{
		if(marks[0]){
			strncpy(buffer,tmp,marks[0]+1);
			buffer[marks[0]]='\0';
			bufferlen=strlen(buffer);
			}
		i=2;
		if(marks[i]==0){
			char* left=tmp+marks[1]+1;
			strcat(buffer,left);
			bufferlen=strlen(buffer);
			DBGPRINT("...................%s\n",buffer);
			}
		while(marks[i]){
			tmp=text;
			if((marks[i]-marks[i-1])==1){
				i+=2;
				if(marks[i]==0){
					tmp=text+marks[i-1]+1;
					int k=text+strlen(text)-tmp;
					strncat(buffer,tmp,k);
					bufferlen+=k;
					break;
					}
				continue;
				}
			else{
				int k=marks[i]-marks[i-1]-1;
				DBGPRINT("-=-=-=%d\n",k);
				tmp+=marks[i-1]+1;
				strncat(buffer,tmp,k);
				bufferlen+=k;
				//buffer[bufferlen-1]='\0';
				DBGPRINT(">>>>>>>>>>%s\n",buffer);
				i+=2;
				if(marks[i]==0){
					tmp=text+marks[i-1]+1;
					k=text+strlen(text)-tmp;
					strncat(buffer,tmp,k);
					bufferlen+=k;
					break;
					}
				}
			}
		}
		//this should be the end of m_format
		m_format.Text=m_TextBuffer;//new char[bufferlen];
		//copy to m_TextBuffer temporaly , next will pass the unformated m_TextBuffer directly to DrawonSurface
		if(append)strcat(m_format.Text,buffer);
		else strcpy(m_format.Text,buffer);
		bufferlen=strlen(m_format.Text);
		if(m_format.Text[bufferlen-1]!='\n'){
		    m_format.Text[bufferlen]='\n';
		    m_format.Text[bufferlen+1]='\0';
	 	}
	 	else
	 	    m_format.Text[bufferlen]='\0';
		DBGPRINT("SSAINFO:%s\n",m_format.Text);
	//tmp=strchr(text, '{');
}

int SSAParser :: GetSubtitle(int timeMs, SUBTITLE_STYLE_SHEET *stylesheet, char **text,void *SubBuf) 
{
	EventSpicFrame *pFrame;
	SSA_Dialogue* iterator;
	int endtime;
	char tmpTextBuffer[SSA_BUFFER_LENGTH];
	bool overlap=true;

	if(stylesheet->SubFrom ==SUBTITLE_INTERNAL){//begin internal sub
		stylesheet->type = PLAIN;
		pFrame = (EventSpicFrame *)SubBuf;
		m_startTimeMs = pFrame->startTime;
		m_stopTimeMs = pFrame->startTime + pFrame->duration;
		memcpy(tmpTextBuffer,pFrame->payload,pFrame->size);
		tmpTextBuffer[pFrame->size]='\0';
		int ii=0;
		char* tmp;
		tmp=tmpTextBuffer;
		while(ii<MKVCOLONCNT){
			tmp=strchr(tmp,',');
			if(tmp==NULL)
				return -1;
			tmp++;
			ii++;
			}
		if(strncmp(tmp,"&nbsp",strlen("&nbsp"))!=0)
		    processtext(tmp,false);
		if(timeMs >= m_startTimeMs && timeMs < m_stopTimeMs) {
			*text = m_TextBuffer;
			//DBGPRINT("****%s\n",m_TextBuffer);
			return(m_stopTimeMs - timeMs);
		}
		else 
			return -1;
	}

	stylesheet->type = PLAIN;
	memset(m_TextBuffer,0,SSA_BUFFER_LENGTH);
	if(m_pDialoguestartidx == 0)
		return -1;
	while(m_pDialoguestartidx->starttime<timeMs){
	       if(m_pDialoguestartidx->next != 0){
		    m_pDialoguestartidx=m_pDialoguestartidx->next;//update start idx
		}
		else{
		    if(m_pDialoguestartidx->stoptime<timeMs){
		        m_pDialoguestartidx = 0;//end of dialogues
		        return -1;
		    }
		    break;
		}
	}
	if(m_pDialoguestartidx->starttime>timeMs){//seek in list and find the right position for the start
	    if(m_pDialoguestartidx->last == 0){//the first sentence has not started
	        return -1;//m_pDialoguestartidx->starttime-timeMs;
	    }
	}
	//printpoisition();
	//m_pDialogueendidx point to the first dialogue which is not end
	//m_pDialogueendidx will always in the front of m_pDialogueStartidx
	iterator = m_pDialogueendidx;
	if(iterator == 0)
		return -1;
	while(iterator->stoptime<timeMs){
	    if(m_pDialogueendidx == m_pDialoguestartidx){
	        DBGPRINT("not a good file\n");
	        break;
	    }
	    iterator = iterator->next;
	    if(iterator == 0)
	        return -1;
	    m_pDialogueendidx = iterator;//update end idx
	}
	endtime=m_pDialogueendidx->stoptime;
	if(m_pDialogueendidx->starttime>timeMs){
	    DBGPRINT("timeMs:%d\n",timeMs);
	    DBGPRINT("no suitable dialogue:%d\n",m_pDialogueendidx->starttime);
	    return m_pDialogueendidx->starttime-timeMs;
	}
	DBGPRINT("initial end time:%d\n",endtime);
	//printpoisition();
	//check from m_pDialoguestartidx to m_pDialogueendidx, merge all dialogue in time	
	while(iterator != m_pDialoguestartidx){
	    if((iterator->stoptime>timeMs)&&(iterator->starttime<timeMs)){
	        DBGPRINT("overlap:  time:%d\n",iterator->starttime);
	        if(strncmp(iterator->text,"&nbsp",strlen("&nbsp"))!=0)
	            processtext(iterator->text,overlap);
	        if(iterator->stoptime<endtime){
	            endtime = iterator->stoptime;
	            DBGPRINT("endtime updated:%d\n",endtime);
	        }
	    }
	    iterator = iterator->next;
	}
	if((m_pDialoguestartidx->starttime<=timeMs)&&(m_pDialoguestartidx->stoptime>timeMs)){
	    DBGPRINT("merge current dialogue in\n");
	    if(strncmp(m_pDialoguestartidx->text,"&nbsp",strlen("&nbsp"))!=0)
	        processtext(m_pDialoguestartidx->text,overlap);//get rid of things like {}
	}
	m_startTimeMs = timeMs;
	*text=m_TextBuffer;
	//choose a smaller endtime
	if((m_pDialoguestartidx->starttime<endtime)&&(m_pDialoguestartidx->starttime>timeMs)){//choose between this->stoptime and next->starttime
	    endtime = m_pDialoguestartidx->starttime;
	}
	m_stopTimeMs = endtime;
	DBGPRINT("GETSUBTITLE:result:\n%s",*text);
	DBGPRINT("GETSUBTITLE:result:timeMs:%d\n",timeMs);
	DBGPRINT("GETSUBTITLE:result:m_stopTimeMs:%d\n",m_stopTimeMs);
	DBGPRINT("GETSUBTITLE:result:m_startTimeMs:%d\n",m_startTimeMs);
	DBGPRINT("------------------------------------------\n");
	return (m_stopTimeMs-m_startTimeMs);
}

int SSAParser :: SubtitleSeek(int timeMs, SUBTITLE_STYLE_SHEET *stylesheet, char **text,void *SubBuf)
{
		m_pDialoguestartidx = m_pDialogueendidx = m_pDialoguestart;
		return GetSubtitle(timeMs, stylesheet,text,SubBuf);
}

bool SSAParser :: Close() 
{
    SSA_Dialogue* pdialogue=m_pDialoguestart;
    if(m_fp != NULL)
        fclose(m_fp);
    while(pdialogue!=0){//release the buffer
        SSA_Dialogue* tmp=pdialogue;
        pdialogue = pdialogue->next;
        if(tmp->text!=0)
            free(tmp->text);
        free(tmp);
    }
    return true;
}
