/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/**
 * Todo: - provide filters in some way? Instead of just getting some bit of data also provide a way to feed that data
 * through some other object that will reformat it in some way (like converting dates to another format) - update
 * documentation, HTML column attributes are not set. - specify groupings. - error checking, value or property must be
 * set.
 */

package org.rhq.enterprise.gui.legacy.taglib.display;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;

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

/**
 * This tag works hand in hand with the SmartListTag to display a list of objects. This describes a column of data in
 * the SmartListTag. There can be any (reasonable) number of columns that make up the list.
 *
 * <p>This tag does no work itself, it is simply a container of information. The TableTag does all the work based on the
 * information provided in the attributes of this tag.
 *
 * <p>Usage:
 *
 * <p><display:column property="title" title="College Title" width="33%" href="/osiris/pubs/college/edit.page"
 * paramId="OID" paramProperty="OID" /> Attributes:
 *
 * <p>property - the property method that is called to retrieve the information to be displayed in this column. This
 * method is called on the current object in the iteration for the given row. The property format is in typical struts
 * format for properties (required) showHideProperty - the property that is called to retrieve the information to be
 * displayed as a hidden layer. The data in this property will be displayed beneath the data specified in the property
 * field when the show/hide buttons are manipulated. title - the title displayed for this column. if this is omitted
 * then the property name is used for the title of the column (optional) nulls - by default, null values don't appear in
 * the list, by setting viewNulls to 'true', then null values will appear as "null" in the list (mostly useful for
 * debugging) (optional) width - the width of the column (gets passed along to the html td tag). (optional) group - the
 * grouping level (starting at 1 and incrementing) of this column (indicates if successive contain the same values, then
 * they should not be displayed). The level indicates that if a lower level no longer matches, then the matching for
 * this higher level should start over as well. If this attribute is not included, then no grouping is performed.
 * (optional) decorator - a class that should be used to "decorate" the underlying object being displayed. If a
 * decorator is specified for the entire table, then this decorator will decorate that decorator. (optional) autolink -
 * if set to true, then any email addresses and URLs found in the content of the column are automatically converted into
 * a hypertext link. href - if this attribute is provided, then the data that is shown for this column is wrapped inside
 * a <a href>tag with the url provided through this attribute. Typically you would use this attribute along with one of
 * the struts-like param attributes below to create a dynamic link so that each row creates a different URL based on the
 * data that is being viewed. (optional) paramId - The name of the request parameter that will be dynamically added to
 * the generated href URL. The corresponding value is defined by the paramProperty and (optional) paramName attributes,
 * optionally scoped by the paramScope attribute. (optional) paramName - The name of a JSP bean that is a String
 * containing the value for the request parameter named by paramId (if paramProperty is not specified), or a JSP bean
 * whose property getter is called to return a String (if paramProperty is specified). The JSP bean is constrained to
 * the bean scope specified by the paramScope property, if it is specified. If paramName is omitted, then it is assumed
 * that the current object being iterated on is the target bean. (optional) paramProperty - The name of a property of
 * the bean specified by the paramName attribute (or the current object being iterated on if paramName is not provided),
 * whose return value must be a String containing the value of the request parameter (named by the paramId attribute)
 * that will be dynamically added to this href URL. (optional) paramScope - The scope within which to search for the
 * bean specified by the paramName attribute. If not specified, all scopes are searched. If paramName is not provided,
 * then the current object being iterated on is assumed to be the target bean. (optional) maxLength - If this attribute
 * is provided, then the column's displayed is limited to this number of characters. An elipse (...) is appended to the
 * end if this column is linked, and the user can mouseover the elipse to get the full text. (optional) maxWords - If
 * this attribute is provided, then the column's displayed is limited to this number of words. An elipse (...) is
 * appended to the end if this column is linked, and the user can mouseover the elipse to get the full text. (optional)
 */

public class ColumnTag extends BodyTagSupport implements Cloneable {
    private final Log log = LogFactory.getLog(ColumnTag.class.getName());

    private String property;
    private String showHideProperty;
    private String title;
    private String nulls;
    private String sort;
    private String autolink;
    private String group;

    private boolean isShowHideColumn;

    private String href;
    private String paramId;
    private String paramName;
    private String paramProperty;
    private String paramScope;
    private int maxLength;
    private int maxWords;

    private String width;
    private String align;
    private String background;
    private String bgcolor;
    private String height;
    private String nowrap;
    private String valign;
    private String clazz;
    private String headerClazz;
    private String onClick;

    private Object value;

    private String doubleQuote;

    private ColumnDecorator decorator;

    // -------------------------------------------------------- Accessor methods

    public void setProperty(String v) {
        this.property = v;
    }

    public String getOnClick() {
        return onClick;
    }

    public void setOnClick(String onClick) {
        this.onClick = onClick;
    }

    public void setShowHideProperty(String v) {
        this.showHideProperty = v;
    }

    public void setTitle(String v) {
        this.title = v;
    }

    public void setNulls(String v) {
        this.nulls = v;
    }

    public void setSort(String v) {
        this.sort = v;
    }

    public void setAutolink(String v) {
        this.autolink = v;
    }

    public void setGroup(String v) {
        this.group = v;
    }

    public void setIsShowHideColumn(boolean v) {
        this.isShowHideColumn = v;
    }

    public void setHref(String v) {
        this.href = v;
    }

    public void setParamId(String v) {
        this.paramId = v;
    }

    public void setParamName(String v) {
        this.paramName = v;
    }

    public void setParamProperty(String v) {
        this.paramProperty = v;
    }

    public void setParamScope(String v) {
        this.paramScope = v;
    }

    public void setMaxLength(int v) {
        this.maxLength = v;
    }

    public void setMaxWords(int v) {
        this.maxWords = v;
    }

    public void setWidth(String v) {
        this.width = v;
    }

    public void setAlign(String v) {
        this.align = v;
    }

    public void setBackground(String v) {
        this.background = v;
    }

    public void setBgcolor(String v) {
        this.bgcolor = v;
    }

    public void setHeight(String v) {
        this.height = v;
    }

    public void setNowrap(String v) {
        this.nowrap = v;
    }

    public void setValign(String v) {
        this.valign = v;
    }

    public void setStyleClass(String v) {
        this.clazz = v;
    }

    public void setHeaderStyleClass(String v) {
        this.headerClazz = v;
    }

    public void setValue(Object v) {
        this.value = v;
    }

    public void setDoubleQuote(String v) {
        this.doubleQuote = v;
    }

    public void setDecorator(ColumnDecorator v) {
        this.decorator = v;
    }

    public String getProperty() {
        return this.property;
    }

    public String getShowHideProperty() {
        return this.showHideProperty;
    }

    public String getTitle() {
        return this.title;
    }

    public String getNulls() {
        return this.nulls;
    }

    public String getSort() {
        return this.sort;
    }

    public String getAutolink() {
        return this.autolink;
    }

    public String getGroup() {
        return this.group;
    }

    public boolean getIsShowHideColumn() {
        return this.isShowHideColumn;
    }

    public String getHref() {
        return this.href;
    }

    public String getParamId() {
        return this.paramId;
    }

    public String getParamName() {
        return this.paramName;
    }

    public String getParamProperty() {
        return this.paramProperty;
    }

    public String getParamScope() {
        return this.paramScope;
    }

    public int getMaxLength() {
        return this.maxLength;
    }

    public int getMaxWords() {
        return this.maxWords;
    }

    public String getWidth() {
        return this.width;
    }

    public String getAlign() {
        return this.align;
    }

    public String getBackground() {
        return this.background;
    }

    public String getBgcolor() {
        return this.bgcolor;
    }

    public String getHeight() {
        return this.height;
    }

    public String getNowrap() {
        return this.nowrap;
    }

    public String getValign() {
        return this.valign;
    }

    public String getStyleClass() {
        return this.clazz;
    }

    public String getHeaderStyleClass() {
        return this.headerClazz;
    }

    public Object getValue() {
        return this.value;
    }

    public String getDoubleQuote() {
        return this.doubleQuote;
    }

    public ColumnDecorator getDecorator() {
        return this.decorator;
    }

    public String getDecoratorClass() {
        return this.decorator.getClass().getName();
    }

    public void setDecoratorClass(String decoratorClass) throws JspException {
        try {
            setDecorator((ColumnDecorator) Class.forName(decoratorClass).newInstance());
        } catch (Exception e) {
            throw new JspException(e);
        }
    }

    // --------------------------------------------------------- Tag API methods

    /**
     * Passes attribute information up to the parent TableTag.
     *
     * <p>When we hit the end of the tag, we simply let our parent (which better be a TableTag) know what the user wants
     * to do with this column. We do that by simple registering this tag with the parent. This tag's only job is to hold
     * the configuration information to describe this particular column. The TableTag does all the work.
     *
     * @throws JspException if this tag is being used outside of a <display:list...> tag.
     */

    @Override
    public int doEndTag() throws JspException {
        Class clazz = TableTag.class;
        Object parent = findAncestorWithClass(this, clazz);

        if (parent == null) {
            throw new JspException("Can not use column tag outside of a " + "TableTag. Invalid parent = null");
        }

        if (!(parent instanceof TableTag)) {
            throw new JspException("Can not use column tag outside of a " + "TableTag. Invalid parent = "
                + parent.getClass().getName());
        }

        // Need to clone the ColumnTag before passing it to the TableTag as
        // the ColumnTags can be reused by some containers, and since we are
        // using the ColumnTags as basically containers of data, we need to
        // save the original values, and not the values that are being changed
        // as the tag is being reused...

        ColumnTag copy;
        try {
            copy = (ColumnTag) this.clone();
        } catch (CloneNotSupportedException e) {
            log.debug("clone exception ", e);
            throw new JspException("could not clone: ", e);
        }

        this.release();
        ((TableTag) parent).addColumn(copy);

        return super.doEndTag();
    }

    @Override
    public int doStartTag() throws JspException {
        return (EVAL_BODY_INCLUDE);
    }

    /**
     * called by the jsp framework to release state.
     */
    @Override
    public void release() {
        decorator = null;
        super.release();
    }

    /**
     * Takes all the column pass-through arguments and bundles them up as a string that gets tacked on to the end of the
     * td tag declaration.
     *
     * @return the td tag attributes
     * @throws JspException 
     */
    protected String getCellAttributes() throws JspException {
        StringBuffer results = new StringBuffer();

        if (this.clazz != null) {
            results.append(" class=\"");
            results.append(this.clazz);
            results.append("\"");
        } else {
            results.append(" class=\"tableCell\"");
        }

        if (this.width != null) {
            results.append(" width=\"");
            results.append(this.width);
            results.append("\"");
        }

        if (this.align != null) {
            results.append(" align=\"");
            results.append(this.align);
            results.append("\"");
        } else {
            results.append(" align=\"left\"");
        }

        if (this.background != null) {
            results.append(" background=\"");
            results.append(this.background);
            results.append("\"");
        }

        if (this.bgcolor != null) {
            results.append(" bgcolor=\"");
            results.append(this.bgcolor);
            results.append("\"");
        }

        if (this.height != null) {
            results.append(" height=\"");
            results.append(this.height);
            results.append("\"");
        }

        if (this.nowrap != null) {
            results.append(" nowrap");
        }

        if (this.valign != null) {
            results.append(" valign=\"");
            results.append(this.valign);
            results.append("\"");
        } else {
            results.append(" valign=\"top\"");
        }
        if (this.onClick != null) {
            results.append(" onclick=\"");

            //            TableTag tt = (TableTag) getParent();
            //            int i = onClick.indexOf("_property:");
            //            if (i >= 0) {
            //                int pos = onClick.indexOf(":", i);
            //                int pos2 = onClick.indexOf(":", pos + 1);
            //                // TODO add some error checking
            //                String prop = onClick.substring(pos + 1, pos2);
            //                Object o = tt.lookup(pageContext, "smartRow", prop, null, true);
            //                String rep = o.toString();
            //                oc = onClick.replace("_property:" + prop + ":", rep);
            //                results.append(oc);
            //            } else {
            //                results.append(onClick);
            //            }
            results.append(replacePropertyIfPossible(onClick, pageContext));
            results.append("\"");
        }

        return results.toString();
    }

    public String replacePropertyIfPossible(String input, PageContext pageContext) {
        int i = input.indexOf("_property:");
        if (i >= 0) {
            String oc = input;
            TableTag tt = (TableTag) getParent();
            int pos = input.indexOf(":", i);
            int pos2 = input.indexOf(":", pos + 1);
            // TODO add some error checking
            String prop = input.substring(pos + 1, pos2);

            String rep;
            try {
                Object o = tt.lookup(pageContext, "smartRow", prop, null, true);
                rep = o.toString();
                oc = input.replace("_property:" + prop + ":", rep);
            } catch (JspException e) {
                log.warn("Can't replace the _property on " + input + ": " + e.getMessage());
            }
            return oc;
        } else
            return input;

    }

    /**
     * Returns a String representation of this Tag that is suitable for printing while debugging. Where the placeholders
     * in brackets are replaced with their appropriate instance variables
     */
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer("ColumnTag(");
        sb.append("title=").append(title);
        sb.append(",property=").append(property);
        sb.append(",href=").append(href);
        sb.append(",decorator=").append(decorator);
        sb.append(")");
        return sb.toString();
    }
}