
package de.ivu.elect.jee.memory;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.management.MBeanServer;
import javax.management.ObjectName;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

  private static final long serialVersionUID = 1L;

  private static final Logger LOG = LoggerFactory.getLogger(MemoryController.class);

  private static final String HOTSPOT_COMPONENT_NAME = "com.sun.management:type=HotSpotDiagnostic";

  @Inject
  private transient MemoryGcStore memoryGcStore;

  private transient LineChartModel memoryModel;

  @Inject
  private transient MemoryGc memoryGc;

  public MemoryController() {
    super();
  }

  private void createMemoryModel() {
    // model
    memoryModel = new LineChartModel();
    // heap serie
    LineChartSeries memoryUsageSerie = new LineChartSeries();
    memoryUsageSerie.setLabel("Used");
    memoryModel.addSeries(memoryUsageSerie);

    LineChartSeries committedUsageSerie = new LineChartSeries();
    committedUsageSerie.setLabel("Committed");
    memoryModel.addSeries(committedUsageSerie);

    // max serie
    LineChartSeries maxMemorySerie = new LineChartSeries();
    maxMemorySerie.setLabel("Max");
    memoryModel.addSeries(maxMemorySerie);

    memoryModel.setLegendPosition("ne");
    memoryModel.getAxes().put(AxisType.X, new CategoryAxis("Zeitpunkt"));
    memoryModel.getAxis(AxisType.X).setTickAngle(270);
    memoryModel.getAxis(AxisType.Y).setMin(0);
    memoryModel.getAxis(AxisType.Y).setLabel(" Bytes ");
    memoryModel.setZoom(true);

    memoryGcStore.getData().stream()
        .sorted()
        .skip(1) // ersten Eintrag skippen, siehe GcController;
        .forEach(d -> {
          String dateTime = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, Locale.GERMANY).format(new Date(d.getZeitpunkt()));
          memoryUsageSerie.set(dateTime, d.getMemoryData().getUsed());
          maxMemorySerie.set(dateTime, d.getMemoryData().getMax());
          committedUsageSerie.set(dateTime, d.getMemoryData().getComitted());
        });

  }

  public LineChartModel getMemoryModel() {
    if (memoryModel == null) {
      createMemoryModel();
    }

    return memoryModel;
  }

  public void dumpHeap(String fileName, boolean liveObjectsOnly) {
    try {
      MBeanServer server = ManagementFactory.getPlatformMBeanServer(); // get the platform beans
      ObjectName hotspotDiagnosticBean = new ObjectName(HOTSPOT_COMPONENT_NAME); // should never fail
      server.invoke(hotspotDiagnosticBean, "dumpHeap", new Object[] {fileName, Boolean.valueOf(liveObjectsOnly)}, new String[] {"java.lang.String", "boolean"});

      LOG.info("Heap dump erzeugt: " + fileName);
    } catch (Exception e) { // Should never happen
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  private MemoryMXBean getMemoryMXBean() {
    return ManagementFactory.getMemoryMXBean();
  }

  public void performGc() {
    getMemoryMXBean().gc();
    memoryGc.speichereDaten();
  }

  public void saveData() {
    memoryGc.speichereDaten();
  }

  public void clearData() {
    memoryGc.clear();
  }
}
