//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "str.h"
#include "var.h"
#include "utils.h"
#include "conf.h"
#include "state.h"
#include "gen_html.h"
#include "mutex.h"

extern char      tmp[MAXMAXLEN];

// Predefined templates
#define HTTP_HEAD1 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n<title>"
#define HTTP_HEAD2 "</title>\n"
#define HTTP_HEAD3 "<meta "
#define HTTP_HEAD4 " content=\"text/html; charset=utf-8\">\n"
#define HTTP_HEAD5 "</head>\n<body"
#define HTTP_HEAD6 ">\n<center>"
#define HTTP_SCRIPT      "<script>\nfunction clearForms()\n{\n  var i;\n  for (i = 0; (i < document.forms.length); i++) {\n    document.forms[i].reset();\n  }\n}\n</script>\n"
#define HTTP_SCRIPT_CALL " onload=\"clearForms()\""

#define HTTP_HEAD_CSS_0  "<style type=\"text/css\">\n"
#define HTTP_HEAD_CSS_1  "table{\n\ttext-align:left;\n}\n"
#define HTTP_HEAD_CSS_B1 "body {\n\tcolor:#"
#define HTTP_HEAD_CSS_B2 "; \n\tbackground-color:#"
#define HTTP_HEAD_CSS_B3 ";\n}\n"
#define HTTP_HEAD_CSS_L1 "#menubutton{\n\tborder-radius:5px;\n\t-moz-border-radius:5px;\n\t-webkit-border-radius:10px;\n}\nselect {\n\tcolor:#"
#define HTTP_HEAD_CSS_L3 "; \n\twidth:"
#define HTTP_HEAD_CSS_BT1 "button {\n\tcolor:#"

#define HTTP_HEAD_CSS_MENU1 ".menu {\n\twidth:"

#define HTTP_HEAD_CSS_MENU2 "px;\n\theight:30px;\n\tfont-size:0.85em;\n\tposition: fixed;\n\tbottom: 0;\n}\n\
.menu ul {\n\tpadding:0;\n\tmargin:0;\n\tlist-style-type:none;\n}\n\
.menu input {\n\tpadding:0;\n\tmargin:0;\n}\n.menu ul ul,\n.menu ul input\n{\n\twidth:"

#define HTTP_HEAD_CSS_MENU3 "px;\n}\n.menu li,\n.menu input {\n\tfloat:left;\n\twidth:"
#define HTTP_HEAD_CSS_MENU4 "px;\n\tposition:relative;\n}\n.menu input {\n\tdisplay:block;\n\tfont-size:13px;\n\ttext-decoration:none;\n\tcolor:#ffffff;\n\twidth:"

#define HTTP_HEAD_CSS_MENU5 "px;\
  height:30px;\
  border:1px solid #999999;\
  border-width:1px 1px 0 0;\
  background:#7582aa;\
  padding-left:10px;\
  line-height:29px;\n}\n.menu ul ul input:hover {\
  color:#fff;\
  background:#949eaa;\
}\n.menu ul ul {\nvisibility:hidden;\
  position:absolute;\
  bottom:31px;\
  left:0; \
  width:" // W/2
  
#define HTTP_HEAD_CSS_MENU6 "px;\n}\n.menu ul ul input {\n\tbackground:#a4a8aa;\n\tcolor:#000;\n\theight:30px;\n\tline-height:1em;\n\tpadding:5px 10px\n\twidth:"

#define HTTP_HEAD_CSS_MENU7 "px;\n}\n.menu ul input:hover {\n\tcolor:#000;\n\tbackground:#949eaa;\n}\n.menu ul li:hover ul {\n\tvisibility:visible;\n\theight:auto;\n}\n"

#define HTTP_HEAD_CSS_99 "</style>\n"

#define HTTP_TABLE1 "\n<table border=\"0\" cellpadding=\""
#define HTTP_TABLE2 "\" cellspacing=\"2\">\n<tbody>\n<tr>\n"
//#define HTTP_TABLE3 "	<td style=\"vertical-align: top; text-align: center; width=5%;\">"
#define HTTP_TABLE3 "	<td>"

#define HTTP_MENU1 "\n [<a href=\""
#define HTTP_MENU2 ".menu\">"
#define HTTP_MENU3 "</a>] "

#define HTTP_LIST1 "<select name=\"list\" size=15>\n"

#define HTTP_FORM  "\n<form action=\"/\" method=\"post\">\n"

#define HTTP_EDIT1 "<h4>"
#define HTTP_EDIT2 "</h4></center>\n<form action=\"\" method=\"post\">\n<input type=\"text\" name=\"http_editfield\" value=\""
#define HTTP_EDIT3 "</h4></center>\n<form action=\"\" method=\"post\">\n<input type=\"password\" name=\"http_editfield\" value=\""
#define HTTP_EDIT4 "\" />\n<hr>"

#define HTTP_TAIL "\n<h6><a href=\"/\">Reload page</a></h6>\n</center></body>\n</html>\n"

int _screenSize = 540;  // min(width,heigth)

void initHtmlGenerator()
{
    char* v1 = dupVarValue("ScreenSize");
    if (v1 != NULL) {
        _screenSize = atoi(v1);
        if (_screenSize <= 0) {
            _screenSize = 540;
        }
        free(v1);
    }
    INFO2("[WS]: $(ScreenSize) = %d", _screenSize);
}

int htmlScreenWidth()
{
    return _screenSize;
}

int htmlScreenHeight()
{
    return _screenSize;
}

string_t* fontHTML(const char* f)
{
    string_t* fh = stringNew("");
    
    if (strncmp(f,"small",5) == 0) {
	    stringAppend(fh, "h6>\n");
    } else if (strncmp(f,"medium",6) == 0) {
    	stringAppend(fh, "h4>\n");
    } else if (strncmp(f,"large",5) == 0) {
    	stringAppend(fh, "h2>\n");
    }
    
    return fh;
}

static int getUseCSS()
{
    return 0;
}

char abuf[8];

static char* getAccessKey(int index)
{
    if (index == 10) {
        abuf[0] = '*';
        abuf[1] = '\0';
    } else if (index == 11) {
        abuf[0] = '0';
        abuf[1] = '\0';
    } else if (index == 12) {
        abuf[0] = '#';
        abuf[1] = '\0';
    } else {
        sprintf(abuf,"%d",index);
    }
    return abuf;
}

static void addFormMenu(int formId, string_t* page)
{
    mutexLock(M_STATE);
    
    SingleList* list = userMenu();

    INFO2("[WS]: addFormMenu %d #%d", formId, listSingleLength(list));
    switch (formId) {

    case CF:
    case TX:
    case WM:
        if (listSingleLength(list) > 0) {

            string_t* menu = stringNew("<hr>");

            //stringAppend(menu, "<div style=\"width:240px; overflow:auto;\">");

            while(list) {

                //INFO2("[WS]: addFormMenu add item ");
                if (list->data && ((string_t*) list->data)->str) {

                    stringAppend(menu, HTTP_MENU1);
                    stringAppend(menu, ((string_t*) list->data)->str);
                    stringAppend(menu, HTTP_MENU2);
                    stringAppend(menu, ((string_t*) list->data)->str);
                    stringAppend(menu, HTTP_MENU3);

                    //INFO2("[WS]: addFormMenu item %s", ((string_t*) list->data)->str);
                }
                list = listSingleNext(list);
            }
            //stringAppend(menu, "</div>");
            stringAppend(menu, "<hr>");
            stringAppend(page, menu->str);
            stringFree(menu,  BOOL_YES);
        }
        break;

    case LI:
    case EF:
    
        {
            int listSz = listSingleLength(list);

            if (listSz > 0) {

                int useCSS = getUseCSS();

                if (useCSS) {

                    string_t* menu = stringNew("<div class=\"menu\">\n<ul>\n");
                    int i = 0;
                    while(list) {

                        if (i == 0) {
                            stringAppend(menu, "<li><input name=\"action\" type=\"submit\" id=\"menubutton\" value=\"");
                            stringAppend(menu, ((string_t*) list->data)->str);
                            stringAppend(menu, "\"></li>\n");
                        } else {
                            if (i == 1 && listSz > 2) {
                                stringAppend(menu, "<li>\n\t<li><input type=\"text\" id=\"menubutton\" value=\"More\">\n\t<ul>\n\t");
                            }
                            stringAppend(menu, "<li><input name=\"action\" type=\"submit\" id=\"menubutton\" value=\"");
                            stringAppend(menu, ((string_t*) list->data)->str);
                            stringAppend(menu, "\"></li>\n\t");
                        }

                        i++;
                        list = listSingleNext(list);
                    }
                    if (listSz > 2) {
                        stringAppend(menu, "</ul>\n");
                    }
                    stringAppend(menu, "</li>\n</ul>\n</div>\n");
                    
                    stringAppend(page, menu->str);
                    stringFree(menu,  BOOL_YES);

                } else {

                    string_t* menu = stringNew("");
                    
                    int i = 0;
                    while(list) {

                        stringAppend(menu, "<input name=\"action\" type=\"submit\" id=\"menubutton\" value=\"");
                        stringAppend(menu, ((string_t*) list->data)->str);
                        stringAppend(menu, "\">\n");

                        i++;
                        list = listSingleNext(list);
                    }
                    stringAppend(menu, "<hr>");
                    stringAppend(page, menu->str);
                    stringFree(menu,  BOOL_YES);
                }
            }
        }
        break;
    }
    
    mutexUnlock(M_STATE);
}

string_t* renderFormHead(int form, const char* caption, const char* fg, const char* bg)
{
    string_t* head = stringNew(HTTP_HEAD1);
    stringAppend(head, caption);
    stringAppend(head, HTTP_HEAD2);
    if (form == LI) {
        stringAppend(head, HTTP_SCRIPT);
    }
    stringAppend(head, HTTP_HEAD3);
    stringAppend(head, HTTP_HEAD4);

    // Do some CSS styling
    int useCSS = getUseCSS();
    
    stringAppend(head, HTTP_HEAD_CSS_0);
    //stringAppend(head, HTTP_HEAD_CSS_1);

    char buf[16];
    sprintf(buf,"%d",_screenSize);
    
    stringAppend(head, HTTP_HEAD_CSS_B1);
    stringAppend(head, fg);
    stringAppend(head, HTTP_HEAD_CSS_B2);
    stringAppend(head, bg);
    
    if (form == LI) {
        stringAppend(head, ";\n\twidth:");
        stringAppend(head, buf);
        stringAppend(head, "px");
    }
    stringAppend(head, HTTP_HEAD_CSS_B3);
    

    if (form == CF) {

        stringAppend(head, HTTP_HEAD_CSS_BT1);
        stringAppend(head, fg);
        stringAppend(head, HTTP_HEAD_CSS_B2);
        stringAppend(head, bg);
        stringAppend(head, ";\n\tborder-radius:10px;\n\t-moz-border-radius:10px;\n\t-webkit-border-radius:10px;\n\tborder:2px solid "); 
        stringAppend(head, fg);
        stringAppend(head, ";}\n");
        stringAppend(head, "p.widthScr {\n\twidth:"); 
	    stringAppend(head, buf);
        stringAppend(head, "px;\n\toverflow:auto;\n}\n");
    
    } else if (form == LI) {
        
	    stringAppend(head, HTTP_HEAD_CSS_L1);
        stringAppend(head, fg);
        stringAppend(head, HTTP_HEAD_CSS_B2);
        stringAppend(head, bg);
        stringAppend(head, HTTP_HEAD_CSS_L3);
        stringAppend(head, buf);
        stringAppend(head, "px");
        stringAppend(head, HTTP_HEAD_CSS_B3);
    
    } else if (form == TX) {

        stringAppend(head, "p.widthScr {\n\twidth:"); 
        stringAppend(head, buf);
        stringAppend(head, "px;\n\toverflow:auto;\n}\n");
    }
    
    if (useCSS) {
        char w2[16];
        sprintf(w2,"%d",_screenSize/2);
        
        char w2_11[16];
        sprintf(w2_11,"%d",_screenSize/2-11);

        char w2_10[16];
        sprintf(w2_10,"%d",_screenSize/2+10);
        
        stringAppend(head, HTTP_HEAD_CSS_MENU1);
        stringAppend(head, buf);
        stringAppend(head, HTTP_HEAD_CSS_MENU2);
        stringAppend(head, w2);
        stringAppend(head, HTTP_HEAD_CSS_MENU3);
        stringAppend(head, w2);
        stringAppend(head, HTTP_HEAD_CSS_MENU4);
        stringAppend(head, w2_11);
        stringAppend(head, HTTP_HEAD_CSS_MENU5);
        stringAppend(head, w2);
        stringAppend(head, HTTP_HEAD_CSS_MENU6);
        stringAppend(head, w2_10);
        stringAppend(head, HTTP_HEAD_CSS_MENU7);
    }
    
    stringAppend(head, HTTP_HEAD_CSS_99);

    stringAppend(head, HTTP_HEAD5);
    if (form == LI) {
        stringAppend(head, HTTP_SCRIPT_CALL);
    }
    stringAppend(head, HTTP_HEAD6);

    return head;
}

string_t* renderCtrlHTMLForm(string_t* no_use, int port)
{
    INFO2("[WS]: renderCtrlHTMLForm %d", bottomlineSkin());

    string_t* fontSpec = fontHTML(cfFont());
    
    string_t* page = renderFormHead(CF, cfCaption(), cfFg(), cfBg());
    
    stringAppend(page, "<");
    stringAppend(page, fontSpec->str);
    stringAppend(page, "\n<p class=\"widthScr\">");
    stringAppend(page, cfStatus());
    stringAppend(page, "</p>\n</");
    stringAppend(page, fontSpec->str);
    
    mutexLock(M_STATE);
    if (bottomlineSkin()) {
        const char * nc = cfNamedCover();
        const char * cv = cfCover();
        //printf("COVER %s %s\n", nc ? "Y" : "N", cv ? "Y" : "N");
        if (nc) {
            stringAppend(page, "\n<img src=\"");
            stringAppend(page, nc);
            stringAppend(page, ".cover\" border=\"1\">"); 
        } else if (cv) {
            stringAppend(page, "\n<img src=\"");
            stringAppend(page, cv);
            stringAppend(page, "\" border=\"1\">");
        }
    }
    mutexUnlock(M_STATE);

    stringAppend(page, HTTP_FORM);

    stringAppend(page, HTTP_TABLE1);
    
    char buf[8];
    sprintf(buf,"%d",iconPadding());
    stringAppend(page, buf);
    
    stringAppend(page, HTTP_TABLE2);

    int i = 0;
    int col = 1;
    
    int iconMax = (bottomlineSkin() ? ICON_NUM_BTM : ICON_NUM);
    
    mutexLock(M_STATE);
    while (i < iconMax) {

        
        const char* icon = cfIcon(i);
        const char* hint = cfHint(i);
	
	    INFO2("[WS]: renderControlForm %d %s", i, (icon? icon : "NULL"));
	
        if (bottomlineSkin() && (!icon || strcmp(icon,"none") == 0)) {
             break; // skip if empty icon specified
        }

        stringAppend(page, HTTP_TABLE3);

        if (!icon || strcmp(icon,"none") == 0) {

            //<button <img src="XX" alt="" style="vertical-align: middle">label_empty</button>
            stringAppend(page, "<button <img src=\"transparent.png\" border=\"0\" alt=\"");
            if (hint && strlen(hint) > 0) {
                stringAppend(page, hint);
                stringAppend(page, "\" title=\"");
                stringAppend(page, hint);
            }
            stringAppend(page, "\" style=\"vertical-align: middle\"></button>");
	
	    } else {


            //<button accesskey="XX" type="submit" value="XX"> <img src="XX" alt="" style="vertical-align: middle">label_empty</button>
            stringAppend(page, "<button name=\"action\" accesskey=\"");
            stringAppend(page, getAccessKey(i+1));
            stringAppend(page, "\" type=\"submit\" value=\"");
            
	        char maze[8];
	        sprintf(maze,"%d",i+1);
            stringAppend(page, maze);
	    
            stringAppend(page, "\"> <img src=\"");
            stringAppend(page, icon);
            stringAppend(page, ".png\" border=\"0\" alt=\"");
            if (hint && strlen(hint) > 0) {
                stringAppend(page, hint);
                stringAppend(page, "\" title=\"");
                stringAppend(page, hint);
            }
            stringAppend(page, "\" style=\"vertical-align: middle\"></button>");

        }

        stringAppend(page, "</td>\n");
        if (!bottomlineSkin() && col == 3) {
            stringAppend(page, "    \n</tr>\n<tr>\n");
            col = 0;
        }
        i++;
        col++;
    }
    mutexUnlock(M_STATE);

    stringAppend(page, "    </tr>\n</tbody>\n</table>");
    stringAppend(page, "    </form>\n");

    stringAppend(page, "<");
    stringAppend(page, fontSpec->str);
    stringAppend(page, "\n<p class=\"widthScr\">");
    stringAppend(page, cfTitle());
    stringAppend(page, "</p>\n");

    const char* v = cfVolume();
    if (useVolume() && v && strlen(v) > 0) {
        stringAppend(page, "<p>");
        stringAppend(page, v);
        stringAppend(page, "%</p>");
    }
    stringAppend(page, "</");
    stringAppend(page, fontSpec->str);
    stringFree(fontSpec,  BOOL_YES);

    addFormMenu(CF, page);

    stringAppend(page, HTTP_TAIL);
    
    return page;
}

string_t* renderTextHTMLForm(string_t* no_use, int port)
{
    INFO2("[WS]: renderTextHTMLForm");
    
    string_t* fontSpec = fontHTML(tfFont());
    
    string_t* page = renderFormHead(TX,tfCaption(),tfFg(),tfBg());

    stringAppend(page, "<");
    stringAppend(page, fontSpec->str);

    mutexLock(M_STATE);
    const char* tx = tfText();
    
    if (tx) {
	    while (1) {
            const char* tx2 = tx;
	        tx = strstr(tx2, "\n");

            stringAppend(page, "\n    <p class=\"widthScr\">");

	        if (tx) {
		        stringAppendLen(page, tx2, tx - tx2);
	        } else {
		        stringAppend(page, tx2);
	        }
	        stringAppend(page, "</p>");

	        if (!tx) break;

	        tx++;
	    }
    }
    mutexUnlock(M_STATE);
    
    stringAppend(page, "\n</");
    stringAppend(page, fontSpec->str);
    stringFree(fontSpec,  BOOL_YES);

    addFormMenu(TX, page);

    stringAppend(page, HTTP_TAIL);
    
    return page;
}

string_t* renderListHTMLForm(string_t* no_use, int port)
{
    INFO2("[WS]: renderListHTMLForm");

    string_t* fontSpec = fontHTML(lfFont());
    string_t* page = renderFormHead(LI,lfCaption(),lfFg(),lfBg());
    
    stringAppend(page, "<");
    stringAppend(page, fontSpec->str);
    stringAppend(page, HTTP_FORM);
    stringAppend(page, HTTP_LIST1);

    int idx = 0;
    
    mutexLock(M_STATE);
    
    SingleList* list = lfList();
    char num[16];

    while (list) {

        sprintf(num,"%d", idx);

        stringAppend(page, "<option value=\"");
        stringAppend(page, num);
        stringAppend(page, "\"");
        if (idx == lfIndex() - 1) {
            stringAppend(page, " selected ");
        }
        stringAppend(page, ">");
        stringAppend(page, ((ListItem*) list->data)->string->str);
        stringAppend(page, "</option>\n");

        list = listSingleNext(list);
        idx++;
    }
    mutexUnlock(M_STATE);

    stringAppend(page, "</select>\n<hr>\n");

    addFormMenu(LI, page);

    stringAppend(page, "</form>\n</");
    stringAppend(page, fontSpec->str);
    stringFree(fontSpec,  BOOL_YES);
    stringAppend(page, "\n");
    stringAppend(page, HTTP_TAIL);

    return page;
}

string_t* renderWmanHTMLForm(string_t* no_use, int port)
{
    INFO2("[WS]: renderWmanHTMLForm");

    string_t* fontSpec = fontHTML(cfFont());
    string_t* page = renderFormHead(WM, cfCaption(), cfFg(), cfBg());
    
    stringAppend(page, "<");
    stringAppend(page, fontSpec->str);
    stringAppend(page, cfTitle());
    stringAppend(page, "</");
    stringAppend(page, fontSpec->str);
    
    mutexLock(M_STATE);
    
    const char* window = wfImage();
    if (window != NULL) {
        stringAppend(page, "\n<img src=\"");
        stringAppend(page,window);
        stringAppend(page, "\" border=\"1\">");
    }
    mutexUnlock(M_STATE);

    addFormMenu(WM, page);

    stringAppend(page, HTTP_TAIL);
    stringFree(fontSpec,  BOOL_YES);

    return page;
}

string_t* renderEditHTMLForm(string_t* no_use, int port)
{
    INFO2("[WS]: renderEditHTMLForm askpass=%d", efPassword());

    string_t* fontSpec = fontHTML(cfFont());
    string_t* page = renderFormHead(EF, efCaption(), cfFg(), cfBg());

    stringAppend(page, "<");
    stringAppend(page, fontSpec->str);
    stringAppend(page, HTTP_EDIT1);
    
    mutexLock(M_STATE);
    
    stringAppend(page, efLabel());

    if (efPassword()) {
        stringAppend(page, HTTP_EDIT3);
    } else {
        stringAppend(page, HTTP_EDIT2);
    }

    stringAppend(page, efText());
    
    mutexUnlock(M_STATE);
    
    stringAppend(page, HTTP_EDIT4);

    addFormMenu(EF, page);

    stringAppend(page, "</form>\n</");
    stringAppend(page, fontSpec->str);
    stringAppend(page, "\n");
    stringAppend(page, HTTP_TAIL);
    
    stringFree(fontSpec,  BOOL_YES);

    return page;
}

//
// Use this for internal password check
//
string_t* renderPassHTMLForm(string_t* no_use, int port)
{
    INFO2("[WS]: renderPasswordForm");

    string_t* fontSpec = fontHTML(cfFont());
    string_t* page = renderFormHead(EF, "Enter Password", cfFg(), cfBg());
    
    stringAppend(page, "<");
    stringAppend(page, fontSpec->str);
    stringAppend(page, HTTP_EDIT1);
    stringAppend(page, "Enter Password");
    stringAppend(page, HTTP_EDIT3);
    stringAppend(page, HTTP_EDIT4);
    
    // add menu manually
    stringAppend(page, "<input name=\"action\" type=\"submit\" value=\"Ok\">\n");
    stringAppend(page, "<input name=\"action\" type=\"submit\" value=\"Back\">\n");

    stringAppend(page, "<hr>\n</form>\n</");
    stringAppend(page, fontSpec->str);
    stringAppend(page, "\n");
    stringAppend(page, HTTP_TAIL);

    stringFree(fontSpec,  BOOL_YES);
    
    return page;
}
