package net.sf.jasperreports.engine.design;

import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.sf.jasperreports.crosstabs.JRCrosstab;
import net.sf.jasperreports.crosstabs.design.JRDesignCrosstab;
import net.sf.jasperreports.engine.JRBand;
import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRExpressionCollector;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JROrigin;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRReport;
import net.sf.jasperreports.engine.JRReportFont;
import net.sf.jasperreports.engine.JRReportTemplate;
import net.sf.jasperreports.engine.JRSortField;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRVariable;
import net.sf.jasperreports.engine.JRVisitor;
import net.sf.jasperreports.engine.base.JRBaseReport;
import net.sf.jasperreports.engine.design.events.PropagationChangeListener;
import net.sf.jasperreports.engine.util.JRElementsVisitor;
import net.sf.jasperreports.engine.util.JRVisitorSupport;

public class JasperDesign extends JRBaseReport {
  private static final long serialVersionUID = 10200L;
  
  public static final String PROPERTY_BACKGROUND = "background";
  
  public static final String PROPERTY_BOTTOM_MARGIN = "bottomMargin";
  
  public static final String PROPERTY_COLUMN_COUNT = "columnCount";
  
  public static final String PROPERTY_COLUMN_FOOTER = "columnFooter";
  
  public static final String PROPERTY_COLUMN_HEADER = "columnHeader";
  
  public static final String PROPERTY_COLUMN_SPACING = "columnSpacing";
  
  public static final String PROPERTY_COLUMN_WIDTH = "columnWidth";
  
  public static final String PROPERTY_DATASETS = "datasets";
  
  public static final String PROPERTY_DEFAULT_FONT = "defaultFont";
  
  public static final String PROPERTY_DEFAULT_STLYE = "defaultStyle";
  
  public static final String PROPERTY_DETAIL = "detail";
  
  public static final String PROPERTY_FLOAT_COLUMN_FOOTER = "floatColumnFooter";
  
  public static final String PROPERTY_FONTS = "fonts";
  
  public static final String PROPERTY_FORMAT_FACTORY_CLASS = "formatFactoryClass";
  
  public static final String PROPERTY_IGNORE_PAGINATION = "ignorePagination";
  
  public static final String PROPERTY_IMPORTS = "imports";
  
  public static final String PROPERTY_LANGUAGE = "language";
  
  public static final String PROPERTY_LAST_PAGE_FOOTER = "lastPageFooter";
  
  public static final String PROPERTY_LEFT_MARGIN = "leftMargin";
  
  public static final String PROPERTY_MAIN_DATASET = "mainDataset";
  
  public static final String PROPERTY_NAME = "name";
  
  public static final String PROPERTY_NO_DATA = "noData";
  
  public static final String PROPERTY_ORIENTATION = "orientation";
  
  public static final String PROPERTY_PAGE_FOOTER = "pageFooter";
  
  public static final String PROPERTY_PAGE_HEADER = "pageHeader";
  
  public static final String PROPERTY_PAGE_HEIGHT = "pageHeight";
  
  public static final String PROPERTY_PAGE_WIDTH = "pageWidth";
  
  public static final String PROPERTY_PRINT_ORDER = "printOrder";
  
  public static final String PROPERTY_RIGHT_MARGIN = "rightMargin";
  
  public static final String PROPERTY_STYLES = "styles";
  
  public static final String PROPERTY_SUMMARY = "summary";
  
  public static final String PROPERTY_SUMMARY_NEW_PAGE = "summaryNewPage";
  
  public static final String PROPERTY_TEMPLATES = "templates";
  
  public static final String PROPERTY_TITLE = "title";
  
  public static final String PROPERTY_TITLE_NEW_PAGE = "titleNewPage";
  
  public static final String PROPERTY_TOP_MARGIN = "topMargin";
  
  private List templateList = new ArrayList();
  
  private Map fontsMap = new HashMap();
  
  private List fontsList = new ArrayList();
  
  private Map stylesMap = new HashMap();
  
  private List stylesList = new ArrayList();
  
  private JRDesignDataset mainDesignDataset;
  
  private Map datasetMap = new HashMap();
  
  private List datasetList = new ArrayList();
  
  public JasperDesign() {
    setMainDataset(new JRDesignDataset(true));
  }
  
  public void setName(String name) {
    Object old = this.name;
    this.name = name;
    this.mainDesignDataset.setName(name);
    getEventSupport().firePropertyChange("name", old, this.name);
  }
  
  public void setLanguage(String language) {
    Object old = this.language;
    this.language = language;
    getEventSupport().firePropertyChange("language", old, this.language);
  }
  
  public void setColumnCount(int columnCount) {
    int old = this.columnCount;
    this.columnCount = columnCount;
    getEventSupport().firePropertyChange("columnCount", old, this.columnCount);
  }
  
  public void setPrintOrder(byte printOrder) {
    int old = this.printOrder;
    this.printOrder = printOrder;
    getEventSupport().firePropertyChange("printOrder", old, this.printOrder);
  }
  
  public void setPageWidth(int pageWidth) {
    int old = this.pageWidth;
    this.pageWidth = pageWidth;
    getEventSupport().firePropertyChange("pageWidth", old, this.pageWidth);
  }
  
  public void setPageHeight(int pageHeight) {
    int old = this.pageHeight;
    this.pageHeight = pageHeight;
    getEventSupport().firePropertyChange("pageHeight", old, this.pageHeight);
  }
  
  public void setOrientation(byte orientation) {
    int old = this.orientation;
    this.orientation = orientation;
    getEventSupport().firePropertyChange("orientation", old, this.orientation);
  }
  
  public void setColumnWidth(int columnWidth) {
    int old = columnWidth;
    this.columnWidth = columnWidth;
    getEventSupport().firePropertyChange("columnWidth", old, this.columnWidth);
  }
  
  public void setColumnSpacing(int columnSpacing) {
    int old = this.columnSpacing;
    this.columnSpacing = columnSpacing;
    getEventSupport().firePropertyChange("columnSpacing", old, this.columnSpacing);
  }
  
  public void setLeftMargin(int leftMargin) {
    int old = this.leftMargin;
    this.leftMargin = leftMargin;
    getEventSupport().firePropertyChange("leftMargin", old, this.leftMargin);
  }
  
  public void setRightMargin(int rightMargin) {
    int old = this.rightMargin;
    this.rightMargin = rightMargin;
    getEventSupport().firePropertyChange("rightMargin", old, this.rightMargin);
  }
  
  public void setTopMargin(int topMargin) {
    int old = this.topMargin;
    this.topMargin = topMargin;
    getEventSupport().firePropertyChange("topMargin", old, this.topMargin);
  }
  
  public void setBottomMargin(int bottomMargin) {
    int old = this.bottomMargin;
    this.bottomMargin = bottomMargin;
    getEventSupport().firePropertyChange("bottomMargin", old, this.bottomMargin);
  }
  
  public void setBackground(JRBand background) {
    Object old = this.background;
    this.background = background;
    setBandOrigin(this.background, (byte)1);
    getEventSupport().firePropertyChange("background", old, this.background);
  }
  
  public void setTitle(JRBand title) {
    Object old = this.title;
    this.title = title;
    setBandOrigin(this.title, (byte)2);
    getEventSupport().firePropertyChange("title", old, this.title);
  }
  
  public void setTitleNewPage(boolean isTitleNewPage) {
    boolean old = this.isTitleNewPage;
    this.isTitleNewPage = isTitleNewPage;
    getEventSupport().firePropertyChange("titleNewPage", old, this.isTitleNewPage);
  }
  
  public void setSummary(JRBand summary) {
    Object old = this.summary;
    this.summary = summary;
    setBandOrigin(this.summary, (byte)11);
    getEventSupport().firePropertyChange("summary", old, this.summary);
  }
  
  public void setNoData(JRBand noData) {
    Object old = this.noData;
    this.noData = noData;
    setBandOrigin(this.noData, (byte)12);
    getEventSupport().firePropertyChange("noData", old, this.noData);
  }
  
  public void setSummaryNewPage(boolean isSummaryNewPage) {
    boolean old = this.isSummaryNewPage;
    this.isSummaryNewPage = isSummaryNewPage;
    getEventSupport().firePropertyChange("summaryNewPage", old, this.isSummaryNewPage);
  }
  
  public void setFloatColumnFooter(boolean isFloatColumnFooter) {
    boolean old = this.isFloatColumnFooter;
    this.isFloatColumnFooter = isFloatColumnFooter;
    getEventSupport().firePropertyChange("floatColumnFooter", old, this.isFloatColumnFooter);
  }
  
  public void setPageHeader(JRBand pageHeader) {
    Object old = this.pageHeader;
    this.pageHeader = pageHeader;
    setBandOrigin(this.pageHeader, (byte)3);
    getEventSupport().firePropertyChange("pageHeader", old, this.pageHeader);
  }
  
  public void setPageFooter(JRBand pageFooter) {
    Object old = this.pageFooter;
    this.pageFooter = pageFooter;
    setBandOrigin(this.pageFooter, (byte)9);
    getEventSupport().firePropertyChange("pageFooter", old, this.pageFooter);
  }
  
  public void setLastPageFooter(JRBand lastPageFooter) {
    Object old = this.lastPageFooter;
    this.lastPageFooter = lastPageFooter;
    setBandOrigin(this.lastPageFooter, (byte)10);
    getEventSupport().firePropertyChange("lastPageFooter", old, this.lastPageFooter);
  }
  
  public void setColumnHeader(JRBand columnHeader) {
    Object old = this.columnHeader;
    this.columnHeader = columnHeader;
    setBandOrigin(this.columnHeader, (byte)4);
    getEventSupport().firePropertyChange("columnHeader", old, this.columnHeader);
  }
  
  public void setColumnFooter(JRBand columnFooter) {
    Object old = this.columnFooter;
    this.columnFooter = columnFooter;
    setBandOrigin(this.columnFooter, (byte)8);
    getEventSupport().firePropertyChange("columnFooter", old, this.columnFooter);
  }
  
  public void setDetail(JRBand detail) {
    Object old = this.detail;
    this.detail = detail;
    setBandOrigin(this.detail, (byte)6);
    getEventSupport().firePropertyChange("detail", old, this.detail);
  }
  
  public void setScriptletClass(String scriptletClass) {
    this.mainDesignDataset.setScriptletClass(scriptletClass);
  }
  
  public void setFormatFactoryClass(String formatFactoryClass) {
    Object old = this.formatFactoryClass;
    this.formatFactoryClass = formatFactoryClass;
    getEventSupport().firePropertyChange("formatFactoryClass", old, this.formatFactoryClass);
  }
  
  public void setResourceBundle(String resourceBundle) {
    this.mainDesignDataset.setResourceBundle(resourceBundle);
  }
  
  public void addImport(String value) {
    if (this.importsSet == null)
      this.importsSet = new HashSet(); 
    if (this.importsSet.add(value))
      getEventSupport().fireCollectionElementAddedEvent("imports", value, this.importsSet.size() - 1); 
  }
  
  public void removeImport(String value) {
    if (this.importsSet != null)
      if (this.importsSet.remove(value))
        getEventSupport().fireCollectionElementRemovedEvent("imports", value, -1);  
  }
  
  public void setDefaultFont(JRReportFont font) {
    Object old = this.defaultFont;
    this.defaultFont = font;
    getEventSupport().firePropertyChange("defaultFont", old, this.defaultFont);
  }
  
  public JRReportFont[] getFonts() {
    JRReportFont[] fontsArray = new JRReportFont[this.fontsList.size()];
    this.fontsList.toArray((Object[])fontsArray);
    return fontsArray;
  }
  
  public List getFontsList() {
    return this.fontsList;
  }
  
  public Map getFontsMap() {
    return this.fontsMap;
  }
  
  public void addFont(JRReportFont reportFont) throws JRException {
    if (this.fontsMap.containsKey(reportFont.getName()))
      throw new JRException("Duplicate declaration of report font : " + reportFont.getName()); 
    this.fontsList.add(reportFont);
    this.fontsMap.put(reportFont.getName(), reportFont);
    if (reportFont.isDefault())
      setDefaultFont(reportFont); 
    getEventSupport().fireCollectionElementAddedEvent("fonts", reportFont, this.fontsList.size() - 1);
  }
  
  public JRReportFont removeFont(String propName) {
    return removeFont((JRReportFont)this.fontsMap.get(propName));
  }
  
  public JRReportFont removeFont(JRReportFont reportFont) {
    if (reportFont != null) {
      if (reportFont.isDefault())
        setDefaultFont((JRReportFont)null); 
      int idx = this.fontsList.indexOf(reportFont);
      if (idx >= 0) {
        this.fontsList.remove(idx);
        this.fontsMap.remove(reportFont.getName());
        getEventSupport().fireCollectionElementRemovedEvent("fonts", reportFont, idx);
      } 
    } 
    return reportFont;
  }
  
  public void setDefaultStyle(JRStyle style) {
    Object old = this.defaultStyle;
    this.defaultStyle = style;
    getEventSupport().firePropertyChange("defaultStyle", old, this.defaultStyle);
  }
  
  public JRStyle[] getStyles() {
    JRStyle[] stylesArray = new JRStyle[this.stylesList.size()];
    this.stylesList.toArray((Object[])stylesArray);
    return stylesArray;
  }
  
  public List getStylesList() {
    return this.stylesList;
  }
  
  public Map getStylesMap() {
    return this.stylesMap;
  }
  
  public void addStyle(JRStyle style) throws JRException {
    if (this.stylesMap.containsKey(style.getName()))
      throw new JRException("Duplicate declaration of report style : " + style.getName()); 
    this.stylesList.add(style);
    this.stylesMap.put(style.getName(), style);
    if (style.isDefault())
      setDefaultStyle(style); 
    getEventSupport().fireCollectionElementAddedEvent("styles", style, this.stylesList.size() - 1);
  }
  
  public JRStyle removeStyle(String styleName) {
    return removeStyle((JRStyle)this.stylesMap.get(styleName));
  }
  
  public JRStyle removeStyle(JRStyle style) {
    if (style != null) {
      if (style.isDefault())
        setDefaultStyle((JRStyle)null); 
      int idx = this.stylesList.indexOf(style);
      if (idx >= 0) {
        this.stylesList.remove(idx);
        this.stylesMap.remove(style.getName());
        getEventSupport().fireCollectionElementRemovedEvent("styles", style, idx);
      } 
    } 
    return style;
  }
  
  public List getParametersList() {
    return this.mainDesignDataset.getParametersList();
  }
  
  public Map getParametersMap() {
    return this.mainDesignDataset.getParametersMap();
  }
  
  public void addParameter(JRParameter parameter) throws JRException {
    this.mainDesignDataset.addParameter(parameter);
  }
  
  public JRParameter removeParameter(String parameterName) {
    return this.mainDesignDataset.removeParameter(parameterName);
  }
  
  public JRParameter removeParameter(JRParameter parameter) {
    return this.mainDesignDataset.removeParameter(parameter);
  }
  
  public void setQuery(JRDesignQuery query) {
    this.mainDesignDataset.setQuery(query);
  }
  
  public List getFieldsList() {
    return this.mainDesignDataset.getFieldsList();
  }
  
  public Map getFieldsMap() {
    return this.mainDesignDataset.getFieldsMap();
  }
  
  public void addField(JRField field) throws JRException {
    this.mainDesignDataset.addField(field);
  }
  
  public JRField removeField(String fieldName) {
    return this.mainDesignDataset.removeField(fieldName);
  }
  
  public JRField removeField(JRField field) {
    return this.mainDesignDataset.removeField(field);
  }
  
  public List getSortFieldsList() {
    return this.mainDesignDataset.getSortFieldsList();
  }
  
  public void addSortField(JRSortField sortField) throws JRException {
    this.mainDesignDataset.addSortField(sortField);
  }
  
  public JRSortField removeSortField(String fieldName) {
    return this.mainDesignDataset.removeSortField(fieldName);
  }
  
  public JRSortField removeSortField(JRSortField sortField) {
    return this.mainDesignDataset.removeSortField(sortField);
  }
  
  public List getVariablesList() {
    return this.mainDesignDataset.getVariablesList();
  }
  
  public Map getVariablesMap() {
    return this.mainDesignDataset.getVariablesMap();
  }
  
  public void addVariable(JRDesignVariable variable) throws JRException {
    this.mainDesignDataset.addVariable(variable);
  }
  
  public JRVariable removeVariable(String variableName) {
    return this.mainDesignDataset.removeVariable(variableName);
  }
  
  public JRVariable removeVariable(JRVariable variable) {
    return this.mainDesignDataset.removeVariable(variable);
  }
  
  public List getGroupsList() {
    return this.mainDesignDataset.getGroupsList();
  }
  
  public Map getGroupsMap() {
    return this.mainDesignDataset.getGroupsMap();
  }
  
  public void addGroup(JRDesignGroup group) throws JRException {
    this.mainDesignDataset.addGroup(group);
  }
  
  public JRGroup removeGroup(String groupName) {
    return this.mainDesignDataset.removeGroup(groupName);
  }
  
  public JRGroup removeGroup(JRGroup group) {
    return this.mainDesignDataset.removeGroup(group);
  }
  
  public Collection getExpressions() {
    return JRExpressionCollector.collectExpressions((JRReport)this);
  }
  
  public JRDataset[] getDatasets() {
    JRDataset[] datasetArray = new JRDataset[this.datasetList.size()];
    this.datasetList.toArray((Object[])datasetArray);
    return datasetArray;
  }
  
  public List getDatasetsList() {
    return this.datasetList;
  }
  
  public Map getDatasetMap() {
    return this.datasetMap;
  }
  
  public void addDataset(JRDesignDataset dataset) throws JRException {
    if (this.datasetMap.containsKey(dataset.getName()))
      throw new JRException("Duplicate declaration of dataset : " + dataset.getName()); 
    this.datasetList.add(dataset);
    this.datasetMap.put(dataset.getName(), dataset);
    getEventSupport().fireCollectionElementAddedEvent("datasets", dataset, this.datasetList.size() - 1);
  }
  
  public JRDataset removeDataset(String datasetName) {
    return removeDataset((JRDataset)this.datasetMap.get(datasetName));
  }
  
  public JRDataset removeDataset(JRDataset dataset) {
    if (dataset != null) {
      int idx = this.datasetList.indexOf(dataset);
      if (idx >= 0) {
        this.datasetList.remove(idx);
        this.datasetMap.remove(dataset.getName());
        getEventSupport().fireCollectionElementRemovedEvent("datasets", dataset, idx);
      } 
    } 
    return dataset;
  }
  
  public JRDesignDataset getMainDesignDataset() {
    return this.mainDesignDataset;
  }
  
  public void setMainDataset(JRDesignDataset dataset) {
    Object old = this.background;
    this.mainDataset = (JRDataset)(this.mainDesignDataset = dataset);
    this.mainDesignDataset.setName(getName());
    this.mainDesignDataset.getEventSupport().addPropertyChangeListener((PropertyChangeListener)new PropagationChangeListener(getEventSupport()));
    getEventSupport().firePropertyChange("mainDataset", old, this.mainDataset);
  }
  
  public void preprocess() {
    for (Iterator it = getCrosstabs().iterator(); it.hasNext(); ) {
      JRDesignCrosstab crosstab = it.next();
      crosstab.preprocess();
    } 
  }
  
  protected List getCrosstabs() {
    final List crosstabs = new ArrayList();
    JRElementsVisitor.visitReport((JRReport)this, (JRVisitor)new JRVisitorSupport() {
          private final List val$crosstabs;
          
          private final JasperDesign this$0;
          
          public void visitCrosstab(JRCrosstab crosstab) {
            crosstabs.add(crosstab);
          }
        });
    return crosstabs;
  }
  
  public void setIgnorePagination(boolean ignorePagination) {
    boolean old = this.ignorePagination;
    this.ignorePagination = ignorePagination;
    getEventSupport().firePropertyChange("ignorePagination", old, this.ignorePagination);
  }
  
  public JRExpression getFilterExpression() {
    return this.mainDesignDataset.getFilterExpression();
  }
  
  public void setFilterExpression(JRExpression expression) {
    this.mainDesignDataset.setFilterExpression(expression);
  }
  
  public void addTemplate(JRReportTemplate template) {
    this.templateList.add(template);
    getEventSupport().fireCollectionElementAddedEvent("templates", template, this.templateList.size() - 1);
  }
  
  public boolean removeTemplate(JRReportTemplate template) {
    int idx = this.templateList.indexOf(template);
    if (idx >= 0) {
      this.templateList.remove(idx);
      getEventSupport().fireCollectionElementRemovedEvent("templates", template, idx);
      return true;
    } 
    return false;
  }
  
  public JRReportTemplate[] getTemplates() {
    return (JRReportTemplate[])this.templateList.toArray((Object[])new JRReportTemplate[this.templateList.size()]);
  }
  
  protected void setBandOrigin(JRBand band, byte type) {
    if (band instanceof JRDesignBand) {
      JROrigin origin = new JROrigin(type);
      ((JRDesignBand)band).setOrigin(origin);
    } 
  }
}
