/***************************************************************************
 *
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 *
 * Copyright (C) 2006 Frederik M.J. Vestre
 * Copyright (C) 2006 Adam Gashlin
 *
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "plugin.h"
#include <ctype.h>
#include <inttypes.h>
#include "mallocer.h" /*mallocer resetable memory allocator*/
#include "mwdb.h"
#ifdef SIMULATOR
#define printsim printf
#else
#define printsim(...)
#endif
static bool wrquit=false;
static char *filename;
#define MAINMEMBUF 0
#define INFLATEBUF 1
#define ARTICLEBUF 2
/*static void *mainmembuf;*/
void *inflatebuf;
char *articlebuf;
const char * renderloc;
uint32_t articlebuflen=0;
uint32_t articlelen=0;
//uint32_t gbo=0;
//uint8_t lnk=0;
int32_t curline=0;
struct plugin_api *rb;

PLUGIN_HEADER

int nprintf(const char *fmt, ...);
static void viewer_exit(void *parameter);
static void updtscreen(int reset);
static const char * render(const char * article, uint32_t articlelen, int reset, int quick);
static void viewer_exit(void *parameter)
{
    (void)parameter;
/*    Z_Close();
    rb->close(fd);*/
}

static bool viewer_init(void)
{
	char artnmebuf[100];
	
	inflatebuf=0;
	articlelen=0;
	articlebuf=0;
	wpw_init_mempool(rb,MAINMEMBUF);
	inflatebuf=wpw_malloc(MAINMEMBUF,0x13100);
	articlebuflen=wpw_available(MAINMEMBUF)>0x32000?0x32000:wpw_available(MAINMEMBUF);
	articlebuf=wpw_malloc(MAINMEMBUF,articlebuflen);
	rb->memset(artnmebuf,0,100);
	mwdb_getarticle(filename,artnmebuf,100,inflatebuf,articlebuf,articlebuflen,&articlelen,true,true);

	renderloc=articlebuf+10;
	
	updtscreen(1);
	curline=0;
	
	int button;
	while (!wrquit) {
		//button = rb->button_get_w_tmo(HZ/250);
		button = rb->button_get(true);
		//printf("Button:%d\n",button);
		//(1000/HZ) * ticks
		if (rb->default_event_handler_ex(button, viewer_exit, NULL)== SYS_USB_CONNECTED)
			return PLUGIN_USB_CONNECTED;
		switch(button){
			case BUTTON_MENU:
				wrquit=true;
			break;
			case BUTTON_SCROLL_FWD:
			case BUTTON_SCROLL_FWD|BUTTON_REPEAT:
			    updtscreen(0);
			    curline++;
				break;
			case BUTTON_SCROLL_BACK:
			case BUTTON_SCROLL_BACK|BUTTON_REPEAT:
                if (curline>0) curline--;

                /* Hi there, I'm incredibly lazy */
                {
                    int i;
                    renderloc=articlebuf+10;
                    for (i=0;i<curline;i++) {
                        renderloc=render(renderloc,articlelen-(renderloc-(articlebuf+10)),(i==0),1);
                    }
                }
                updtscreen((curline==0));
				break;
		}
		
	}
	return true;
}
uint16_t decode_utf8_char(const char *src,uint16_t *olen)
{
	uint16_t ucs;
	const char *retchar=rb->utf8decode(src,&ucs);
	*olen=retchar-src;
	return ucs;
}

int iswhitespace(uint16_t c) {
    return (c==' ' || c=='\n');
}
int islinebreak(uint16_t c) {
    return (c=='\n');
}

static void updtscreen(int reset) {
    rb->sleep(5);
    
    rb->lcd_clear_display();

	renderloc=render(renderloc,articlelen-(renderloc-(articlebuf+10)),reset,0);

    rb->lcd_update();
}

#define LINEBUFLEN 255
#define MARKUP_STARTBOLD    3
#define MARKUP_STARTITALIC  5
#define MARKUP_ENDFONT      6
#define MARKUP_STARTLINK    7
#define MARKUP_ENDLINK      8
#define MARKUP_BAR          0xd

/* return next place to scroll to */
static const char * render(const char * article, uint32_t articlelen,int reset,int quick) {
    const char * lastspace, * linestart, * nextchar;
    int x,y,i;
    char buf[LINEBUFLEN+1];
    uint16_t ucs;
    int fontheight;
    const char * linknamestart;
    int inlink;
    const char * lastspace_linknamestart=0;
    int lastspace_inlink=0,lastspace_i=0;
    int linedone;

    const char * nextline; /* for scrolling */
    static const char * nextline_linknamestart=0;
    int nextline_inlink=0;

    fontheight = rb->font_get(FONT_UI)->height;
    nextline=0;    
    y=1;
    
    if(reset) {nextline_linknamestart=0;nextline_inlink=0;}
    linknamestart=nextline_linknamestart;
    inlink=nextline_inlink;
    
    //for (i=0;i<0x1000;i++)
    //    printf("%c",article[i]);
        
    //return article;
    
    while (((y+fontheight) < LCD_HEIGHT) && articlelen>0) {
        lastspace=0;
        linestart=article;
        ucs=0;
        /* scroll halfway down the screen */
        //if (y==(LCD_HEIGHT/fontheight/2)*fontheight+1) halfway=article;
        /* see how much we can fit on a line */
        for (x=1,i=0,linedone=0;!linedone && i<LINEBUFLEN && articlelen>0;) {
            /*handle markup*/
            switch (*article) {
                case MARKUP_STARTBOLD:
                case MARKUP_STARTITALIC:
                case MARKUP_ENDFONT:
                    /* no alternate typefaces yet */
                    articlelen--;
                    article++;
                    break;                    
                case MARKUP_STARTLINK:
                    //printf("startlink\n");
                    inlink=1;
                    linknamestart=article;
                    articlelen--;
                    article++;
                    break;
                case MARKUP_ENDLINK:
                    //printf("endlink\n");
                    if (inlink) {
                        /* start outputting link text */
                        articlelen+=article-linknamestart;
                        article-=article-linknamestart;
                        linknamestart=0;
                        inlink=0;
                    }
                    articlelen--;
                    article++;
                    break;
                case MARKUP_BAR:
                    if (inlink) linknamestart=article;
                    articlelen--;
                    article++;
                    break;
                default:
                    nextchar = rb->utf8decode(article,&ucs);
                    if (islinebreak(ucs)) {linedone=1; break;}
                    if (!inlink) {
                        /* display */
                        if ((x+=rb->font_get_width(rb->font_get(FONT_UI),ucs)) > LCD_WIDTH) {linedone=1; break;}

                        if (iswhitespace(ucs)) {
                            lastspace=article;
                            lastspace_i=i;
                            lastspace_linknamestart=linknamestart;
                            lastspace_inlink=inlink;
                        }

                        if (!quick) rb->memcpy(buf+i,article,(nextchar-article));
                        i+=nextchar-article;

                    } else {
                        /* hidden */
                        
                    }
                    articlelen-=nextchar-article;
                    article+=nextchar-article;
            } /* end markup switch */
        } /* end for characters in a line */
        if (x>=LCD_WIDTH) {
            /* the next character would be offscreen, terminate at previous space */
            if (lastspace==0) {
                /* if we have a long word, break it here */
            } else {
                articlelen-=lastspace-article;
                article+=lastspace-article;
                inlink=lastspace_inlink;
                linknamestart=lastspace_linknamestart;
                i=lastspace_i;
            }
                            
            //*lastspace=savechar;
        }
            
        //rb->memcpy(buf,linestart,(((article-linestart)<LINEBUFLEN)?(article-linestart):LINEBUFLEN)*sizeof(char));
        if (!quick) {
            buf[(i<=LINEBUFLEN)?i:LINEBUFLEN]='\0';
        
            rb->lcd_putsxy(1,y, (unsigned char *)buf);
        }
        
        /* if this is a natural newline or we break on a space */   
        if ((x<LCD_WIDTH || lastspace!=0) && articlelen>0) {
            /* consume the character we're breaking on */
            nextchar = rb->utf8decode(article,&ucs);
            articlelen-=nextchar-article;
            article+=nextchar-article;
        }
     
        y+=rb->font_get(FONT_UI)->height;
        /*scroll one line*/
        if (nextline==0) {
            nextline=article;
            nextline_linknamestart=linknamestart;
            nextline_inlink=inlink;
            if (quick) return nextline;
        }
    }

    return (nextline?nextline:article);
}

enum plugin_status plugin_start(struct plugin_api* api, void* file)
{
    rb = api;

    if (!file)
    	return PLUGIN_ERROR;
    filename = file;
    filename[rb->strlen(filename)-4]='\0';

    if (!viewer_init())
	    return PLUGIN_ERROR;
    
    viewer_exit(NULL);
    return PLUGIN_OK;
}

int nprintf(const char *fmt, ...)
{
   static int p_xtpt;
   char p_buf[50];
   bool ok;
   va_list ap;

   va_start(ap, fmt);
   ok = rb->vsnprintf(p_buf,sizeof(p_buf), fmt, ap);
   va_end(ap);

   rb->lcd_putsxy(1,p_xtpt, (unsigned char *)p_buf);
   rb->lcd_update();

   p_xtpt+=rb->font_get(FONT_UI)->height;
   if(p_xtpt>LCD_HEIGHT-rb->font_get(FONT_UI)->height)
   {
      p_xtpt=0;
      rb->lcd_clear_display();
   }
   return 1;
}

