package de.chandre.quartz.spring.listener;

import javax.annotation.PostConstruct;

import org.quartz.JobExecutionContext;
import org.quartz.Trigger;
import org.quartz.Trigger.CompletedExecutionInstruction;
import org.quartz.listeners.TriggerListenerSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.util.StringUtils;

import de.chandre.quartz.spring.QuartzSchedulerProperties.Metrics;

/**
 * Quartz metrics listener for Spring Boot actuator.<br>
 * requires a CounterService and GaugeService to be configured.
 * 
 * @author André
 * @since 1.0.5
 *
 */
public class TriggerMetricsListener extends TriggerListenerSupport {

	public static final String SEPARATOR = ".";

	public static final String METRIC_PREFIX = "quartz" + SEPARATOR;

	public static final String METRIC_INFIX_TYPE_JOB = "job" + SEPARATOR;
	public static final String METRIC_INFIX_TYPE_TRIGGER = "trigger" + SEPARATOR;

	public static final String METRIC_SUFFIX_START = SEPARATOR + "fired";
	public static final String METRIC_SUFFIX_COMPLETE = SEPARATOR + "completed";
	public static final String METRIC_SUFFIX_MISFIRE = SEPARATOR + "misfired";

	private Metrics metricSettings;
	private String name;

	@Autowired(required = false)
	private CounterService counterService;

	@Autowired(required = false)
	private GaugeService gaugeService;

	public TriggerMetricsListener(Metrics metrics, String name) {
		this.metricSettings = metrics;
		this.name = StringUtils.isEmpty(name) ? getClass().getSimpleName() : name;
	}

	public boolean isActive() {
		return this.metricSettings.isEnabled() && (this.counterService != null || this.gaugeService != null);
	}

	@PostConstruct
	public void init() {
		getLog().info(this.getClass().getName() + " is " + (isActive() ? "active" : "deactivated"));
	}

	@Override
	public String getName() {
		return this.name;
	}

	protected void mesure(String suffix, Trigger trigger, JobExecutionContext context,
			CompletedExecutionInstruction triggerInstructionCode) {
		if (null == this.counterService && null == this.gaugeService) {
			return;
		}
		getLog().trace("exposing metrics");

		String jobKey = METRIC_PREFIX + METRIC_INFIX_TYPE_JOB + trigger.getJobKey().getGroup() + SEPARATOR
				+ trigger.getJobKey().getName() + suffix;
		String triggerKey = METRIC_PREFIX + METRIC_INFIX_TYPE_TRIGGER + trigger.getKey().getGroup() + SEPARATOR
				+ trigger.getKey().getName() + suffix;

		if (null != this.counterService) {
			if (this.metricSettings.isEnableJobGroupCounter()) {
				// count job group
				this.counterService.increment(METRIC_PREFIX + METRIC_INFIX_TYPE_JOB + trigger.getJobKey().getGroup() + suffix);
			}
			if (this.metricSettings.isEnableJobCounter()) {
				// count job group and job name
				this.counterService.increment(jobKey);
			}
			if (this.metricSettings.isEnableTriggerCounter()) {
				// count trigger group and trigger name
				this.counterService.increment(triggerKey);
			}
			
			// count finish code
			if (this.metricSettings.isEnableExecutionInstructionCounter() && null != triggerInstructionCode) {
				if (this.metricSettings.isEnableTriggerCounter()) {
					this.counterService.increment(triggerKey + SEPARATOR + triggerInstructionCode.name());
				}
				if (this.metricSettings.isEnableJobCounter()) {
					// if a job has more than one trigger .
					this.counterService.increment(jobKey + SEPARATOR + triggerInstructionCode.name());
				}
			}
		}

		if (null != context && null != this.gaugeService) {
			if (context.getJobRunTime() != -1) {
				if (this.metricSettings.isEnableTriggerGauges()) {
					gaugeService.submit(triggerKey, Long.valueOf(context.getJobRunTime()).doubleValue());
				}
				if (this.metricSettings.isEnableJobGauges()) {
					// if a job has more than one trigger .
					gaugeService.submit(jobKey, Long.valueOf(context.getJobRunTime()).doubleValue());
				}
			}
		}

	}

	@Override
	public void triggerFired(Trigger trigger, JobExecutionContext context) {
		mesure(METRIC_SUFFIX_START, trigger, context, null);
	}

	@Override
	public void triggerMisfired(Trigger trigger) {
		mesure(METRIC_SUFFIX_MISFIRE, trigger, null, null);
	}

	@Override
	public void triggerComplete(Trigger trigger, JobExecutionContext context,
			CompletedExecutionInstruction triggerInstructionCode) {
		mesure(METRIC_SUFFIX_COMPLETE, trigger, context, triggerInstructionCode);
	}

}