/*
 * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.search.ui;

import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.util.Traversal;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormModel;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.parameters.ParameterData;
import com.arsdigita.xml.Element;
import com.arsdigita.util.Assert;

import com.arsdigita.globalization.GlobalizedMessage;

import com.arsdigita.search.QuerySpecification;
import com.arsdigita.search.FilterSpecification;
import com.arsdigita.search.Search;

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;

import org.apache.log4j.Logger;


/**
 * This is a simple extension of the QueryComponent that
 * provides management of the 'terms' parameter and uses 
 * FilterGenerators to populate a query specification
 * <p>
 * Typical use would be as follows:
 *<pre>
 * Form f = new Form("search");
 * BaseQueryComponent q = new BaseQueryComponent();
 * q.add(new ObjectTypeFilterComponent("com.arsdigita.kernel.User");
 * q.add(new PermissionGenerator(PrivilegeDescriptor.READ));
 * q.add(new Submit("Go"));
 * f.add(q);
 *</pre>
 */
public class BaseQueryComponent extends QueryComponent {

    private static final Logger s_log =
        Logger.getLogger(BaseQueryComponent.class);
    
    private Set m_filters;
    private Form m_form;
    private StringParameter m_terms = new StringParameter("terms");
    
    /**
     * Creates a new query component
     */
    public BaseQueryComponent() {
        super("query");
        m_filters = new HashSet();
    }
    
    public void register(Page p) {
        super.register(p);
        
        findFilters(m_filters);
    }
    
    public void register(Form form, FormModel model) {
        if (s_log.isDebugEnabled()) {
            s_log.debug("Adding " + m_terms.getName() + " to form model");
        }
        m_terms.setPassIn(true);
        model.addFormParam(m_terms);
        m_form = form;
    }
        
    /**
     * Gets the current search terms
     */
    protected String getTerms(PageState state) {
        FormData fd = m_form.getFormData(state);
        if (fd != null) {
            ParameterData data = fd.getParameter(m_terms.getName());
            return (String)data.getValue();
        }
        return null;
    }
    
    protected FilterSpecification[] getFilters(PageState state) {
        FilterSpecification[] filters = new FilterSpecification[m_filters.size()];
        
        Iterator i = m_filters.iterator();
        int c = 0;
        while (i.hasNext()) {
            FilterGenerator filter = (FilterGenerator)i.next();
            filters[c++] = filter.getFilter(state);
        }

        return filters;
    }


    public void generateXML(PageState state,
                            Element parent) {
        Element content = generateParent(parent);
        
        Element terms = Search.newElement("terms");
        terms.addAttribute("param", m_terms.getName());
        terms.addAttribute("value", getTerms(state));
        generateErrorXML(state, terms);
        content.addContent(terms);

        generateChildrenXML(state, content);
    }

    protected void generateErrorXML(PageState state,
                                    Element parent) {
        FormData f = m_form.getFormData(state);
        if (f == null) {
            return;
        }
        Iterator i = f.getErrors(m_terms.getName());

        while (i.hasNext()) {
            Element error = Search.newElement("error");
            error.setText(
                (String) ((GlobalizedMessage) i.next()).localize(state.getRequest())
            );
            parent.addContent(error);
        }
    }

    protected void findFilters(Set filters) {
        FilterTraversal trav = new FilterTraversal(filters);
        trav.preorder(this);
    }
    
    private class FilterTraversal extends Traversal {
        private Set m_filters;
        
        public FilterTraversal(Set filters) {
            m_filters = filters;
        }

        public void act(Component c) {
            if (c instanceof FilterGenerator) {
                m_filters.add(c);
            }
        }
    }
}
