/*
 * $Id: RedirectResponse.java 220 2007-07-16 10:32:15Z sugimotokenichi $
 * Copyright (C) 2006 SUGIMOTO Ken-ichi
 * 作成日: 2006/03/17
 */
package feat2.impl;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import feat2.CommandContext;
import feat2.FeatRuntimeException;
import feat2.FileUploadException;
import feat2.Processor;
import feat2.PropertyAccessException;
import feat2.Response;
import feat2.StringUtil;
import feat2.config.RedirectConfig;

/**
 * リダイレクト。
 * @author SUGIMOTO Ken-ichi
 */
public class RedirectResponse implements Response {

    private RedirectConfig config;


    public RedirectResponse(RedirectConfig config) {
        this.config = config;
    }


    public String output(CommandContext ctx)
            throws IOException, PropertyAccessException, FileUploadException {

        // 入力値

        Map inputs = Processor.processInputs(config, ctx);

        // リダイレクト

        if ( config.getMethod().equalsIgnoreCase("get") ) {

            // GETメソッドでリダイレクトする場合

            redirectGet(config.getUri(), inputs, ctx);

        }

        else if ( config.getMethod().equalsIgnoreCase("post") ) {

            // POSTメソッドでリダイレクトする場合

            redirectPost(config.getUri(), inputs, ctx);

        }

        else {

            // 設定ファイルのバリデーションで検査しているのでここへは来ないはず

            throw new FeatRuntimeException("");
        }

        return null;

    }


    public static void redirectGet(String uri, Map inputs, CommandContext ctx) throws UnsupportedEncodingException, IOException {

        String encoding = getEncoding(ctx);
        String query = null;
        StringBuffer queryBuf = new StringBuffer();

        if ( uri.indexOf('#') > -1 )
            uri = uri.substring(0, uri.indexOf('#'));

        if ( uri.indexOf('?') > -1 ) {
            query = uri.substring(uri.indexOf('?')+1);
            uri = uri.substring(0, uri.indexOf('?'));

            if ( query != null )
                queryBuf.append(query);
        }

        // クエリーを再構築

        ArrayList queryList = new ArrayList(inputs.size()*2);

        // 入力値をリストに追加

        for (Iterator keys = inputs.keySet().iterator(); keys.hasNext();) {
            String key = (String)keys.next();
            String encKey = URLEncoder.encode(key, encoding);
            String[] value = toStringArray(inputs.get(key));

            for (int i = 0; i < value.length; i++) {
                queryList.add(new String[] {encKey, URLEncoder.encode(value[i], encoding)});
            }
        }

        // リストからクエリー文字列に変換

        for(Iterator it=queryList.iterator(); it.hasNext(); ) {

            String[] pair = (String[])it.next();

            if ( queryBuf.length() > 0 ) {
                queryBuf.append('&');
            }

            queryBuf.append(pair[0]);
            queryBuf.append('=');

            if ( pair[1] != null && pair[1].length() > 0 ) {
                queryBuf.append(pair[1]);
            }

        }

        // リダイレクト

        String newUri = uri;
        if ( queryBuf.length() > 0 )
            newUri = newUri + "?" + queryBuf.toString();
        ctx.getResponse().sendRedirect(newUri);

    }


    public static void redirectPost(String uri, Map inputs, CommandContext ctx) throws IOException, UnsupportedEncodingException {

        String encoding = getEncoding(ctx);
        StringBuffer html = new StringBuffer();
        StringBuffer form = new StringBuffer();

        html.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" ");
        html.append("\"http://www.w3.org/TR/html4/strict.dtd\">\n");
        html.append("<html><head>");
        html.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
        html.append(encoding);
        html.append("\">\n");

        // フォームをsubmitするJavaScript

        html.append("<script type=\"text/javascript\">\n<!--\n");
        html.append("function submitForm() {\n");
        html.append("    document.forms[0].submit();\n");
        html.append("}\n//-->\n</script></head>\n");

        // フォーム

        form.append("<form name=\"form0\" method=\"post\" action=\"");
        form.append(uri);
        form.append("\">\n");

        for(Iterator it=inputs.keySet().iterator(); it.hasNext(); ) {

            String key = (String)it.next();
            String[] val = toStringArray(inputs.get(key));

            if ( val.length == 1 ) {

                form.append("<textarea name=\"").append(StringUtil.escapeHTMLText(key)).append("\">");
                form.append(StringUtil.escapeHTMLText(val[0]));
                form.append("</textarea>\n");

            }

            else if ( val.length > 1 ) {

                for (int i = 0; i < val.length; i++) {

                    // フォームの要素

                    form.append("<input type=\"checkbox\" name=\"");
                    form.append(StringUtil.escapeHTMLText(key));
                    form.append("\" value=\"");
                    form.append(StringUtil.escapeHTMLText(val[i]));
                    form.append("\" checked>\n");

                }

            }

        }

        form.append("</form>\n");

        // body要素

        html.append("<body onLoad=\"submitForm();\"><div style=\"display: none\">");
        html.append(form);
        html.append("</div></body></html>");

        // 出力

        ctx.getResponse().setContentType("text/html; charset="+encoding);
        OutputStream out = ctx.getResponse().getOutputStream();
        out.write(html.toString().getBytes(encoding)); // FIXME configで出力エンコーディングを指定できるようにする。指定なしのときはフィーチャーで設定されたエンコーディング
        out.flush();
        out.close();
    }


    /**
     * フィーチャーで指定されたエンコーディングを返す。
     * フィーチャーでエンコーディングが指定されていなかったらUTF-8を返す。
     * @param ctx
     * @return
     */
    private static String getEncoding(CommandContext ctx) {
        String encoding = ctx.getCurrentFeatureConfig().getEncoding();
        if ( encoding == null )
            encoding = "UTF-8";
        return encoding;
    }


    /**
     * オブジェクトを文字列の配列に変換する。
     * @param o
     * @return
     */
    private static String[] toStringArray(Object o) {

        String[] ret;

        if ( o == null )
            return new String[0];

        if ( o instanceof String[] ) {
            return (String[])o;
        }

        else if ( o.getClass().isArray() ) {

            Object[] array = (Object[])o;
            ret = new String[array.length];
            for (int i = 0; i < array.length; i++) {
                ret[i] = array[i].toString();
            }

        }

        else {
            ret = new String[] {o.toString()};
        }

        return ret;

    }
}
