content: package gui.chart.panel;
  
  import core.manager.ColorManager;
  import gui.chart.CategoryTableXYDatasetRDA;
  import lombok.Getter;
  import lombok.Setter;
  import lombok.extern.slf4j.Slf4j;
  import org.jfree.chart.ChartPanel;
  import org.jfree.chart.JFreeChart;
  import org.jfree.chart.axis.DateAxis;
  import org.jfree.chart.axis.PeriodAxis;
  import org.jfree.chart.axis.PeriodAxisLabelInfo;
  import org.jfree.chart.block.BlockBorder;
  import org.jfree.chart.block.BlockContainer;
  import org.jfree.chart.block.BorderArrangement;
  import org.jfree.chart.labels.StandardXYToolTipGenerator;
  import org.jfree.chart.panel.selectionhandler.EntitySelectionManager;
  import org.jfree.chart.panel.selectionhandler.MouseClickSelectionHandler;
  import org.jfree.chart.panel.selectionhandler.RectangularHeightRegionSelectionHandler;
  import org.jfree.chart.plot.XYPlot;
  import org.jfree.chart.renderer.item.IRSUtilities;
  import org.jfree.chart.renderer.xy.StackedXYAreaRenderer3;
  import org.jfree.chart.title.LegendTitle;
  import org.jfree.chart.ui.HorizontalAlignment;
  import org.jfree.chart.ui.RectangleEdge;
  import org.jfree.chart.ui.RectangleInsets;
  import org.jfree.chart.util.IDetailPanel;
  import org.jfree.chart.util.SortOrder;
  import org.jfree.data.extension.DatasetIterator;
  import org.jfree.data.extension.DatasetSelectionExtension;
  import org.jfree.data.extension.impl.DatasetExtensionManager;
  import org.jfree.data.extension.impl.XYCursor;
  import org.jfree.data.extension.impl.XYDatasetSelectionExtension;
  import org.jfree.data.general.Dataset;
  import org.jfree.data.general.SelectionChangeEvent;
  import org.jfree.data.general.SelectionChangeListener;
  import org.jfree.data.time.Day;
  import org.jfree.data.time.Month;
  
  import java.awt.*;
  import java.text.DecimalFormat;
  import java.text.SimpleDateFormat;
  import java.util.LinkedHashSet;
  import java.util.TimeZone;
  import java.util.concurrent.atomic.AtomicInteger;
  
  @Slf4j
  public class StackedChart implements SelectionChangeListener<XYCursor> {
      @Getter @Setter private ChartPanel chartPanel;
      private JFreeChart jFreeChart;
      private XYPlot xyPlot;
      private DateAxis dateAxis;
  
      private CategoryTableXYDatasetRDA categoryTableXYDataset;
      private DatasetSelectionExtension<XYCursor> datasetExtension;
      private StackedXYAreaRenderer3 stackedXYAreaRenderer3;
  
      private BlockContainer blockContainerParent;
      private BlockContainer legendItemContainer;
      private LegendTitle legendTitle;
      @Getter @Setter private int legendFontSize;
      private RectangularHeightRegionSelectionHandler selectionHandler;
  
      private LinkedHashSet<String> listLinkedEventLst;
      private AtomicInteger counter;
  
      private ColorManager colorManager;
  
      public StackedChart(ColorManager colorManager,
                          ChartPanel chartPanel){
          this.colorManager = colorManager;
          this.chartPanel = chartPanel;
          this.jFreeChart = this.chartPanel.getChart();
          this.xyPlot = (XYPlot) this.jFreeChart.getPlot();
          this.dateAxis = (DateAxis) this.xyPlot.getDomainAxis();
          this.categoryTableXYDataset = (CategoryTableXYDatasetRDA) this.xyPlot.getDataset();
      }
  
      public void initialize(){
          this.counter = new AtomicInteger(0);
          this.listLinkedEventLst = new LinkedHashSet<>();
  
          this.datasetExtension = new XYDatasetSelectionExtension(this.categoryTableXYDataset);
          this.datasetExtension.addChangeListener(this);
  
          /////////////////////////////////////////////////////
          this.setStackedXYAreaRenderer3(this.datasetExtension);
  
          this.xyPlot.setRenderer(this.getStackedXYAreaRenderer3());
          this.xyPlot.getRangeAxis().setLowerBound(0.0);
          this.xyPlot.getRangeAxis().setAutoRange(true);
  
          //////////////////////////////////////////////////////////////////
          this.dateAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
  
          // add a selection handler //
          this.selectionHandler = new RectangularHeightRegionSelectionHandler();
          this.chartPanel.addMouseHandler(selectionHandler);
          this.chartPanel.addMouseHandler(new MouseClickSelectionHandler());
          this.chartPanel.removeMouseHandler(this.chartPanel.getZoomHandler());
  
          // add a selection manager //
          DatasetExtensionManager dExManager = new DatasetExtensionManager();
          dExManager.registerDatasetExtension(this.datasetExtension);
          this.chartPanel.setSelectionManager(new EntitySelectionManager(this.chartPanel,
                  new Dataset[] { this.categoryTableXYDataset}, dExManager));
          this.setLegendTitle();
          this.jFreeChart.addSubtitle(this.legendTitle);
          this.chartPanel.setRangeZoomable(false);
      }
  
      public ChartPanel getChartPanel() { return this.chartPanel; }
  
      public void changeHeightRectangularHeightRegionSelectionHandler(int oldH, int newH){
          if (this.selectionHandler.getStartPoint() != null && oldH!=newH) {
              Shape shape = this.chartPanel.getSelectionShape();
              int xD = (int) shape.getBounds().getX();
              int yD = (int) shape.getBounds().getY();
              int wD = (int) shape.getBounds().getWidth();
              int hD = (int) shape.getBounds().getHeight();
              double hDnew = 0;
              double hDnewD = 0;
              int hDnewI = 0;
              double oldHD =  oldH;
              double newHD =  newH;
              if (oldH<newH){
                  hDnew = (((newHD-oldHD)/newHD));
                  hDnewI = (int) ((shape.getBounds().getHeight())*hDnew*1.5
                          + (shape.getBounds().getHeight()));
              } else {
                  hDnew = (((oldHD-newHD)/oldHD)*shape.getBounds().getHeight());
                  hDnewD = shape.getBounds().getHeight() - hDnew;
                  hDnewI = (int) Math.round(hDnewD);
              }
              Rectangle selectionRect = new Rectangle(xD, yD, wD, hDnewI);
              this.chartPanel.setSelectionShape(selectionRect);
              this.chartPanel.repaint();
          }
      }
  
      public void setChartTitle(String titleText){
          this.jFreeChart.setTitle(titleText);
      }
  
      public void setSeriesPaintDynamicDetail(String seriesName){
  
          if (this.listLinkedEventLst.stream().noneMatch(e -> e.equalsIgnoreCase(seriesName))){
              try {
                  int cnt = counter.getAndIncrement();
                  this.stackedXYAreaRenderer3.setSeriesPaint(cnt, this.colorManager.getColor(seriesName));
                  this.categoryTableXYDataset.saveSeriesValues(cnt, seriesName);
                  this.listLinkedEventLst.add(seriesName);
  
              } catch (Exception ee){
                  log.error(ee.toString());
                  ee.printStackTrace();
              }
          }
      }
  
      public void setDateAxisWeekAndMore(){
          PeriodAxis domainAxis = new PeriodAxis(" ");
          domainAxis.setTimeZone(TimeZone.getDefault());
  
          domainAxis.setMajorTickTimePeriodClass(Day.class);
          PeriodAxisLabelInfo[] info = new PeriodAxisLabelInfo[2];
          info[0] = new PeriodAxisLabelInfo(Day.class,
                  new SimpleDateFormat("d"), new RectangleInsets(2, 2, 2, 2),
                  new Font("SansSerif", Font.BOLD, 8), Color.blue, false,
                  new BasicStroke(0.0f), Color.lightGray);
          info[1] = new PeriodAxisLabelInfo(Month.class,
                  new SimpleDateFormat("MMM"));
          /*info[2] = new PeriodAxisLabelInfo(Year.class,
                  new SimpleDateFormat("yyyy"));*/
          domainAxis.setLabelInfo(info);
  
          this.xyPlot.setDomainAxis(domainAxis);
      }
  
      public void addSeriesValue(double x, double y, String seriesName){
          this.categoryTableXYDataset.addSeriesValue(x, y, seriesName);
      }
  
      public void deleteAllSeriesData(int holdNumberOfItems){
          this.categoryTableXYDataset.deleteValuesFromDatasetDetail(holdNumberOfItems);
      }
  
      private void setLegendTitle() {
          this.legendTitle = new LegendTitle(this.jFreeChart.getPlot());
  
          this.blockContainerParent = new BlockContainer(new BorderArrangement());
          this.blockContainerParent.setFrame(new BlockBorder(1.0, 1.0, 1.0, 1.0));
  
          this.legendItemContainer = this.legendTitle.getItemContainer();
          this.legendItemContainer.setPadding(2, 10, 5, 2);
  
          this.blockContainerParent.add(this.legendItemContainer);
          this.legendTitle.setWrapper(this.blockContainerParent);
  
          this.legendTitle.setItemFont(new Font(LegendTitle.DEFAULT_ITEM_FONT.getFontName(),
                  LegendTitle.DEFAULT_ITEM_FONT.getStyle(), this.getLegendFontSize()));
  
          this.legendTitle.setPosition(RectangleEdge.RIGHT);
          this.legendTitle.setHorizontalAlignment(HorizontalAlignment.LEFT);
          this.legendTitle.setSortOrder(SortOrder.DESCENDING);
      }
  
      private StackedXYAreaRenderer3 getStackedXYAreaRenderer3() {
          return stackedXYAreaRenderer3;
      }
  
      private void setStackedXYAreaRenderer3(DatasetSelectionExtension<XYCursor> datasetExtension) {
  
          StandardXYToolTipGenerator standardXYToolTipGenerator = new StandardXYToolTipGenerator
                  ("{0} ({1}, {2})",
                          new SimpleDateFormat("HH:mm"),
                          new DecimalFormat("0.0"));
          this.stackedXYAreaRenderer3 = new StackedXYAreaRenderer3(standardXYToolTipGenerator,null);
          this.stackedXYAreaRenderer3.setRoundXCoordinates(true);
  
          //XYPlot plot = (XYPlot) this.getjFreeChart().getPlot();
          this.xyPlot.setDomainPannable(true);
          this.xyPlot.setRangePannable(true);
          this.xyPlot.setDomainCrosshairVisible(true);
          this.xyPlot.setRangeCrosshairVisible(true);
          datasetExtension.addChangeListener(this.xyPlot);
  
          IRSUtilities.setSelectedItemFillPaint(this.getStackedXYAreaRenderer3(), datasetExtension, Color.black);
      }
  
      public void addChartListenerReleaseMouse(IDetailPanel l){
          chartPanel.addListenerReleaseMouse(l);
      }
  
      public void removeChartListenerReleaseMouse(IDetailPanel l){
          chartPanel.removeListenerReleaseMouse(l);
      }
  
      @Override
      public void selectionChanged(SelectionChangeEvent<XYCursor> event) {
          XYDatasetSelectionExtension ext = (XYDatasetSelectionExtension)
                  event.getSelectionExtension();
          DatasetIterator<XYCursor> iter = ext.getSelectionIterator(true);
      }
  
  }
  