/*
 * $Revision: 1.5 $ $Date: 2005/11/03 09:41:48 $
 * Copyright (C) 2004-2005 SUGIMOTO Ken-ichi
 * 作成日: 2004/10/9
 */
package feat.v1.view;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import feat.v1.ContextAccessor;
import feat.v1.ContextAttributeAccessException;
import feat.v1.HTTPSessionException;
import feat.v1.View;
import feat.v1.template.HTMLDocument;
import feat.v1.template.HTMLElement;
import feat.v1.template.HTMLNode;
import feat.v1.template.HTMLNodeList;
import feat.v1.template.NodeNotFoundException;
import feat.v1.template.NodeSelector;
import feat.v1.template.NodeTreeIterator;

/**
 * フォームに値を自動入力するビュー。
 * @author SUGIMOTO Ken-ichi
 */
public class FillFormView extends View {

    private static Log log = LogFactory.getLog(FillFormView.class);

    /*private String formId;
    private String[] names;*/

    public FillFormView() {
    }

    /*public FillFormView(String formId, String list) {
        this.formId = formId;
        names = Util.parseList(list);
    }*/

    public HTMLDocument format(HTMLDocument template, String[] params, ContextAccessor acc) throws NodeNotFoundException {
        if ( params.length < 2 || params[0] == null || params[1] == null ) {
            log.error("引数が不足している");
            return template;
        }

        String formId = params[0];
        String objectPath = params[1];

        // フォームに入れる値が入っているオブジェクトを取り出す
        Map valueMap;
        try {
            valueMap = acc.getMappedValue(objectPath);
        }
        catch (ContextAttributeAccessException ex) {
            log.error(ex);
            return template;
        }
        catch (HTTPSessionException ex) {
            log.error(ex);
            return template;
        }

        // フォームのノード
        HTMLElement form = template.findElement(formId);

        // フォームからコントロールのノードを探してMapに入れる
        Map controlMap = selectControlElement(form, new HashMap());

        // コントロールに値を入れる
        for(Iterator it = controlMap.keySet().iterator(); it.hasNext(); ) {
            String controlName = (String)it.next();
            Object o = controlMap.get(controlName);
            String[] values = null;
            if ( objectPath.equals("param") ) {
                values = acc.getParameterValues(controlName);
            }
            else {
                Object v = valueMap.get(controlName);
                if ( v != null )
                    values = new String[] {v.toString()};
            }

            // コントロールに対応した値があったらセットする
            if ( values != null ) {
                if ( o instanceof HTMLElement ) {
                    HTMLElement tag = (HTMLElement)o;
                    String tagName = tag.getTagName();
                    if ( tagName.equalsIgnoreCase("input") ) {
                        fillInput(tag, values);
                    }
                    else if ( tagName.equalsIgnoreCase("textarea") ) {
                        fillTextarea(tag, values);
                    }
                    else if ( tagName.equalsIgnoreCase("select") ) {
                        fillSelect(tag, values);
                    }
                }
                // ラジオボタン(type="radio")は一つの名前に複数の
                // input要素が対応するので、リストに入っている
                else if ( o instanceof List ) {
                    fillRadio((List)o, values);
                }
            }
        }
        return template;
    }

    private void fillInput(HTMLElement inputTag, String[] values) {
        String type = inputTag.getAttribute("type");

        if ( type.equalsIgnoreCase("text") || type.equalsIgnoreCase("file") ||
             type.equalsIgnoreCase("password") ) {
            if ( values != null && values.length > 0 )
                inputTag.setAttribute("value", values[0]);
            else
                inputTag.setAttribute("value", "");
        }

        else if ( type.equalsIgnoreCase("checkbox") ) {
            if ( values != null && values.length > 0 )
                inputTag.setAttribute("checked", null);
            else
                inputTag.removeAttribute("checked");
        }
    }

    private void fillRadio(List list, String[] values) {
        if ( values != null && values.length > 0 )
            for(Iterator it = list.iterator(); it.hasNext(); ) {
                HTMLElement radio = (HTMLElement)it.next();
                String radioValue = radio.getAttribute("value");
                if ( radioValue != null && radioValue.equals(values[0]) )
                    radio.setAttribute("checked", null);
            }
    }

    private void fillTextarea(HTMLElement textAreaTag, String[] values) {
        if ( values != null && values.length > 0 ) {
            textAreaTag.setText(values[0]);
        }
    }

    private void fillSelect(HTMLElement selectTag, String[] values) {
        Map optionMap = selectOptionElement(selectTag, new HashMap());

        // selected属性をクリアする
        for(Iterator it=optionMap.keySet().iterator(); it.hasNext(); ) {
            HTMLElement option = (HTMLElement)optionMap.get(it.next());
            option.removeAttribute("selected");
        }

        if ( values != null ) {
            for(int i=0; i<values.length; i++) {
                HTMLElement option = (HTMLElement)optionMap.get(values[i]);
                if ( option != null )
                    option.setAttribute("selected", null);
            }
        }
    }

    private Map selectControlElement(HTMLNode formElem, Map result) {
        NodeTreeIterator it = new NodeTreeIterator(formElem);
        while(it.hasNext()) {
            HTMLNode node = it.nextNode();
            // ノードがフォームのコントロールだったら、
            if ( node instanceof HTMLElement ) {
                HTMLElement tag = (HTMLElement)node;
                String tagName = tag.getTagName();
                if ( tagName.equalsIgnoreCase("input") || tagName.equalsIgnoreCase("textarea") ||
                        tagName.equalsIgnoreCase("select") ) {

                    // コントロール名を取得
                    String name = tag.getAttribute("name");
                    if ( name != null ) {
                        // type="radio"のときだけリストに入れるてマップに入れる
                        String type = tag.getAttribute("type");
                        if ( type != null && type.equalsIgnoreCase("radio") ) {
                            Object o = result.get(name);
                            if ( o == null ) {
                                o = new ArrayList();
                                result.put(name, o);
                            }
                            ((ArrayList)o).add(tag);
                        }
                        else
                            // それ以外のコントロールはそのままマップに入れる
                            result.put(name, tag);
                    }

                }
            }
        }

        /*for(HTMLNode node = formElem; node != null; node = node.getNext()) {
            if ( node instanceof HTMLElement ) {
                HTMLElement tag = (HTMLElement)node;
                String tagName = tag.getTagName();
                if ( tagName.equalsIgnoreCase("input") || tagName.equalsIgnoreCase("textarea") ||
                     tagName.equalsIgnoreCase("select") ) {
                    String name = tag.getAttribute("name");
                    if ( name != null ) {
                        // type="radio"のときだけリストに入れる
                        String type = tag.getAttribute("type");
                        if ( type != null && type.equalsIgnoreCase("radio") ) {
                            Object o = result.get(name);
                            if ( o == null ) {
                                o = new ArrayList();
                                result.put(name, o);
                            }
                            ((ArrayList)o).add(tag);
                        }
                        else
                            result.put(name, tag);
                    }
                }
                if ( tag.getFirstChild() != null ) {
                    selectControlElement(tag.getFirstChild(), result);
                }
            }
        }*/
        return result;
    }

    private Map selectOptionElement(HTMLNode selectElem, Map result) {
        HTMLNodeList list = NodeSelector.selectTags(new NodeTreeIterator(selectElem), "option");
        for(int i=0; i<list.size(); i++) {
            HTMLElement tag = (HTMLElement)list.get(i);
            // value属性があればその値をキーにする
            String value = tag.getAttribute("value");
            if ( value != null )
                result.put(value, tag);
            else {
                // value属性がなければテキストをキーにする
                String text = tag.getString().trim();
                if ( text.length() > 0 ) {
                    result.put(text, tag);
                }

            }
        }

        /*for(HTMLNode node = selectElem; node != null; node = node.getNext()) {
            if ( node instanceof HTMLElement ) {
                HTMLElement tag = (HTMLElement)node;
                String tagName = tag.getTagName();
                if ( tagName.equalsIgnoreCase("option") ) {
                    // value属性があればその値をキーにする
                    String value = tag.getAttribute("value");
                    if ( value != null )
                        result.put(value, tag);
                    else {
                        // value属性がなければテキストをキーにする
                        String text = tag.getString().trim();
                        if ( text.length() > 0 ) {
                            result.put(text, tag);
                        }

                    }
                }
                if ( tag.getFirstChild() != null ) {
                    selectOptionElement(tag.getFirstChild(), result);
                }
            }
        }*/
        return result;
    }

}
