package com.alibaba.druid.stat;

import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class JdbcResultSetStat implements JdbcResultSetStatMBean {
   private final AtomicInteger openingCount = new AtomicInteger();
   private final AtomicInteger openingMax = new AtomicInteger();
   private final AtomicLong openCount = new AtomicLong();
   private final AtomicLong errorCount = new AtomicLong();
   private final AtomicLong aliveNanoTotal = new AtomicLong();
   private final AtomicLong aliveNanoMax = new AtomicLong();
   private final AtomicLong aliveNanoMin = new AtomicLong();
   private Throwable lastError;
   private long lastErrorTime;
   private long lastOpenTime = 0L;
   private final AtomicLong fetchRowCount = new AtomicLong(0L);
   private final AtomicLong closeCount = new AtomicLong(0L);

   public void reset() {
      this.openingMax.set(0);
      this.openCount.set(0L);
      this.errorCount.set(0L);
      this.aliveNanoTotal.set(0L);
      this.aliveNanoMax.set(0L);
      this.aliveNanoMin.set(0L);
      this.lastError = null;
      this.lastErrorTime = 0L;
      this.lastOpenTime = 0L;
      this.fetchRowCount.set(0L);
      this.closeCount.set(0L);
   }

   public void beforeOpen() {
      int invoking = this.openingCount.incrementAndGet();

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

      this.openCount.incrementAndGet();
      this.lastOpenTime = System.currentTimeMillis();
   }

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

   public int getOpeningCount() {
      return this.openingCount.get();
   }

   public int getOpeningMax() {
      return this.openingMax.get();
   }

   public long getOpenCount() {
      return this.openCount.get();
   }

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

   public long getAliveNanoTotal() {
      return this.aliveNanoTotal.get();
   }

   public long getAliveMillisTotal() {
      return this.aliveNanoTotal.get() / 1000000L;
   }

   public long getAliveMilisMin() {
      return this.aliveNanoMin.get() / 1000000L;
   }

   public long getAliveMilisMax() {
      return this.aliveNanoMax.get() / 1000000L;
   }

   public void afterClose(long aliveNano) {
      this.openingCount.decrementAndGet();
      this.aliveNanoTotal.addAndGet(aliveNano);

      long max;
      do {
         max = this.aliveNanoMax.get();
      } while(aliveNano > max && !this.aliveNanoMax.compareAndSet(max, aliveNano));

      do {
         max = this.aliveNanoMin.get();
      } while(aliveNano < max && !this.aliveNanoMin.compareAndSet(max, aliveNano));

   }

   public Throwable getLastError() {
      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 getHoldMillisTotal() {
      return this.getAliveNanoTotal() / 1000000L;
   }

   public long getFetchRowCount() {
      return this.fetchRowCount.get();
   }

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

   public void addFetchRowCount(long fetchCount) {
      this.fetchRowCount.addAndGet(fetchCount);
   }

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