#pragma warning(disable:4115)
#include <windows.h>
#include <stdlib.h>

TYPED_STACK_DEFINE_CLASS(u,unsigned)

/*	======================================================================
 *	Индексированный текстовый файл
 */

typedef struct textlines_s
{
	mmap_t		*fm;	/* Отображение файла базы в память */
	vp_stack_t	rptr;	/* Указатели на начала строк */
    u_stack_t	lens;	/* Длины строк в байтах */
}	textlines_t, *textlines_p;

static unsigned
estimate_line_count(unsigned read_in,unsigned total,unsigned read_lines)
{
	if(read_lines==0)
    	return 1000; /* Just guess ... */
	else
    {
    	unsigned line_len = read_in / read_lines;
		return total / line_len;
	}
}

#pragma warning(disable:4100)
static void __stdcall def_progress(int progress,void *param) {}
#pragma warning(default:4100)

/*
 *	textOpen()
 */
textlines_p __declspec(dllexport) __stdcall
textOpen(char *file_name,void (__stdcall *on_progress)(int,void*),void *param)
{
	unsigned p, cp, len, read_lines, total_lines, read_in;
	char *base, *ptr, *end, *start_line;
	textlines_t *self=(textlines_t*)memAlloc(sizeof(textlines_t));
    if(!on_progress)
    	on_progress=def_progress;
    vpsInit(&self->rptr);
    usInit(&self->lens);
    self->fm=mmapOpenRead(file_name);
    if(!self->fm)
    	goto err_cleanup;
	base=(char*)mmapBase(self->fm);
    end=base+mmapSize(self->fm);
    for(p=cp=0,ptr=base;ptr<end;ptr++)
    {
    	while((*ptr==0x0D || *ptr==0x0A) && ptr<end)
        	ptr++;
        start_line=ptr;
    	while(*ptr!=0x0D && *ptr!=0x0A && ptr<end)
        	ptr++;
		len=ptr-start_line;
        if(len)
        {
        	vpsPush(&self->rptr,start_line);
            usPush(&self->lens,len); /* skip EOL */
		}
        read_lines=vpsCount(&self->rptr);
        if(read_lines)
        {
	        read_in=ptr-base;
    	    total_lines=estimate_line_count(read_in,
            	mmapSize(self->fm),read_lines);
	        cp = read_lines * 100 / total_lines;
    	    if(p!=cp)
	        {
    	    	on_progress(cp,param);
        	    p=cp;
			}
		}
    }
    while(cp<99)
    {
    	on_progress(cp,param);
        cp++;
	}
    if(vpsCount(&self->rptr)==0)
    	goto err_cleanup;
	return self;
err_cleanup:;
	if(self)
    {
    	if(self->fm)
        	mmapClose(self->fm);
		vpsClear(&self->rptr);
    	usClear(&self->lens);
	    memFree(self);
	}
    return NULL;
}

/*
 *	textLineCount()
 */
unsigned __declspec(dllexport) __stdcall
textLineCount(textlines_t *self)
{
	return (self) ? vpsCount(&self->rptr) : 0;
}

/*
 *	textLineLen()
 */
unsigned __declspec(dllexport) __stdcall
textLineLen(textlines_t *self,unsigned line)
{
	unsigned count;
	if(!self)
    	return 0;
	count=vpsCount(&self->rptr);
    if(count<=line)
    	return 0;
	return usGet(&self->lens,line);
}

/*
 *	textLine()
 */
unsigned __declspec(dllexport) __stdcall
textLine(textlines_t *self,unsigned line,char *buffer,unsigned buf_len)
{
	unsigned len,count;
	if(!self)
    	return 0;
	count=vpsCount(&self->rptr);
    if(count<=line)
    	return 0;
	len=usGet(&self->lens,line);
	if(len>=buf_len)
    	return 0;
	memCopy(buffer,vpsGet(&self->rptr,line),len);
    buffer[len]=0;
    return len;
}

/*
 *	textClose()
 */
void __declspec(dllexport) __stdcall
textClose(textlines_t *self)
{
	if(self)
    {
    	if(self->fm)
        	mmapClose(self->fm);
		vpsClear(&self->rptr);
    	usClear(&self->lens);
	    memFree(self);
	}
}

/* #define DEBUG */

#ifdef DEBUG

#include <stdio.h>

void __stdcall progress(int count,void *param)
{
	printf("%d %% done ...\n",count);
}

int main()
{
	unsigned i,num_lines,len;
    char *ptr=NULL;
    unsigned size;
	textlines_t *lines=textOpen("db-big.csv",progress);
    if(!lines)
    {
    	printf("Error: rc=%d\n",GetLastError());
        return -1;
	}
    size=4;
    ptr=(char*)malloc(size);
    num_lines=textLineCount(lines);
    for(i=0;i<num_lines;i++)
    {
    	len=textLineLen(lines,i);
        if(!len)
        {
        	printf("can't access line %d\n",i);
            continue;
        }
        printf("line %d has len=%d; ",i,len);
        len+=4;
        if(len>=size)
        {
            size=len;
        	ptr=(char*)realloc(ptr,size);
            if(!ptr)
	        {
    	    	printf("can't realloc(%d)\n",size);
        	    continue;
	        }
		}
        len=textLine(lines,i,ptr,size);
        if(!len)
        {
        	printf("can't access line !\n",i);
            continue;
        }
		fprintf(stdout,"[%s]\n",ptr);
	}
    if(ptr)
    	free(ptr);
	textClose(lines);
    return 0;
}

#endif /* DEBUG */

