/*
 * 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.internal.statistics;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.logging.log4j.Logger;

import org.apache.geode.Statistics;
import org.apache.geode.StatisticsType;
import org.apache.geode.internal.util.StopWatch;
import org.apache.geode.logging.internal.log4j.api.LogService;

/**
 * Integration TestCase for StatSampler.
 *
 * @since GemFire 7.0
 */
public abstract class StatSamplerTestCase {

  protected static final Logger logger = LogService.getLogger();

  protected abstract StatisticsManager getStatisticsManager();

  protected int getStatListModCount() {
    return getStatisticsManager().getStatListModCount();
  }

  protected List<Statistics> getStatsList() {
    return getStatisticsManager().getStatsList();
  }

  protected static void waitForFileToExist(final File file, final long millis, final long sleep) {
    boolean done = false;
    try {
      for (StopWatch time = new StopWatch(true); !done && time.elapsedTimeMillis() < millis; done =
          (file.exists())) {
        Thread.sleep(sleep);
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    assertTrue("Waiting for file to exist: " + file, done);
  }

  protected static void waitForFileToDelete(final File file, final long millis, final long sleep) {
    boolean done = false;
    try {
      for (StopWatch time = new StopWatch(true); !done && time.elapsedTimeMillis() < millis; done =
          (!file.exists())) {
        Thread.sleep(sleep);
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    assertTrue("Waiting for file to be deleted: " + file, done);
  }

  protected static void waitForExpectedStatValue(final Statistics statSamplerStats,
      final String statName, final int expectedStatValue, final long millis, final long sleep) {
    boolean done = false;
    try {
      for (StopWatch time = new StopWatch(true); !done && time.elapsedTimeMillis() < millis; done =
          (statSamplerStats.getInt(statName) >= expectedStatValue)) {
        Thread.sleep(sleep);
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    assertTrue("Waiting for " + statName + " >= " + expectedStatValue, done);
  }

  protected static void waitForStatSample(final Statistics statSamplerStats, final int samples,
      final long millis, final long sleep) {
    waitForExpectedStatValue(statSamplerStats, "sampleCount", samples, millis, sleep);
  }

  protected static void assertStatValueDoesNotChange(final Statistics statSamplerStats,
      final String statName, final int expectedStatValue, final long millis, final long sleep) {
    boolean done = false;
    try {
      for (StopWatch time = new StopWatch(true); !done && time.elapsedTimeMillis() < millis; done =
          (statSamplerStats.getInt(statName) != expectedStatValue)) {
        Thread.sleep(sleep);
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    assertFalse("Waiting to assert that " + statName + " does not change from " + expectedStatValue,
        done);
  }

  protected static class AllStatistics {

    private final HostStatSampler statSampler;
    private final Map<StatisticsType, Set<Statistics>> allStatistics;

    protected AllStatistics(HostStatSampler statSampler) throws InterruptedException {
      this.statSampler = statSampler;
      allStatistics = initAllStatistics();
    }

    private Map<StatisticsType, Set<Statistics>> initAllStatistics() throws InterruptedException {
      assertTrue(statSampler.waitForInitialization(5000));

      Map<StatisticsType, Set<Statistics>> statsTypeToStats =
          new HashMap<>();
      Statistics[] stats = statSampler.getStatistics();
      for (final Statistics stat : stats) {
        StatisticsType statsType = stat.getType();
        Set<Statistics> statsSet = statsTypeToStats.get(statsType);
        if (statsSet == null) {
          statsSet = new HashSet<>();
          statsSet.add(stat);
          statsTypeToStats.put(statsType, statsSet);
        } else {
          statsSet.add(stat);
        }
      }
      return statsTypeToStats;
    }

    protected boolean containsStatisticsType(StatisticsType type) {
      throw new UnsupportedOperationException("TODO");
    }

    protected boolean containsStatisticsType(String typeName) throws InterruptedException {
      for (StatisticsType statType : allStatistics.keySet()) {
        if (statType.getName().equals(typeName)) {
          return true;
        }
      }
      return false;
    }

    protected boolean containsStatistics(Statistics statistics) {
      throw new UnsupportedOperationException("TODO");
    }

    protected boolean containsStatistics(String instanceName) throws InterruptedException {
      for (StatisticsType statType : allStatistics.keySet()) {
        for (Statistics statistics : allStatistics.get(statType)) {
          if (statistics.getTextId().equals(instanceName)) {
            return true;
          }
        }
      }
      return false;
    }

    /**
     * Statistics[0]: typeName=StatSampler instanceName=statSampler Statistics[1]: typeName=VMStats
     * instanceName=vmStats Statistics[2]: typeName=VMMemoryUsageStats
     * instanceName=vmHeapMemoryStats Statistics[3]: typeName=VMMemoryUsageStats
     * instanceName=vmNonHeapMemoryStats Statistics[4]: typeName=VMMemoryPoolStats instanceName=Code
     * Cache-Non-heap memory Statistics[5]: typeName=VMMemoryPoolStats instanceName=PS Eden
     * Space-Heap memory Statistics[6]: typeName=VMMemoryPoolStats instanceName=PS Survivor
     * Space-Heap memory Statistics[7]: typeName=VMMemoryPoolStats instanceName=PS Old Gen-Heap
     * memory Statistics[8]: typeName=VMMemoryPoolStats instanceName=PS Perm Gen-Non-heap memory
     * Statistics[9]: typeName=VMGCStats instanceName=PS Scavenge Statistics[10]: typeName=VMGCStats
     * instanceName=PS MarkSweep Statistics[11]: typeName=LinuxSystemStats
     * instanceName=kuwait.gemstone.com Statistics[12]: typeName=LinuxProcessStats
     * instanceName=javaApp0-proc
     */
    protected void dumpStatistics() throws InterruptedException {
      Statistics[] stats = statSampler.getStatistics();
      for (int i = 0; i < stats.length; i++) {
        logger.info("Statistics[{}]: typeName={} instanceName={}", i, stats[i].getType().getName(),
            stats[i].getTextId());
      }
    }
  }
}
