/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.apache.geode.management.bean.stats;

import static org.apache.geode.cache.Region.SEPARATOR;
import static org.apache.geode.internal.statistics.StatisticsClockFactory.enabledClock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.experimental.categories.Category;

import org.apache.geode.internal.cache.CachePerfStats;
import org.apache.geode.internal.cache.DiskRegionStats;
import org.apache.geode.internal.cache.PartitionedRegionStats;
import org.apache.geode.internal.statistics.StatisticsClock;
import org.apache.geode.management.internal.beans.DiskRegionBridge;
import org.apache.geode.management.internal.beans.PartitionedRegionBridge;
import org.apache.geode.management.internal.beans.RegionMBeanBridge;
import org.apache.geode.test.junit.categories.JMXTest;

/**
 * Implementing RegionMXBean to ensure test coverage
 *
 */
@Category({JMXTest.class})
public class RegionStatsJUnitTest extends MBeanStatsTestCase {

  private RegionMBeanBridge bridge;
  private PartitionedRegionBridge parBridge;
  private DiskRegionBridge diskBridge;
  private CachePerfStats cachePerfStats;
  private PartitionedRegionStats partitionedRegionStats;
  private DiskRegionStats diskRegionStats;
  private StatisticsClock statisticsClock;

  @Override
  protected void init() {
    statisticsClock = enabledClock();
    cachePerfStats = new CachePerfStats(system, statisticsClock);
    partitionedRegionStats =
        new PartitionedRegionStats(system, SEPARATOR + "tests", statisticsClock);
    diskRegionStats = new DiskRegionStats(system, "test-disk");

    bridge = new RegionMBeanBridge(cachePerfStats);
    parBridge = new PartitionedRegionBridge(partitionedRegionStats);
    diskBridge = new DiskRegionBridge(diskRegionStats);
  }

  @Test
  public void testPlainCounters() throws InterruptedException {
    cachePerfStats.incEntryCount(400);
    cachePerfStats.incDiskTasksWaiting();
    partitionedRegionStats.incBucketCount(2);
    partitionedRegionStats.incLowRedundancyBucketCount(1);
    partitionedRegionStats.setActualRedundantCopies(2);

    partitionedRegionStats.setConfiguredRedundantCopies(3);
    partitionedRegionStats.incDataStoreEntryCount(1);
    partitionedRegionStats.incPrimaryBucketCount(10);

    sample();

    assertEquals(400, getEntryCount());
    assertEquals(2, getBucketCount());
    assertEquals(1, getNumBucketsWithoutRedundancy());
    assertEquals(2, getActualRedundancy());
    // assertTrue(getAvgBucketSize() > 0);
    // assertIndexDetailsEquals(3, getConfiguredRedundancy());
    assertEquals(1, getDataStoreEntryCount());
    assertEquals(10, getPrimaryBucketCount());
  }

  @Test
  public void testDiskCounters() throws InterruptedException {
    final long startTime = statisticsClock.getTime();

    diskRegionStats.incNumEntriesInVM(10);
    diskRegionStats.incNumOverflowOnDisk(15);
    diskRegionStats.startWrite();
    diskRegionStats.incWrittenBytes(1000);

    sample();

    assertEquals(1, getTotalDiskWritesProgress());
    assertEquals(10, getTotalDiskEntriesInVM());
    assertEquals(15, getTotalEntriesOnlyOnDisk());

    diskRegionStats.endWrite(startTime, statisticsClock.getTime());
    diskRegionStats.endRead(startTime, statisticsClock.getTime(), 1000);

    sample();

    assertTrue(getDiskReadsRate() > 0);
    assertTrue(getDiskReadsAverageLatency() > 0);

    assertTrue(getDiskWritesRate() > 0);
    assertTrue(getDiskWritesAverageLatency() > 0);
  }

  @Test
  public void testTimeBasedCounters() throws InterruptedException {
    final long startTime = statisticsClock.getTime();

    cachePerfStats.startCacheListenerCall();
    cachePerfStats.startCacheWriterCall();
    partitionedRegionStats.startPutRemote();

    sample();

    cachePerfStats.endCacheListenerCall(startTime);
    cachePerfStats.endCacheWriterCall(startTime);
    partitionedRegionStats.endPutRemote(startTime);

    sample();

    assertTrue(getCacheListenerCallsAvgLatency() > 0);
    assertTrue(getCacheWriterCallsAvgLatency() > 0);
    assertTrue(getPutRemoteLatency() > 0);
  }

  private long getEntryCount() {
    return bridge.getEntryCount();
  }

  private long getCacheListenerCallsAvgLatency() {
    return bridge.getCacheListenerCallsAvgLatency();
  }

  private long getCacheWriterCallsAvgLatency() {
    return bridge.getCacheWriterCallsAvgLatency();
  }

  private float getCreatesRate() {
    return bridge.getCreatesRate();
  }

  private float getPutAllRate() {
    return bridge.getPutAllRate();
  }

  private float getPutLocalRate() {
    return parBridge.getPutLocalRate();
  }

  private float getPutRemoteRate() {
    return parBridge.getPutRemoteRate();
  }

  private long getPutRemoteAvgLatency() {
    return parBridge.getPutRemoteAvgLatency();
  }

  private long getPutRemoteLatency() {
    return parBridge.getPutRemoteLatency();
  }

  private float getPutsRate() {
    return bridge.getPutsRate();
  }

  private float getDestroyRate() {
    return bridge.getDestroyRate();
  }

  private float getGetsRate() {
    return bridge.getGetsRate();
  }

  private long getHitCount() {
    return bridge.getHitCount();
  }

  private float getHitRatio() {
    return bridge.getHitRatio();
  }

  private long getLastAccessedTime() {
    return bridge.getLastAccessedTime();
  }

  private long getLastModifiedTime() {
    return bridge.getLastModifiedTime();
  }

  private float getLruDestroyRate() {
    return bridge.getLruDestroyRate();
  }

  private float getLruEvictionRate() {
    return bridge.getLruEvictionRate();
  }

  private long getMissCount() {
    return bridge.getMissCount();
  }

  private float getDiskReadsRate() {
    return diskBridge.getDiskReadsRate();
  }

  private float getDiskWritesRate() {
    return diskBridge.getDiskWritesRate();
  }

  private long getDiskReadsAverageLatency() {
    return diskBridge.getDiskReadsAverageLatency();
  }

  private long getDiskWritesAverageLatency() {
    return diskBridge.getDiskWritesAverageLatency();
  }

  private long getTotalDiskWritesProgress() {
    return diskBridge.getTotalDiskWritesProgress();
  }

  private long getTotalDiskEntriesInVM() {
    return diskBridge.getTotalDiskEntriesInVM();
  }

  private long getTotalEntriesOnlyOnDisk() {
    return diskBridge.getTotalEntriesOnlyOnDisk();
  }

  private int getActualRedundancy() {
    return parBridge.getActualRedundancy();
  }

  private int getAvgBucketSize() {
    return parBridge.getAvgBucketSize();
  }

  private int getBucketCount() {
    return parBridge.getBucketCount();
  }

  private int getConfiguredRedundancy() {
    return parBridge.getConfiguredRedundancy();
  }


  private int getNumBucketsWithoutRedundancy() {
    return parBridge.getNumBucketsWithoutRedundancy();
  }

  private int getPrimaryBucketCount() {
    return parBridge.getPrimaryBucketCount();
  }

  private int getDataStoreEntryCount() {
    return parBridge.getTotalBucketSize();
  }

  private long getDiskTaskWaiting() {
    return 0;
  }
}
