/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.awt.BorderLayout;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import ilog.views.chart.IlvChart;
import ilog.views.chart.IlvChartLayout;
import ilog.views.chart.IlvLegend;
import ilog.views.chart.data.IlvTreeTableDataSource;
import ilog.views.chart.datax.IlvDataColumnInfo;
import ilog.views.chart.datax.IlvDefaultDataColumnInfo;
import ilog.views.chart.datax.tree.list.IlvDefaultTreeListModel;
import ilog.views.chart.graphic.IlvDataLabelAnnotation;
import ilog.views.chart.interactor.IlvChartInfoViewInteractor;
import ilog.views.chart.renderer.IlvTreemapChartRenderer;
import ilog.views.util.IlvProductUtil;

public class Treemap extends JFrame {

  static class ExpenseItem {

    private String name;
    private double amount2004;
    private double amount2005;

    /**
     * Creates an expense item without associated amounts.
     */
    public ExpenseItem(String name) {
      this.name = name;
    }

    /**
     * Creates an expense item with associated amounts.
     */
    public ExpenseItem(String name, double amount2004, double amount2005) {
      this.name = name;
      this.amount2004 = amount2004;
      this.amount2005 = amount2005;
    }

    /**
     * Returns the amount for the year 2004, or 0 if none.
     */
    public double getAmount2004() {
      return this.amount2004;
    }

    /**
     * Returns the amount for the year 2005, or 0 if none.
     */
    public double getAmount2005() {
      return this.amount2005;
    }

    /**
     * Returns the growth factor from 2004 to 2005.
     */
    public double getGrowth() {
      if (this.amount2004 != 0)
        return this.amount2005 / this.amount2004;
      else if (this.amount2005 > 0)
        return Double.POSITIVE_INFINITY;
      else if (this.amount2005 < 0)
        return Double.NEGATIVE_INFINITY;
      else
        return Double.NaN;
    }

    /**
     * Returns the string representation of this object. For simplicity, we use
     * the name here.
     */
    Override
    public String toString() {
      return this.name;
    }
  }

  IlvDataColumnInfo amount2004Column = new IlvDefaultDataColumnInfo("Amount 2004", Double.class);
  IlvDataColumnInfo amount2005Column = new IlvDefaultDataColumnInfo("Amount 2005", Double.class);
  IlvDataColumnInfo growthColumn = new IlvDefaultDataColumnInfo("Growth", Double.class);
  IlvDataColumnInfo[] columns = new IlvDataColumnInfo[] { amount2004Column, amount2005Column, growthColumn };

  static final int amount2004Index = 0;
  static final int amount2005Index = 1;
  static final int growthIndex = 2;

  static class ExpensesModel extends IlvDefaultTreeListModel {

    public ExpensesModel(IlvDataColumnInfo[] columns) {
      super(columns);
    }

    Override
    public double getDoubleAt(Object object, int columnIndex) {
      switch (columnIndex) {
      case amount2004Index:
        return ((ExpenseItem) object).getAmount2004();
      case amount2005Index:
        return ((ExpenseItem) object).getAmount2005();
      case growthIndex:
        return ((ExpenseItem) object).getGrowth();
      default:
        throw new IllegalArgumentException("invalid column");
      }
    }

    Override
    public Object getValueAt(Object object, int columnIndex) {
      switch (columnIndex) {
      case amount2004Index:
      case amount2005Index:
      case growthIndex:
        return Double.valueOf(getDoubleAt(object, columnIndex));
      default:
        throw new IllegalArgumentException("invalid column");
      }
    }

    Override
    public List<?> getChildren(Object parent) {
      return super.getChildren(parent);
    }
    
    Override
    public void setDoubleAt(double value, Object object, int columnIndex) {
      throw new UnsupportedOperationException("the values are read-only");
    }

    Override
    public void setValueAt(Object value, Object object, int columnIndex) {
      throw new UnsupportedOperationException("the values are read-only");
    }
  }

  /** Creates new Treemap */
  public Treemap() {
    super("Basic Treemap Chart");
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    //
    // Instantiate the data model.
    //

    ExpensesModel model = new ExpensesModel(columns);

    ExpenseItem root = new ExpenseItem("Expenses");
    model.setRoot(root);
    {
      ExpenseItem kind;
      kind = new ExpenseItem("Cost of revenues");
      model.addChild(kind, root);
      {
        ExpenseItem purpose;
        purpose = new ExpenseItem("Cost of manufacturing");
        model.addChild(purpose, kind);
        model.addChild(new ExpenseItem("Materials and Supplies", 580, 611), purpose);
        model.addChild(new ExpenseItem("Production wages", 101, 104), purpose);
        purpose = new ExpenseItem("Cost of maintenance", 271, 324);
        model.addChild(purpose, kind);
        purpose = new ExpenseItem("Cost of training", 110, 80);
        model.addChild(purpose, kind);
        purpose = new ExpenseItem("Cost of consulting", 250, 270);
        model.addChild(purpose, kind);
      }
      kind = new ExpenseItem("Operating expenses");
      model.addChild(kind, root);
      {
        ExpenseItem kind1;
        kind1 = new ExpenseItem("Marketing and Promotion");
        model.addChild(kind1, kind);
        model.addChild(new ExpenseItem("Marketing employee costs", 120, 133), kind1);
        model.addChild(new ExpenseItem("Marketing campaigns", 30, 30), kind1);
        kind1 = new ExpenseItem("Sales employee costs");
        model.addChild(kind1, kind);
        model.addChild(new ExpenseItem("Sales employee salaries", 279, 291), kind1);
        model.addChild(new ExpenseItem("Sales commissions", 184, 195), kind1);
        kind1 = new ExpenseItem("General & Administrative");
        model.addChild(kind1, kind);
        model.addChild(new ExpenseItem("Finance department", 40, 43), kind1);
        model.addChild(new ExpenseItem("Legal department", 11, 12), kind1);
        model.addChild(new ExpenseItem("Management Information Systems", 40, 37), kind1);
        {
          ExpenseItem facilities = new ExpenseItem("Facilities");
          model.addChild(facilities, kind1);
          {
            ExpenseItem office = new ExpenseItem("Office expenses");
            model.addChild(office, facilities);
            model.addChild(new ExpenseItem("Rental", 38, 40), office);
            model.addChild(new ExpenseItem("Electricity", 2, 2), office);
            model.addChild(new ExpenseItem("Other office expenses", 24, 23), office);
          }
          model.addChild(new ExpenseItem("Telecommunication", 13, 11), facilities);
          model.addChild(new ExpenseItem("Other facilities", 9, 9), facilities);
        }
      }
      kind = new ExpenseItem("Financial Interests", 6, 5);
      model.addChild(kind, root);
      kind = new ExpenseItem("Income Taxes", 0, 49);
      model.addChild(kind, root);
    }

    //
    // Create a data source.
    //

    IlvTreeTableDataSource dataSource = new IlvTreeTableDataSource(model);

    //
    // Create a treemap chart with renderers.
    //

    IlvChart chart = new IlvChart(IlvChart.TREEMAP);
    chart.setDataSource(dataSource);
    IlvTreemapChartRenderer renderer = (IlvTreemapChartRenderer) chart.getRenderer(0);

    //
    // Customize the treemap renderer.
    //

    // Set the column which determines the area.
    renderer.setAreaColumn(amount2005Column);

    // Set the column which determines the color.
    // Small growth of expenses is green (good),
    // large growth of expenses is red (bad).
    renderer.setColorColumn(growthColumn);
    renderer.setColorScheme(IlvTreemapChartRenderer.COLORSCHEME_DIVERGING_GREEN_RED);

    // Add a label to each rectangle, and arrange to avoid label overlaps.
    renderer.setAnnotation(new IlvDataLabelAnnotation());
    renderer.setAnnotationVisibility(IlvTreemapChartRenderer.VISIBILITY_SMART);

    // Add a legend to the chart.
    // Format the growth values in the legend as percentages.

    renderer.setLegendLabelFormat(new DecimalFormat("+##0%;-##0%") {
      Override
      public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
        return super.format(number - 1, result, fieldPosition);
      }
    });
    chart.addLegend(new IlvLegend(), IlvChartLayout.SOUTH_BOTTOM);

    //
    // Add an interactor to the chart that displays a tooltip.
    //

    chart.addInteractor(new IlvChartInfoViewInteractor());

    //
    // Add the chart to the frame.
    //

    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(chart, BorderLayout.CENTER);
    setSize(500, 600);
  }

  public static void main(String[] args) {
    // This sample uses JViews Charts features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Charts Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Charts_Deployment);

    SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        Treemap frame = new Treemap();
        frame.setVisible(true);
      }
    });
  }
}