
package de.ivu.elect.jee.memory;

import java.io.Serializable;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryManagerMXBean;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

import org.primefaces.model.chart.Axis;
import org.primefaces.model.chart.AxisType;
import org.primefaces.model.chart.CategoryAxis;
import org.primefaces.model.chart.LineChartModel;
import org.primefaces.model.chart.LineChartSeries;
import org.primefaces.model.chart.LinearAxis;

/**
 * http://www.dzone.com/links/r/monitoring_memory_usage_inside_a_java_web_applica.html
 */
@RequestScoped
public class GcController implements Serializable {

  private static final long serialVersionUID = 1L;

  private transient LineChartModel gcModel;

  @Inject
  private transient MemoryGcStore memoryGcStore;

  public GcController() {
    super();
  }

  private void createMemoryModel() {
    // model
    gcModel = new LineChartModel();

    LineChartSeries collectionTimeSerie = new LineChartSeries();
    collectionTimeSerie.setLabel("Collection time");
    collectionTimeSerie.setYaxis(AxisType.Y2);
    gcModel.addSeries(collectionTimeSerie);

    LineChartSeries collectionCountSerie = new LineChartSeries();
    collectionCountSerie.setLabel("Collection count");
    collectionCountSerie.setYaxis(AxisType.Y);
    gcModel.addSeries(collectionCountSerie);

    gcModel.setLegendPosition("ne");
    gcModel.getAxes().put(AxisType.X, new CategoryAxis("Zeitpunkt"));
    gcModel.getAxis(AxisType.X).setTickAngle(270);

    gcModel.getAxis(AxisType.Y).setMin(0);
    gcModel.getAxis(AxisType.Y).setLabel(" Anzahl ");

    Axis yAxis2 = new LinearAxis(" Zeit in Millisekunden ");
    yAxis2.setMin(0);
    gcModel.getAxes().put(AxisType.Y2, yAxis2);

    gcModel.setZoom(true);

    List<MemoryGcData> data = new ArrayList<>(memoryGcStore.getData());
    Collections.sort(data);
    boolean first = true;
    long offsetCount = 0;
    long offsetTime = 0;

    for (MemoryGcData datum : data) {
      GcData gcData = datum.getGcData();

      long gcCountTotal = gcData.getCollectionCount();
      long gcTimeTotal = gcData.getCollectionTime();
      if (first) {
        // ersten Eintrag als Referenz nehmen, nicht eintragen
        first = false;
      } else {
        String dateTime = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, Locale.GERMANY).format(new Date(datum.getZeitpunkt()));
        long countFinal = gcCountTotal - offsetCount;
        long timeFinal = gcTimeTotal - offsetTime;
        collectionTimeSerie.set(dateTime, timeFinal);
        collectionCountSerie.set(dateTime, countFinal);
      }

      offsetCount = gcCountTotal;
      offsetTime = gcTimeTotal;

    }
  }

  public LineChartModel getGcModel() {
    if (gcModel == null) {
      createMemoryModel();
    }

    return gcModel;
  }

  private List<GarbageCollectorMXBean> getGcMXBean() {
    return ManagementFactory.getGarbageCollectorMXBeans();
  }

  public String getName() {
    return getGcMXBean().stream().map(MemoryManagerMXBean::getName).collect(Collectors.joining(" / "));
  }

}
