/*
 * $Id: Form.java 220 2007-07-16 10:32:15Z sugimotokenichi $
 * Copyright (C) 2005 SUGIMOTO Ken-ichi
 * 作成日: 2005/12/06
 */
package feat2.template.form;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import feat2.template.HTMLElement;
import feat2.template.HTMLNodeList;
import feat2.template.NodeSelector;
import feat2.template.NodeTreeIterator;

/**
 * フォームを簡単に扱うためのクラス。
 * フォーム中のOBJECT要素はサポートしない。
 * @author SUGIMOTO Ken-ichi
 */
public class Form {

    private HTMLElement formElement;

    public Form(HTMLElement formElement) {
        this.formElement = formElement;
    }

    /**
     * フォームのコントロール要素を返す。
     * @param name
     * @return nameで指定されたフォームのコントロール。見つからなかったときは空の配列
     */
    public FormControl getControlElement(String name) {

        // name属性で検索
        HTMLNodeList result = NodeSelector.selectElementsByAttribute(new NodeTreeIterator(formElement), "name", name);

        // フォームコントロールを抽出
        ArrayList list = new ArrayList();
        for(int i=0; i<result.size(); i++) {
            HTMLElement node = (HTMLElement)result.get(i);
            String tagName = node.getTagName();
            if ( tagName != null && (
                tagName.equalsIgnoreCase("input") ||
                tagName.equalsIgnoreCase("button") ||
                tagName.equalsIgnoreCase("select") ||
                tagName.equalsIgnoreCase("textarea") ) ) {

                list.add(node);
            }
        }

        // 検索した要素からフォームコントロールオブジェクトを作る
        FormControl ret = null;
        if ( list.size() > 0 ) {
            HTMLElement[] elements = (HTMLElement[])list.toArray(new HTMLElement[list.size()]);
            String tagName = elements[0].getTagName();
            if ( tagName != null ) {

                if ( tagName.equalsIgnoreCase("input") ) {

                    String type = elements[0].getAttribute("type");

                    // 単数のエレメント
                    if ( type.equalsIgnoreCase("text") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_TEXT);
                    }
                    else if ( type.equalsIgnoreCase("password") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_PASSWORD);
                    }
                    else if ( type.equalsIgnoreCase("submit") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_SUBMIT);
                    }
                    else if ( type.equalsIgnoreCase("reset") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_RESET);
                    }
                    else if ( type.equalsIgnoreCase("file") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_FILE);
                    }
                    else if ( type.equalsIgnoreCase("hidden") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_HIDDEN);
                    }
                    else if ( type.equalsIgnoreCase("image") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_IMAGE);
                    }
                    else if ( type.equalsIgnoreCase("button") ) {
                        ret = new SimpleControl(elements[0], FormControl.TYPE_BUTTON);
                    }

                    // ラジオボタン
                    else if ( type.equalsIgnoreCase("radio") ) {
                        ret = new RadioButton(elements);
                    }

                    // チェックボックス
                    else if ( type.equalsIgnoreCase("checkbox") ) {
                        ret = new CheckBox(elements);
                    }

                }

                else if ( tagName.equalsIgnoreCase("button") ) {
                    ret = new SimpleControl(elements[0], FormControl.TYPE_BUTTON_ELEMENT);
                }

                else if ( tagName.equalsIgnoreCase("select") ) {
                    ret = new SelectControl(elements[0]);
                }

                else if ( tagName.equalsIgnoreCase("textarea") ) {
                    ret = new TextArea(elements[0]);
                }
            }
        }

        return ret;
    }

    /**
     * フォームのコントロールから値を取得する。
     * 要素から取得する値の以下のとおり。<br>
     * テキスト、ボタン、HIDDEN要素: value属性値<br>
     * テキストエリア: テキスト要素<br>
     * チェックボックス: checked属性のついた要素のvalue属性値<br>
     * ラジオボタン: checked属性のついた要素の属性値<br>
     * メニュー: selected属性のついたoption要素のvalue属性値、またはテキスト要素
     * @param name
     * @return コントロールが見つからなかったときはnullを返す
     */
    public String getValue(String name) {
        FormControl control = getControlElement(name);
        if ( control != null )
            return control.getValue();
        else
            return null;
    }

    /**
     * フォームのコントロールから値を取得する。
     * フォームの値が1つのときは要素数が1つの配列を返す。
     * @param name
     * @return
     */
    public String[] getValues(String name) {
        FormControl control = getControlElement(name);
        if ( control != null )
            return control.getValues();
        else
            return null;
    }

    /**
     * リクエストパラメータの値をフォームにセットする。
     * @param request
     */
    public void setValues(HttpServletRequest request) {
        Map params = request.getParameterMap();
        setValues(params);
    }

    public void setValues(Map map) {
        for(Iterator it = map.keySet().iterator(); it.hasNext(); ) {
            String key = (String)it.next();
            String[] values = (String[])map.get(key);
            FormControl control = getControlElement(key);
            if ( control != null ) {
                control.setValues(values);
            }
        }
    }

    /**
     * フォームのコントロールに値をセットする。
     * @param name
     * @param value
     */
    public void setValue(String name, String value) {
        FormControl control = getControlElement(name);
        if ( control != null )
            control.setValue(value);
    }

    /**
     * フォームのコントロールに値をセットする。
     * @param name
     * @param values
     */
    public void setValue(String name, String[] values) {
        FormControl control = getControlElement(name);
        if ( control != null )
            control.setValues(values);
    }

    /**
     * リストコントロールの選択肢を返す。
     * @param name リストコントロールの名前
     * @return リストコントロールの選択肢。リストコントロールがなかったらnull
     */
    public ListItem[] getOptions(String name) {
        FormControl c = getControlElement(name);
        if ( c != null && c instanceof SelectControl ) {
            SelectControl select = (SelectControl)c;
            return select.getItems();
        }
        else
            return null;
    }

    /**
     * リストコントロールの選択肢を設定する。
     * @param name リストコントロールの名前
     * @param options
     */
    public void setOptions(String name, ListItem[] options) {
        FormControl c = getControlElement(name);
        if ( c != null && c instanceof SelectControl ) {
            SelectControl select = (SelectControl)c;
            select.setItems(options);
        }
    }

    /**
     * リストコントロールの選択肢を追加する。
     * @param name リストコントロールの名前
     * @param option
     */
    public void addOption(String name, ListItem option) {
        FormControl c = getControlElement(name);
        if ( c != null && c instanceof SelectControl ) {
            SelectControl select = (SelectControl)c;
            select.addItem(option);
        }
    }

    /**
     * 指定のコントロールにラベルを設定する。
     * 指定の名前のコントロールにlabel要素がなかったら何もしない。
     * ただし、value属性がラベルの役割をするコントロール(INPUT要素のsubmit, reset, button)の場合はlabel要素ではなくコントロールのvalue属性を設定する。
     * label要素のfor属性で明示的にコントロールを指定していない場合はラベルを設定しない。
     * @param name
     * @param label
     */
    public void setLabel(String name, String label) {
        FormControl control = getControlElement(name);
        if ( control != null ) {

            // value属性をラベルに使うコントロール
            if ( (control.getType() & (FormControl.TYPE_BUTTON | FormControl.TYPE_RESET | FormControl.TYPE_SUBMIT)) != 0 ) {
                control.setValue(label);
                return;
            }

            // コントロールのIDに結び付けられたラベルを変更する
            HTMLElement element = control.getElement();
            if ( element != null ) {
                setLabel(element, label);
            }
        }
    }

    /**
     * 名前を共有するコントロールにラベルを設定する。
     * checkboxとradioのみ。
     * @param name
     * @param labels
     */
    public void setLabels(String name, String[] labels) {
        FormControl control = getControlElement(name);
        if ( control != null ) {

            HTMLElement[] elements = null;

            // 要素
            if ( control instanceof CheckBox ) {
                elements = ((CheckBox)control).getElements();
            }
            else if ( control instanceof RadioButton ) {
                elements = ((RadioButton)control).getElements();
            }

            // ラベルを設定
            if ( elements != null ) {
                int size = elements.length;

                if ( size > labels.length ) {
                    size = labels.length;
                }

                for(int i=0; i<size; i++) {
                    setLabel(elements[i], labels[i]);
                }
            }

        }
    }

    /**
     * 指定のフォームコントロールに対応したlabel要素に値を設定する。
     * @param element
     * @param label
     */
    private void setLabel(HTMLElement element, String label) {
        String id = element.getId();
        if ( id != null ) {
            HTMLElement labelElement = NodeSelector.getElementByAttribute(new NodeTreeIterator(formElement), "for", id);
            if ( labelElement != null ) {
                labelElement.setText(label);
                return;
            }
        }
    }
}
