/*
 * Handler for internal: pseudo-protocol.
 *
 * Here will be 'about', 'keys' (after keys rebinding implementation),
 * and so on.
 *
 * TODO: Internationalize
 *
 */

#include "links.h"

/* We must declare it before... */
struct included_files {
        unsigned char *filename;
        unsigned char *contents;
        int length;
};

#include "docs/included.inc"

void add_internal_header(unsigned char **text, int *length, unsigned char *title, int refresh)
{
	add_to_str(text, length,
		   "<html><head>\n"
		   "<title>");
        add_to_str(text, length, title);
	add_to_str(text, length, "</title>\n");
	if(refresh){
		add_to_str(text, length,
			   "<meta http-equiv=\"Refresh\" content=\"");
		add_num_to_str(text, length, refresh);
		add_to_str(text, length, "\">\n");
	}
	add_to_str(text, length, "</head>\n<body>\n");
	add_to_str(text, length,
		  "</head><body link=red vlink=red alink=red text=black>\n");

	add_to_str(text, length,
		   "<table width=\"100%\" border=0 bgcolor=black cellpadding=1 cellspacing=0>\n"
		   "<tr><td align=center><font color=white>");
	add_to_str(text, length, title);
	add_to_str(text, length,
		   "</font></td></tr><tr><td>"
		   "<table border=0 width=\"100%\" bgcolor=white><tr><td>\n");
}

void add_internal_footer(unsigned char **text, int *length)
{
	add_to_str(text, length,
		   "</td></tr></table>"
		   "</td></tr></table>"
		   "</body>\n"
		   "</html>\n");
}

/* returns 1 if one of included files requested; this file will be shown
 to the user */
static int check_included(unsigned char *name, struct connection *c, struct cache_entry *e)
{
        int i;

        for(i=0;included[i].filename;i++)
                if(included[i].length &&
                   !strcmp(name, included[i].filename)){
                        /* Send included file to the user */
                        add_fragment(e, 0, included[i].contents, included[i].length);
                        truncate_entry(e, included[i].length, 1);
                        c->cache->incomplete = 0;
                        setcstate(c, S_OK);

                        return 1;
                }

        return 0;
}

/* Get name of requested file */
static unsigned char *get_requested_name(unsigned char *url)
{
        if(strlen(url)<9)
                return NULL;

        return stracpy(url+9);
}

/* Create page with list of compiled-in features */
static unsigned char *get_features()
{
        unsigned char *text = init_str();
        int length = 0;
        int i;

	add_internal_header(&text, &length, "Optional Features", 0);

	add_to_str(&text, &length,
                   "<h3 align=left>Hacked Links version "VERSION" with</h3>\n");
#ifdef G
        {
                unsigned char *drivers = (unsigned char*)list_graphics_drivers();

                add_to_str(&text, &length, "<br>Graphics with drivers: ");
                add_to_str(&text, &length, drivers);
                add_to_str(&text, &length,"\n");
                mem_free(drivers);
        }
#endif
#ifdef JS
        add_to_str(&text, &length, "<br>Javascript engine\n");
#endif
#ifdef HAVE_SSL
        add_to_str(&text, &length, "<br>SSL support\n");
#endif
#ifdef HAVE_LUA
        add_to_str(&text, &length, "<br>LUA scripting\n");
#endif
#ifdef HAVE_FREETYPE
        add_to_str(&text, &length, "<br>FreeType font backend\n");
#endif
#ifdef HAVE_GLOBHIST
        add_to_str(&text, &length, "<br>Global history\n");
#endif
#ifdef FORM_SAVE
        add_to_str(&text, &length, "<br>Form saving and loading\n");
#endif
#ifdef HAVE_ZLIB_H
        add_to_str(&text, &length, "<br>Content-Encoding: gzip\n");
#endif
#ifdef HAVE_BZLIB_H
        add_to_str(&text, &length, "<br>Content-Encoding: bzip2\n");
#endif
#ifdef BACKTRACE
        add_to_str(&text, &length, "<br>Backtrace on segfault\n");
#endif
#ifndef USE_GAMMA_CORRECTION
        add_to_str(&text, &length, "<br>Gamma-correction disabled\n");
#endif
        add_internal_footer(&text, &length);

        return text;
}

unsigned char *get_short_name(unsigned char *name, int max_length)
{
	unsigned char *text = init_str();
	int length = 0;

	while(length < max_length &&
	      length < strlen(name)){
		if(max_length - length > 3)
			add_chr_to_str(&text, &length, name[length]);
		else
                        add_chr_to_str(&text, &length, '.');
	}

        return text;
}

#ifdef GLOBHIST
/* Create page with list of recently-visited pages */
static unsigned char *get_globhist()
{
        unsigned char *text = init_str();
        int length = 0;
        struct global_history_item *item;

        add_internal_header(&text, &length, "Global History", 0);

        add_to_str(&text, &length, "<table border=0 width=\"100%\">\n");

	foreach(item, global_history.items){
		unsigned char *short_title = strlen(item->title) ? get_short_name(item->title, 40) : stracpy("Untitled");

		add_to_str(&text, &length,"<tr><td><a href=\"");
                add_to_str(&text, &length,item->url);
                add_to_str(&text, &length,"\">");
                add_to_str(&text, &length, short_title);
                add_to_str(&text, &length,"</a></td><td>");
                add_to_str(&text, &length,ctime(&item->last_visit));
		add_to_str(&text, &length,"</td></tr>\n");
                mem_free(short_title);
        }
        add_to_str(&text, &length, "</table>\n");

	add_internal_footer(&text, &length);

        return text;
}
#endif

/* Create page with list of current downloads */
static unsigned char *get_downloads()
{
        unsigned char *text = init_str();
        int length = 0;
        struct download *down;
        double total_size = 0;
        double total_downloaded = 0;
        double total_speed = 0;


        add_internal_header(&text, &length, "Downloags", 1);

        add_to_str(&text, &length, "<table border=1 frame=void rules=cols cellspacing=1 cellpadding=2 width=\"100%\">\n");

        if(list_empty(downloads))
                add_to_str(&text, &length, "<tr><td>None yet...</td></tr>\n");
	else {
		add_to_str(&text, &length,
			   "<tr>"
			   "<th align=center>File</th>"
			   "<th align=center>%</th>"
			   "<th align=center>ETA</th>"
			   "<th align=center>Transferred</th>"
			   "<th align=center>Speed</th>"
			   "</tr>");

		foreach(down, downloads) {
			struct status *stat = &down->stat;
			unsigned char *short_url = get_short_name(down->file ? down->file : down->url, 40);

			add_to_str(&text, &length, "<tr><td align=left><a href=\"");
			add_to_str(&text, &length, down->url);
			add_to_str(&text, &length, "\">");
			add_to_str(&text, &length, short_url);
			add_to_str(&text, &length, "</a></td>");
			if (stat->state == S_TRANS && stat->prg->elapsed / 100) {
				int pos  = stat->prg->pos;
				int size = stat->prg->size;

				/* Percents */
				add_to_str(&text, &length, "<td align=right>");
				if(size>0){
					add_num_to_str(&text, &length, (int)((longlong)100 * (longlong)pos / (longlong)size));
				} else
					add_to_str(&text, &length, "??");
				add_to_str(&text, &length, "</td>");

				/* Estimated time */
				add_to_str(&text, &length, "<td align=right>");
				if (size >= 0 && stat->prg->loaded > 0)
					add_time_to_str(&text, &length, (stat->prg->size - stat->prg->pos) / ((longlong)stat->prg->loaded * 10 / (stat->prg->elapsed / 100)) * 1000);
				else
					add_to_str(&text, &length, "??");
				add_to_str(&text, &length, "</td>");

				/* Transferred */
				add_to_str(&text, &length, "<td align=right>");
				add_xnum_to_str(&text, &length, pos);
                                total_downloaded += pos;
				if (stat->prg->size >= 0){
					add_to_str(&text, &length, "/");
					add_xnum_to_str(&text, &length, size);
					total_size += size;
				}
				add_to_str(&text, &length, "</td>");

				/* Speed */
				add_to_str(&text, &length, "<td align=right>");
				add_xnum_to_str(&text, &length, (longlong)stat->prg->loaded * 10 / (stat->prg->elapsed / 100));
				add_to_str(&text, &length, "/s");
				if (stat->prg->elapsed >= CURRENT_SPD_AFTER * SPD_DISP_TIME){
					add_to_str(&text, &length, " (");
					add_xnum_to_str(&text, &length, stat->prg->cur_loaded / (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000));
					add_to_str(&text, &length, "/s now)");
                                        total_speed += (double)stat->prg->cur_loaded / (CURRENT_SPD_SEC * SPD_DISP_TIME / 1000);
				} else
                                        total_speed += (double)stat->prg->loaded * 10 / (stat->prg->elapsed / 100);
				add_to_str(&text, &length, "</td></tr>\n");

			} else {
				add_to_str(&text, &length, "<td colspan=4>");
				add_to_str(&text, &length, get_err_msg(stat->state));
				add_to_str(&text, &length, "</td></tr>\n");
			}
                        mem_free(short_url);
		}
        }
        add_to_str(&text, &length, "</table>\n");
        if(total_downloaded > 0){
                /* Short summary */
		add_to_str(&text, &length, "</td></tr><tr><td bgcolor=black><font color=white>");
                add_xnum_to_str(&text, &length, (int)total_downloaded);
		add_to_str(&text, &length, " (");
                add_num_to_str(&text, &length, total_size ? (int)total_downloaded*100./total_size : 0);
		add_to_str(&text, &length, "%) of ");
                add_xnum_to_str(&text, &length, (int)total_size);
		add_to_str(&text, &length, " at  ");
                add_xnum_to_str(&text, &length, (int)total_speed);
		add_to_str(&text, &length, "/s");
		add_to_str(&text, &length, "</font></td></tr>\n");
        }

	add_internal_footer(&text, &length);

        return text;
}

/* Create page with list of currently cached urls */
static unsigned char *get_cache()
{
        unsigned char *text = init_str();
        int length = 0;
	struct cache_entry *ce, *cache;

        cache = (struct cache_entry *)cache_info(CI_LIST);

        add_internal_header(&text, &length, "Cached Pages", 1);

        add_to_str(&text, &length, "<table border=0 width=\"100%\">\n");

	foreach(ce, *cache) {
		add_to_str(&text, &length, "<tr><td><a href=\"");
                add_to_str(&text, &length, ce->url);
		add_to_str(&text, &length, "\">");
                add_to_str(&text, &length, ce->url);
                add_to_str(&text, &length, "</a></td></tr>\n");
        }
        add_to_str(&text, &length, "</table>\n");

	add_internal_footer(&text, &length);

        return text;
}

/* Create page with list of current connections */
static unsigned char *get_connections()
{
        unsigned char *text = init_str();
        int length = 0;
	struct connection *conn, *conn_queue;

        conn_queue = (struct connection *)connect_info(CI_LIST);

        add_internal_header(&text, &length, "Connections", 1);

        add_to_str(&text, &length, "<table border=0 width=\"100%\">\n");

        foreach(conn, *conn_queue) {
                unsigned char *short_url = get_short_name(conn->url, 40);

		add_to_str(&text, &length, "<tr><td>");

		switch(conn->state){
                case(S_WAIT):
                        add_to_str(&text, &length, "WAIT");
                        break;
                case(S_DNS):
                        add_to_str(&text, &length, "DNS");
                        break;
                case(S_CONN):
                        add_to_str(&text, &length, "CONN");
                        break;
                case(S_SSL_NEG):
                        add_to_str(&text, &length, "SSL_NEG");
                        break;
                case(S_SENT):
                        add_to_str(&text, &length, "SENT");
                        break;
                case(S_LOGIN):
                        add_to_str(&text, &length, "LOGIN");
                        break;
                case(S_GETH):
                        add_to_str(&text, &length, "GETH");
                        break;
                case(S_PROC):
                        add_to_str(&text, &length, "PROC");
                        break;
                case(S_TRANS):
                        add_to_str(&text, &length, "TRANS");
                        break;
                default:
                        add_num_to_str(&text, &length, conn->state);
                }
		add_to_str(&text, &length, " <a href=\"");
                add_to_str(&text, &length, conn->url);
		add_to_str(&text, &length, "\">");
                add_to_str(&text, &length, short_url);
		add_to_str(&text, &length, "</a></td></tr>\n");
                mem_free(short_url);
        }
        add_to_str(&text, &length, "</table>\n");

	add_internal_footer(&text, &length);


        return text;
}

void internal_func(struct connection *c)
{
        unsigned char *name = get_requested_name(c->url);
        struct cache_entry *e;
        unsigned char *header;

        /* Very primitive heuristics to determine file type */
        if(strstr(name, ".jpeg"))
                header = stracpy("\r\nContent-Type: image/jpeg;\r\n");
        else if(strstr(name, ".gif"))
                header = stracpy("\r\nContent-Type: image/gif;\r\n");
        else
                header = stracpy("\r\nContent-Type: text/html; charset=utf-8\r\n");

        if (get_cache_entry(c->url, &e)) {
                setcstate(c, S_OUT_OF_MEM);
                goto finish;
        }

        if (e->head) mem_free(e->head);
	e->head = header;
	c->cache = e;

        if (!check_included(name, c, e)){
                unsigned char *text;

                /* Some autogenerated stuff... */
                if(!strcmp(name,"features")) {
                        text=get_features();
#ifdef GLOBHIST
                } else if(!strcmp(name,"history")) {
                        text=get_globhist();
#endif
                } else if(!strcmp(name,"downloads")) {
                        text=get_downloads();
                } else if(!strcmp(name,"cache")) {
                        text=get_cache();
                } else if(!strcmp(name,"connections")) {
                        text=get_connections();
                } else {
                        setcstate(c, S_BAD_URL);
                        goto finish;
                }

                add_fragment(e, 0, text, strlen(text));
                truncate_entry(e, strlen(text), 1);

                c->cache->incomplete = 0;
                setcstate(c, S_OK);

                if(text) mem_free(text);
        }

finish:
        mem_free(name);
        abort_connection(c);
}
