///////////////////////////////
//  Makumba, Makumba tag library
//  Copyright (C) 2000-2003  http://www.makumba.org
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//  -------------
//  $Id: FormResponder.java 2366 2008-05-16 21:06:23Z rosso_nero $
//  $Name$
/////////////////////////////////////

package org.makumba.forms.responder;

import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.makumba.CompositeValidationException;
import org.makumba.DataDefinition;
import org.makumba.FieldDefinition;
import org.makumba.InvalidValueException;
import org.makumba.MakumbaSystem;
import org.makumba.Transaction;
import org.makumba.commons.DbConnectionProvider;
import org.makumba.commons.attributes.RequestAttributes;
import org.makumba.forms.html.FieldEditor;
import org.makumba.forms.html.RecordEditor;
import org.makumba.forms.validation.ClientsideValidationProvider;
import org.makumba.providers.DataDefinitionProvider;
import org.makumba.providers.TransactionProvider;
import org.makumba.providers.TransactionProviderInterface;

public class FormResponder extends Responder {

    private static final long serialVersionUID = 1L;

    RecordEditor editor;
    
    /**
     * reads the data submitted to the controller by http, also sets the values in the request so they can be retrieved
     * as attributes
     */
    public Dictionary getHttpData(HttpServletRequest req, String suffix) {
        if (editor != null)
            return editor.readFrom(req, suffix, !operation.equals("search"));
        else
            return new Hashtable(1);
    }

    @Override
    public ArrayList<InvalidValueException> getUnassignedExceptions(CompositeValidationException e, ArrayList<InvalidValueException> unassignedExceptions,
            String suffix) {
        if (editor != null)
            return editor.getUnassignedExceptions(e, unassignedExceptions, suffix);
        else
            return null;
    }

    Hashtable<String, Integer> indexes = new Hashtable<String, Integer>();

    DataDefinition dd = DataDefinitionProvider.getInstance().getVirtualDataDefinition("Form responder"); // TODO: more precise name

    int max = 0;

    Hashtable<String, Dictionary> fieldParameters = new Hashtable<String, Dictionary>();

    Hashtable<String, String> fieldNames = new Hashtable<String, String>();

    /** Format a field using the editor, and grow the editor as needed */
    public String format(String fname, FieldDefinition ftype, Object fval, Dictionary formatParams,
            String extraFormatting) {
        Dictionary paramCopy = (Dictionary) ((Hashtable) formatParams).clone();
        FieldEditor.setSuffix(paramCopy, storedSuffix);
        FieldEditor.setExtraFormatting(paramCopy, extraFormatting);

        boolean display = (formatParams.get("org.makumba.noDisplay") == null);
        Integer i = (Integer) indexes.get(fname);
        if (i != null)
            return display ? editor.format(i.intValue(), fval, paramCopy) : "";

        indexes.put(fname, new Integer(max));
        String colName = ("col" + max);
        fieldNames.put(colName, fname);
        fieldParameters.put(colName, formatParams);
        dd.addField(DataDefinitionProvider.getInstance().makeFieldWithName(colName, ftype));
        editor = new RecordEditor(dd, fieldNames, database);
        editor.config();
        // add client side validation, but only for edit operations (not search)
        if (!operation.equals("search") && org.makumba.commons.StringUtils.equalsAny(clientSideValidation, new String[] { "true", "live" })) {
            provider.initField(fname, ftype, clientSideValidation.equals("live"));
        }
        max++;
        return display ? editor.format(max - 1, fval, paramCopy) : "";
    }

    public String responderKey() {
        return "" + fieldNames + fieldParameters + super.responderKey();
    }

    protected String action;

    protected String method = "GET";

    protected boolean multipart;

    StringBuffer extraFormatting;

    private ClientsideValidationProvider provider = MakumbaSystem.getClientsideValidationProvider();

    public void setAction(String action) {
        this.action = action;
    }

    public String getAction() {
        return action;
    }

    public void setMultipart(boolean multipart) {
        this.multipart = multipart;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public void setExtraFormatting(StringBuffer extraFormatting) {
        this.extraFormatting = extraFormatting;
    }

    public void writeFormPreamble(StringBuffer sb, String basePointer) {
        if (!storedSuffix.equals(""))
            // no preamble for non-root forms (forms included in other forms)
            return;
        String sep = action.indexOf('?') >= 0 ? "&" : "?";
        // handle anchors in actions (bla.jsp?person=hg34bw#employment)
        String actionBase = action;
        String actionAnchor = "";
        int actionHashPos = action.indexOf('#');
        if (actionHashPos > -1) {
            actionBase = action.substring(0, actionHashPos);
            actionAnchor = action.substring(actionHashPos);
        }

        if (operation.equals("deleteLink")) {

            // a root deleteLink

            sb.append("<a href=\"").append(actionBase).append(sep).append(basePointerName).append("=").append(
                basePointer).append('&').append(responderName).append("=").append(getPrototype()).append(actionAnchor).append(
                "\" ").append(extraFormatting).append(">");

        }

        else if (operation.equals("deleteForm")) {
            sb.append("<form action=").append("\"").append(actionBase).append(actionAnchor).append("\"");

            sb.append(" method=").append("\"").append(method).append("\"");
            if (multipart) {
                sb.append(" enctype=\"multipart/form-data\" ");
            }
            sb.append(extraFormatting);
            sb.append(">");

            sb.append("<input type=\"submit\" ").append("value=\"");

        } else {
            // a root form, translates into an HTML form
            sb.append("<form action=");
            sb.append("\"" + action + "\"");
            sb.append(" method=");
            sb.append("\"" + method + "\"");
            if (multipart)
                sb.append(" enctype=\"multipart/form-data\" ");
            // if we do client side validation, we need to put an extra formatting parameter for onSubmit
            // but, do it only for edit operations (not search)
            if (!operation.equals("search")
                    && org.makumba.commons.StringUtils.equalsAny(clientSideValidation, new String[] { "true", "live" })) {
                StringBuffer onSubmitValidation = provider.getOnSubmitValidation(StringUtils.equals(
                    clientSideValidation, "live"));
                // we append it only if we actually have data
                if (onSubmitValidation != null && onSubmitValidation.length() > 0) {
                    sb.append(" onsubmit=\"return ");
                    sb.append(onSubmitValidation);
                    sb.append("\"");
                }
            }
            sb.append(extraFormatting);
            sb.append(">");
        }
    }

    public void writeFormPostamble(StringBuffer sb, String basePointer, HttpServletRequest request) {
        String session = request.getSession().getId();
        factory.setResponderWorkingDir(request);
        if (storedSuffix.equals("") && operation.equals("deleteLink")) {
            // a root deleteLink
            sb.append("</a>");
            return;
        } else if (storedSuffix.equals("") && operation.equals("deleteForm")) {
            sb.append("\"/>");
        }

        // writes the hidden fields

        if (basePointer != null)
            writeInput(sb, basePointerName, basePointer, storedSuffix);

        String responderValue = getResponderValue();
        String formSessionValue = responderValue + session; // gets the formSession value

        writeInput(sb, responderName, responderValue, "");
        if (multipleSubmitErrorMsg != null && !multipleSubmitErrorMsg.equals("")) {
            sb.append('\n');
            writeInput(sb, formSessionName, formSessionValue, "");

            // insert the formSession into the database
            Transaction db = ((DbConnectionProvider) request.getAttribute(RequestAttributes.PROVIDER_ATTRIBUTE)).getTransactionProvider().getConnectionTo(database);
            try {
                Dictionary<String, String> p = new Hashtable<String, String>();
                p.put("formSession", formSessionValue);
                db.insert("org.makumba.controller.MultipleSubmit", p);
            } finally {
                db.close();
            }
        }

        // write originating page if we reload the form, but only for edit operations (not search)
        if (!operation.equals("search") && reloadFormOnError) {
            String url = request.getRequestURI();
            String queryString = request.getQueryString();
            if (queryString != null) {
                if (queryString.indexOf(FormResponder.originatingPageName) > 0) {
                    queryString = queryString.substring(0, queryString.indexOf(FormResponder.originatingPageName) - 1);
                }
                url += "?" + queryString;
            }
            writeInput(sb, originatingPageName, url, "");
            sb.append('\n');
        }

	        if (storedSuffix.equals(""))
            // a root form
            sb.append("\n</form>");
    }

    public String getResponderValue() {
        return getPrototype() + storedSuffix + storedParentSuffix;
    }

    void writeInput(StringBuffer sb, String name, String value, String suffix) {
        sb.append("<input type=\"hidden\" name=\"").append(name).append(suffix).append("\" value=\"").append(value).append(
            "\">");
    }

    protected void postDeserializaton() {
        if (editor != null) {
            editor.initFormatters();
        }
    }

    public void writeClientsideValidation(StringBuffer sb) {
        sb.append(provider.getClientValidation(clientSideValidation.equals("live")));
    }

}
