package com.alibaba.druid.stat;

import com.alibaba.druid.util.Histogram;
import com.alibaba.druid.util.JMXUtils;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.JMException;
import javax.management.openmbean.CompositeData;

public class JdbcStatementStat implements JdbcStatementStatMBean {
   private final AtomicLong createCount = new AtomicLong(0L);
   private final AtomicLong prepareCount = new AtomicLong(0L);
   private final AtomicLong prepareCallCount = new AtomicLong(0L);
   private final AtomicLong closeCount = new AtomicLong(0L);
   private final AtomicInteger runningCount = new AtomicInteger();
   private final AtomicInteger concurrentMax = new AtomicInteger();
   private final AtomicLong count = new AtomicLong();
   private final AtomicLong errorCount = new AtomicLong();
   private final AtomicLong nanoTotal = new AtomicLong();
   private Throwable lastError;
   private long lastErrorTime;
   private long lastSampleTime = 0L;
   private final Histogram histogram = new Histogram(new long[]{10L, 100L, 1000L, 10000L});

   public long[] getHistogramRanges() {
      return this.histogram.getRanges();
   }

   public long[] getHistogramValues() {
      return this.histogram.toArray();
   }

   public void reset() {
      this.runningCount.set(0);
      this.concurrentMax.set(0);
      this.count.set(0L);
      this.errorCount.set(0L);
      this.nanoTotal.set(0L);
      this.lastError = null;
      this.lastErrorTime = 0L;
      this.lastSampleTime = 0L;
      this.createCount.set(0L);
      this.prepareCount.set(0L);
      this.prepareCallCount.set(0L);
      this.closeCount.set(0L);
      this.histogram.reset();
   }

   public void afterExecute(long nanoSpan) {
      this.runningCount.decrementAndGet();
      this.nanoTotal.addAndGet(nanoSpan);
      long millis = nanoSpan / 1000000L;
      this.histogram.record(millis);
   }

   public void beforeExecute() {
      int invoking = this.runningCount.incrementAndGet();

      int max;
      do {
         max = this.concurrentMax.get();
      } while(invoking > max && !this.concurrentMax.compareAndSet(max, invoking));

      this.count.incrementAndGet();
      this.lastSampleTime = System.currentTimeMillis();
   }

   public long getErrorCount() {
      return this.errorCount.get();
   }

   public int getRunningCount() {
      return this.runningCount.get();
   }

   public int getConcurrentMax() {
      return this.concurrentMax.get();
   }

   public long getExecuteCount() {
      return this.count.get();
   }

   public Date getExecuteLastTime() {
      return this.lastSampleTime == 0L ? null : new Date(this.lastSampleTime);
   }

   public long getNanoTotal() {
      return this.nanoTotal.get();
   }

   public long getMillisTotal() {
      return this.nanoTotal.get() / 1000000L;
   }

   public Throwable getLastException() {
      return this.lastError;
   }

   public Date getLastErrorTime() {
      return this.lastErrorTime <= 0L ? null : new Date(this.lastErrorTime);
   }

   public void error(Throwable error) {
      this.errorCount.incrementAndGet();
      this.lastError = error;
      this.lastErrorTime = System.currentTimeMillis();
   }

   public long getCloseCount() {
      return this.closeCount.get();
   }

   public long getCreateCount() {
      return this.createCount.get();
   }

   public long getExecuteMillisTotal() {
      return this.getNanoTotal() / 1000000L;
   }

   public long getPrepareCallCount() {
      return this.prepareCallCount.get();
   }

   public long getPrepareCount() {
      return this.prepareCount.get();
   }

   public long getExecuteSuccessCount() {
      return this.getExecuteCount() - this.getErrorCount() - (long)this.getRunningCount();
   }

   public CompositeData getLastError() throws JMException {
      return JMXUtils.getErrorCompositeData(this.getLastException());
   }

   public void incrementCreateCounter() {
      this.createCount.incrementAndGet();
   }

   public void incrementPrepareCallCount() {
      this.prepareCallCount.incrementAndGet();
   }

   public void incrementPrepareCounter() {
      this.prepareCount.incrementAndGet();
   }

   public void incrementStatementCloseCounter() {
      this.closeCount.incrementAndGet();
   }
}
