#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>
#include <ctype.h>

/*
 * TODO: implement unicode & utf-8 scanners.
 * Cyrillic letters in ../devel/unitxt/u[l|b]ei.txt & utf.txt
 * TODO: 'crossline' flag.
 */

typedef struct	scanner_struct_s	/* scanner for ANSI text */
{
	libv_object_t base;
	char_p		token_buffer_start;
	char_p		token_buffer_end;
	char_p		token_ptr;
	u8_stack_t	token;
	u8_stack_t	delimiters;
    char		*delim;
	vp_stack_t	posstack;
	file_t		mapping;
}	scanner_struct_t,	*scanner_struct_p;

#ifdef	CHECKED_BUILD
#define	SCANNER_MAGIC	0x53525052
V_LOCAL	char			*scanner_object="scanner";
#endif	/* CHECKED_BUILD */

#define	GET_SCANNER(handle,caller) \
		(CHECK_AND_GET_OBJECT(scanner_struct_t,handle,caller, \
			scanner_object,SCANNER_MAGIC))

V_LOCAL	u32_t V_CALL
tokDestroyScanner(scanner_t self)
{
	scanner_struct_p sc=(scanner_struct_p)self;
    if(--sc->base.ref_count)
    	return sc->base.ref_count;
	u8sClear(&sc->token);
    u8sClear(&sc->delimiters);
    vpsClear(&sc->posstack);
	if(sc->mapping)
		fileClose(sc->mapping);
	memFreeObject(self);
    return 0;
}

V_LOCAL	scanner_struct_p
tokAllocScanner(char *delim)
{
	scanner_struct_p s;
	ALLOC_OBJECT(s,scanner_struct_t,0,SCANNER_MAGIC);
	s->base.destroy=tokDestroyScanner;
	u8sInit(&s->delimiters);
	u8sInit(&s->token);
	vpsInit(&s->posstack);
	if(delim)
    {
		u8sPushBuffer(&s->delimiters,(u8_p)delim,strlen(delim)+1);
        u8sAdjust(&s->delimiters);
	}
	else
		u8sPush(&s->delimiters,0);
	s->delim=(char*)u8sGetStorage(&s->delimiters);
	return s;
}

void	V_GLOBAL
tokDestroy(scanner_t handle)
{
	libvCloseHandle(handle,"tokDestroy",scanner_object,SCANNER_MAGIC);
}

scanner_t V_GLOBAL
tokParseString(char *buffer,char *delim)
{
	scanner_struct_p s;
	if(!buffer)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tokCreateScanner('[NULL]','%s'): incorrect parameter.\n",
			dbs(delim));
#endif
		return NULL;
	}
    s=tokAllocScanner(delim);
	s->token_ptr=s->token_buffer_start=buffer;
	s->token_buffer_end=buffer+strlen(buffer);
	return (scanner_t)s;
}

scanner_t V_GLOBAL
tokParseFile(char *filename,char *delim)
{
	scanner_struct_p s;
	file_t infile;
    void *base;
	infile=fileOpenRead(filename);
	if(!infile)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tokParseFile('%s','%s'): can't open file.\n",
			dbs(filename),dbs(delim));
#endif
		return NULL;
	}
    if(!fileControl(infile,pctl_map_ispace,&base))
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tokParseFile('%s','%s'): can't mmap() file.\n",
			filename,dbs(delim));
#endif
    	fileClose(infile);
        return NULL;
    }
	/*
	 *	TODO1: determine type of file: ASCII, LE/BE UNICODE, or UTF-8.
	 */
    s=tokAllocScanner(delim);
    s->mapping=infile;
    s->token_ptr=s->token_buffer_start=(char_p)base;
	s->token_buffer_end=s->token_buffer_start+fileSize(infile);
	return (scanner_t)s;
}

void	V_GLOBAL
tokRestart(scanner_t handle,char *new_buffer,char *new_delim)
{
	scanner_struct_p sc=GET_SCANNER(handle,"tokRestart");
	if(!sc)
    	return;    
	if(new_buffer)
    {
		sc->token_buffer_start=new_buffer;
		sc->token_buffer_end=new_buffer+strlen(new_buffer);
	}
    if(new_delim)
    {
    	u8sResize(&sc->delimiters,0);
		u8sPushBuffer(&sc->delimiters,(u8_p)new_delim,strlen(new_delim));
        u8sAdjust(&sc->delimiters);
		sc->delim=(char*)u8sGetStorage(&sc->delimiters);
	}
	sc->token_ptr=sc->token_buffer_start;
    vpsResize(&sc->posstack,0);
	return;
}

vboolean	V_GLOBAL
tokPush(scanner_t handle)
{
	scanner_struct_p sc=GET_SCANNER(handle,"tokPush");
	if(!sc)
		return false;
	vpsPush(&sc->posstack,sc->token_ptr);
	return true;
}

vboolean	V_GLOBAL
tokPop(scanner_t handle)
{
	scanner_struct_p sc=GET_SCANNER(handle,"tokPop");
	if(!sc)
		return false;
	return vpsPop(&sc->posstack,(void**)&sc->token_ptr);
}

char_p	V_GLOBAL
tokNext(scanner_t handle)
{
	scanner_struct_p sc;
    char_p end;
    sc=GET_SCANNER(handle,"tokNext");
	if(!sc)
    	return NULL;
	end=sc->token_buffer_end;
	/* Skip whitespace */
    for(;sc->token_ptr<end && isspace(*sc->token_ptr);sc->token_ptr++);
	if(sc->token_ptr == end)	/* no more characters */
    {
    	u8sClear(&sc->token);
		return NULL;
	}
	u8sResize(&sc->token,0);	/* clear dest buffer */
	if(*sc->token_ptr=='"')
	{
		/* Extract quoted token */
		sc->token_ptr++;	/* skip quote */
		while(*sc->token_ptr != '"' && sc->token_ptr<end)
		{
			u8sPush(&sc->token,(u8_t)*sc->token_ptr);
			sc->token_ptr++;
		}
		if(*sc->token_ptr=='"')
			sc->token_ptr++;	/* skip quote */
	}
	else
	{
		if(strchr(sc->delim,*sc->token_ptr))
		{
			/* return delimiter */
			u8sPush(&sc->token,(u8_t)*sc->token_ptr);
			sc->token_ptr++;
		}
		else
		{
			/* extract next token enclosed in delimiters */
			while(!strchr(sc->delim,*sc->token_ptr) &&
				!isspace(*sc->token_ptr))
			{
				u8sPush(&sc->token,(u8_t)*sc->token_ptr);
				sc->token_ptr++;
			}
		}
	}
	u8sPush(&sc->token,0);
    return (char*)u8sGetStorage(&sc->token);
}

/*
 * Same as tokNext(), but handles whitespace & quotes as normal chars,
 * unless they are not in sc->delimiters.
 */
char_p	V_GLOBAL
tokStrict(scanner_t handle)
{
	scanner_struct_p sc;
    char_p end;
    sc=GET_SCANNER(handle,"tokStrict");
	if(!sc)
    	return NULL;
	end=sc->token_buffer_end;
	if(sc->token_ptr == end)	/* no more characters */
    {
    	u8sClear(&sc->token);
		return NULL;
	}
	u8sResize(&sc->token,0);	/* clear dest buffer */
	if(strchr(sc->delim,*sc->token_ptr))
	{
		/* return delimiter */
		u8sPush(&sc->token,(u8_t)*sc->token_ptr);
		sc->token_ptr++;
	}
	else
	{
		/* extract next token enclosed in delimiters */
		while(!strchr(sc->delim,*sc->token_ptr))
		{
			u8sPush(&sc->token,(u8_t)*sc->token_ptr);
			sc->token_ptr++;
		}
	}
	u8sPush(&sc->token,0);
    return (char*)u8sGetStorage(&sc->token);
}

/*
 * Extracts next string from buffer.
 */
char_p	V_GLOBAL
tokString(scanner_t handle)
{
	scanner_struct_p sc;
    char_p end;
    sc=GET_SCANNER(handle,"tokString");
	if(!sc)
    	return NULL;
	end=sc->token_buffer_end;
    /* Skip EOLs */
	while(sc->token_ptr < end &&
    	(*sc->token_ptr == 0x0D || *sc->token_ptr==0x0A))
		sc->token_ptr++;
	if(sc->token_ptr == end)	/* no more characters */
    {
    	u8sClear(&sc->token);
		return NULL;
	}
	u8sResize(&sc->token,0);	/* clear dest buffer */
	while(sc->token_ptr < end &&
    	*sc->token_ptr!=0x0D && *sc->token_ptr!=0x0A)
	{
		u8sPush(&sc->token,(u8_t)*sc->token_ptr);
		sc->token_ptr++;
	}
	u8sPush(&sc->token,0);
    return (char*)u8sGetStorage(&sc->token);
}

/*
 *	Returns last extracted token | NULL when no more chars.
 */
char_p	V_GLOBAL
tokLast(scanner_t handle)
{
	scanner_struct_p sc;
    sc=GET_SCANNER(handle,"tokLast");
	if(!sc)
    	return NULL;
    return (char*)u8sGetStorage(&sc->token);
}
