/* Some part of this file is Copyright (C) 1996 - G.Dauphin
 * See the file "license.mMosaic" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */ 

#include "../config.h"
#include <Xm/TextF.h>
#include "HTMLP.h"
#include "HTMLPutil.h"
#include "../src/mosaic.h"
#include "HTMLform.h"
#include "../libnut/str-tools.h"

#ifndef DISABLE_TRACE
extern int reportBugs;
extern int htmlwTrace;
#endif

typedef enum { 
	InputTypeText,
	InputTypePassword,
	InputTypeCheckbox,
	InputTypeRadio,
	InputTypeSubmit,
	InputTypeReset,
	InputTypeFile,
	InputTypeHidden,
	InputTypeImage,
	InputTypeTextArea
} FormInputType;

static void ProcessOption( SelectInfo *sptr);

/* Fillout forms.  Cannot be nested. */

/* </FORM> */
void EndForm(HTMLWidget hw, MarkInfo ** mptr, PhotoComposeContext * pcc)
{
	if (!pcc->in_form && !pcc->cur_form) { /* It's an error */
		return;
	}

	if (pcc->cw_only) {
		pcc->in_form = False; /* Always put False on end Form */
				      /* because form cannot be nested */
		if (pcc->cur_form->cw_only) {
			if (pcc->cur_form->action)
				free (pcc->cur_form->action);
			if (pcc->cur_form->method)
				free (pcc->cur_form->method);
			if (pcc->cur_form->enctype)
				free (pcc->cur_form->enctype);
			free (pcc->cur_form);
		}
		pcc->cur_form = NULL;
		return;
	}

	/* Here we go to create it really */
	pcc->cur_form->end = pcc->widget_id;
	AddNewForm(hw, pcc->cur_form);
	pcc->cur_form = NULL;
	pcc->in_form = False; /* Always put False on end Form */
}

/* <FORM> */
void BeginForm(HTMLWidget hw, MarkInfo **mptr, PhotoComposeContext *pcc)
{
	MarkInfo *mark = *mptr;

	if (!pcc->cw_only && (pcc->in_form || pcc->cur_form)) {
#ifndef DISABLE_TRACE
		if (htmlwTrace || reportBugs) {
			fprintf(stderr, "Warning: A Form in Form!\n");
		}
#endif
		return;
	}

	/* Create a Form structure */
	pcc->cur_form = (FormInfo *)malloc(sizeof(FormInfo));
	pcc->cur_form->next = NULL;
	pcc->cur_form->hw = (Widget)hw;
	pcc->cur_form->action = ParseMarkTag(mark->start, MT_FORM, "ACTION");
	pcc->cur_form->method = ParseMarkTag(mark->start, MT_FORM, "METHOD");
	pcc->cur_form->enctype = ParseMarkTag(mark->start, MT_FORM, "ENCTYPE");
	pcc->cur_form->start = pcc->widget_id;
	pcc->cur_form->end = -1;
	pcc->cur_form->button_pressed = NULL;
	pcc->in_form = True;
	if (pcc->cw_only)
		pcc->cur_form->cw_only = 1;
	else
		pcc->cur_form->cw_only = 0;
}


/* Just insert the widget.  Can only be inside a FORM tag.
 * Special case the type=image stuff to become a special IMG tag.
 */
void FormInputField(HTMLWidget hw, MarkInfo **mptr, PhotoComposeContext *pcc)
{
	MarkInfo *mark = *mptr;
	char *tptr2, *tptr, *sptr;
	char *text;
	FormInputType input_type = InputTypeText;

	if (!pcc->in_form)		/* error */
		return;

	pcc->have_space_after = 0;

	text = mark->start;
	tptr = ParseMarkTag(text, MT_INPUT, "TYPE");
	if (!tptr) { 		/* Assume type = TEXT */
		input_type = InputTypeText;
	} else {
		if (!my_strcasecmp(tptr, "text")) {
			input_type = InputTypeText;
		} else if (!my_strcasecmp(tptr, "number")) {
			input_type = InputTypeText;
		} else if (!my_strcasecmp(tptr, "password")) {
			input_type = InputTypePassword;
		} else if (!my_strcasecmp(tptr, "checkbox")) {
			input_type = InputTypeCheckbox;
		} else if (!my_strcasecmp(tptr, "radio")) {
			input_type = InputTypeRadio;
		} else if (!my_strcasecmp(tptr, "submit")) {
			input_type = InputTypeSubmit;
		} else if (!my_strcasecmp(tptr, "reset")) {
			input_type = InputTypeReset;
		} else if (!my_strcasecmp(tptr, "file")) {
			input_type = InputTypeFile;
		} else if (!my_strcasecmp(tptr, "hidden")) {
			input_type = InputTypeHidden;
		} else if (!my_strcasecmp(tptr, "image")) {
			input_type = InputTypeImage;
			if (pcc->cw_only) {
				/* Get width and height of image */
				ImagePlace(hw, *mptr, pcc);
				free (tptr);
				return;
			}
		} else {	/* error */
			free (tptr);
			return;
		}
	}

	if (!pcc->cur_form) {		/* error */
		if (tptr)
			free (tptr);
		return;
	}

	if (tptr && caseless_equal(tptr, "image")) {
		sptr = ParseMarkTag(text, MT_INPUT, "NAME");
		free(tptr);
		tptr = (char *)malloc(strlen(mark->start) +
				strlen(" ISMAP") + strlen(MT_IMAGE) -
				strlen(MT_INPUT) + 1);
		strcpy(tptr, MT_IMAGE);
		strcat(tptr, (char *) (mark->start + strlen(MT_INPUT)));
		strcat(tptr, " ISMAP");
		tptr2 = mark->start;
		mark->start = tptr;
		ImagePlace(hw, *mptr, pcc);
		mark->start = tptr2;
		free(tptr);
		/* Save the NAME value so we know which image is which */
		if (sptr && mark->s_picd)
			mark->s_picd->text = sptr;
	} else if (tptr && caseless_equal(tptr, "hidden")) {
		/* Hidden inputs have no element associated with them,
		 * just a widget record. */
		free(tptr);
		pcc->widget_id++;
		(void)MakeWidget(hw, mark->start, pcc, pcc->widget_id);
	} else {
		if (tptr)
			free(tptr);
		WidgetPlace(hw, *mptr, pcc);
	}
}

/* TEXTAREA is a replacement for INPUT type=text size=rows,cols
 * name REQUIRED
 * rows REQUIRED
 * cols REQUIRED
 */             
void FormTextAreaBegin(HTMLWidget hw, MarkInfo **mptr, PhotoComposeContext *pcc)
{
	char *buf;
	int len;        
	MarkInfo *mark = *mptr;
	char *cptr, *rptr;
	int dir, ascent, descent;
	XCharStruct all;
	char *text;
	int m_w, def_w, def_h;
	int cols, rows;
	FormInputType input_type = InputTypeTextArea;

	if (!pcc->in_form || !pcc->cur_form)		/* error */
		return;

	pcc->have_space_after = 0;
	pcc->is_bol = 0;
	pcc->pf_lf_state = 0;

	text = (*mptr)->start;
	cols = 40;
	cptr = ParseMarkTag(text, MT_INPUT, "COLS");
	if (cptr) {
		cols = atoi(cptr);
		free(cptr);
	}
	if (cols <= 0)
		cols = 40;

	rows = 4; 
	rptr = ParseMarkTag(text, MT_INPUT, "ROWS");
	if (rptr) {
		rows = atoi(rptr);
		free(rptr);
	}
	if (rows <= 0)
		rows = 4; 

	if (!pcc->text_area_buf) {
		/* Construct the start of a fake INPUT tag. */
		len = strlen(MT_INPUT) + strlen(" type=textarea value=\"\"");
		buf = (char *)malloc(len + strlen(mark->start) + 1);
		strcpy(buf, MT_INPUT);
		strcat(buf, (char *) (mark->start + strlen(MT_TEXTAREA)));
		strcat(buf, " type=textarea");
		strcat(buf, " value=\"");
		pcc->text_area_buf = buf;  
		pcc->ignore = 1;    
	}                      
}

void FormTextAreaEnd(HTMLWidget hw, MarkInfo **mptr, PhotoComposeContext *pcc)
{
	MarkInfo *mark = *mptr;
	char *start;
	char *buf;

	if (!pcc->in_form || !pcc->cur_form || !pcc->text_area_buf)
		return;

	/* Finish a fake INPUT tag. */
	buf = (char *)malloc(strlen(pcc->text_area_buf) + 2); 
	strcpy(buf, pcc->text_area_buf);
	strcat(buf, "\"");
	/* Stick the fake in, saving the real one. */
	start = mark->start;
	mark->start = buf;
	mark->is_end = 0;
	WidgetPlace(hw, mark, pcc);
	/* Free the fake, put the original back */
	free(buf);
	free(pcc->text_area_buf);
	mark->start = start;
	mark->is_end = 1;
	pcc->text_area_buf = NULL;
	pcc->ignore = 0;
}


/* Can only be inside a SELECT tag. */
void FormSelectOptionField(HTMLWidget hw, MarkInfo **mptr,
        PhotoComposeContext *pcc)
{
	MarkInfo *mark = *mptr;
	char *tptr;

	if (!pcc->in_form || !pcc->in_select || !pcc->cur_form)	/* error */
		return;

	if (pcc->current_select) {
		if (pcc->current_select->option_buf)
			ProcessOption(pcc->current_select);
		pcc->current_select->option_buf = (char *)malloc(1);
		strcpy(pcc->current_select->option_buf, "");
		/* Check if this option starts selected */
		tptr = ParseMarkTag(mark->start, MT_OPTION, "SELECTED");
		if (tptr) {
			pcc->current_select->is_value = 1;
			free(tptr);
		} else {
			pcc->current_select->is_value = 0;
		}       
		/* Check if this option has a different return value field. */
		tptr = ParseMarkTag(mark->start, MT_OPTION, "VALUE");
		if (tptr) {
			pcc->current_select->retval_buf = tptr;
		} else {       
			pcc->current_select->retval_buf = NULL;
		}              
	}                      
}

/* Special INPUT tag.  Allows an option menu or a scrolled list.
 * Due to a restriction in SGML, this can't just be a subset of
 * the INPUT markup.  However, I can treat it that way to avoid duplicating
 * code.  As a result I combine SELECT and OPTION into a faked up INPUT mark.
 */
void FormSelectBegin(HTMLWidget hw, MarkInfo **mptr, PhotoComposeContext *pcc)
{
	MarkInfo *mark = *mptr;

	if (!pcc->in_form || !pcc->cur_form)	/* error */
		return;

	if (!pcc->current_select) {
		pcc->current_select = (SelectInfo *)malloc(sizeof(SelectInfo));
		pcc->current_select->hw = (Widget)hw;
		pcc->current_select->mptr = *mptr;
		pcc->current_select->option_cnt = 0;
		pcc->current_select->returns = NULL;
		pcc->current_select->retval_buf = NULL;
		pcc->current_select->options = NULL;
		pcc->current_select->option_buf = NULL;
		pcc->current_select->value_cnt = 0;
		pcc->current_select->value = NULL;
		pcc->current_select->is_value = -1;
		pcc->ignore = 1;
	}                      
	pcc->in_select = True;
}

void FormSelectEnd(HTMLWidget hw, MarkInfo **mptr, PhotoComposeContext *pcc)
{
	MarkInfo *mark = *mptr;
	char *start;
	int len;  
	char *buf;  
	char *options, *returns, *value;

	if (!pcc->in_form || !pcc->cur_form || !pcc->current_select)
		return;

        if (pcc->current_select->option_buf)
		ProcessOption(pcc->current_select);
	options = ComposeCommaList(pcc->current_select->options,
			pcc->current_select->option_cnt);
	returns = ComposeCommaList(pcc->current_select->returns,
			pcc->current_select->option_cnt);
	value = ComposeCommaList(pcc->current_select->value,
			pcc->current_select->value_cnt);
	FreeCommaList(pcc->current_select->options,
			pcc->current_select->option_cnt);   
	FreeCommaList(pcc->current_select->returns,
			pcc->current_select->option_cnt);
	FreeCommaList(pcc->current_select->value,
			pcc->current_select->value_cnt);
	/* Construct a fake INPUT tag. */
	len = strlen(MT_INPUT) + strlen(options) +
		strlen(returns) + strlen(value) + 
		strlen(" type=select options=\"\" returns=\"\" value=\"\"");
	buf = (char *)malloc(len + strlen(pcc->current_select->mptr->start) +1);
	strcpy(buf, MT_INPUT);
	strcat(buf, " type=select");
	strcat(buf, " options=\"");
	strcat(buf, options);
	strcat(buf, "\" returns=\"");
	strcat(buf, returns);
	strcat(buf, "\" value=\"");
	strcat(buf, value);
	strcat(buf, "\"");  
	strcat(buf, (char *) (pcc->current_select->mptr->start +
		strlen(MT_SELECT)));
	/* Stick the fake in, saving the real one. */
	start = pcc->current_select->mptr->start;
	pcc->current_select->mptr->start = buf;
	WidgetPlace(hw, pcc->current_select->mptr, pcc);
	/* Free the fake, put the original back */
	free(buf);
	free(options);
	free(returns);
	free(value);
	pcc->current_select->mptr->start = start;
	free((char *)pcc->current_select);
	pcc->current_select = NULL;
	pcc->ignore = 0;
	pcc->in_select = False;
}

/* We've just terminated the current OPTION.
 * Put it in the proper place in the SelectInfo structure.
 * Move option_buf into options, and maybe copy into
 * value if is_value is set.    
 */     
static void ProcessOption( SelectInfo *sptr)
{       
        int i, cnt;             
        char **tarray;          
        
        clean_white_space(sptr->option_buf);
        tarray = sptr->options;
        cnt = sptr->option_cnt + 1;
        sptr->options = (char **)malloc(sizeof(char *) * cnt);
        for (i=0; i < (cnt - 1); i++)
                sptr->options[i] = tarray[i];
        if (tarray)
                free((char *)tarray);
        sptr->options[cnt - 1] = sptr->option_buf;
        sptr->option_cnt = cnt;
        tarray = sptr->returns;
        cnt = sptr->option_cnt;
        sptr->returns = (char **)malloc(sizeof(char *) * cnt);
        for (i=0; i < (cnt - 1); i++)
                sptr->returns[i] = tarray[i];
        if (tarray)
                free((char *)tarray);
	/* Use text string if no VALUE specified */
	if (sptr->retval_buf)
	        sptr->returns[cnt - 1] = sptr->retval_buf;
	else
		sptr->returns[cnt - 1] = sptr->option_buf;
        if (sptr->is_value) {
                tarray = sptr->value;
                cnt = sptr->value_cnt + 1;
                sptr->value = (char **)malloc(sizeof(char *) * cnt);
                for (i=0; i < (cnt - 1); i++)
                        sptr->value[i] = tarray[i];
                if (tarray)
                        free((char *)tarray); 
                sptr->value[cnt - 1] = (char *)malloc(
			strlen(sptr->option_buf) + 1);
                strcpy(sptr->value[cnt - 1], sptr->option_buf);
                sptr->value_cnt = cnt;
        }       
}        
