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

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheWriter;
import org.apache.geode.cache.RegionAttributes;

/**
 * Presents an amalgam snapshot of all the {@linkplain org.apache.geode.cache.Region regions} in a
 * distributed system.
 */
public class CompoundRegionSnapshot implements RegionSnapshot {
  private static final long serialVersionUID = 6295026394298398004L;
  /** The name of the Region */
  private final String name;
  // private String constraintClass;

  private long lastModifiedTime = 0L; // the lates modified time
  private long lastAccessTime = 0L; // the latest access time
  private long numHits = 0L; // sum of all hits
  private long numMisses = 0L; // sum of all misses
  private float hitRatio = 0f; // calculated from all
  private long hitResponders = 0;
  private double hitRatioSum = 0.0;
  // private int entryCount = 0; //largest entryCount
  // private int subregionCount = 0; //largest subregionCount

  // private Map individuals = new HashMap();
  private final Set allCapControllers = new HashSet();
  private final Set allListeners = new HashSet();
  private final Set allDataPolicies = new HashSet();
  private final Set allRegionTtl = new HashSet();
  private final Set allEntryTtl = new HashSet();
  private final HashSet allCustomTtl = new HashSet();
  private final Set allRegionIdleTimeout = new HashSet();
  private final Set allEntryIdleTimeout = new HashSet();
  private final HashSet allCustomIdle = new HashSet();
  private final Set allScopes = new HashSet();
  private final Set allUserAttributes = new HashSet();
  private final Set allCacheLoaders = new HashSet();
  private final Set allCacheWriters = new HashSet();
  private final Set allLoadFactors = new HashSet();
  private final Set allInitialCaps = new HashSet();
  private final Set allConcLevels = new HashSet();
  private final Set allStatsEnabled = new HashSet();
  private final Set allKeyConstraints = new HashSet();
  private final Set allValueConstraints = new HashSet();

  /**
   * Creates a new <code>CompoundRegionSnapshot</code> for the region with the given name.
   */
  public CompoundRegionSnapshot(String regionName) {
    name = regionName;
  }

  /**
   * Amalgamates a <code>RegionSnapshot</code> into this <code>CompoundRegionSnapshot</code>.
   *
   * @param systemEntity The member of the distributed system that sent the snapshot
   * @param snap The snapshot to be amalgamated
   *
   * @throws IllegalArgumentException If <code>snap</code> is for a <code>Region</code> other than
   *         the one amalgamated by this snapshot.
   */
  public void addCache(GemFireVM systemEntity, RegionSnapshot snap) {
    if (!snap.getName().equals(name)) {
      throw new IllegalArgumentException(
          "All snapshots in a compound snapshot must have the same name");
    }
    // individuals.put(systemEntity, snap);

    RegionAttributes ra = snap.getAttributes();
    if (ra != null) {
      CacheListener listener = ra.getCacheListener();
      if (listener != null) {
        allListeners.add(listener.toString());
      }

      CacheWriter writer = ra.getCacheWriter();
      if (writer != null) {
        allCacheWriters.add(writer.toString());
      }

      CacheLoader loader = ra.getCacheLoader();
      if (loader != null) {
        allCacheLoaders.add(loader);
      }

      allDataPolicies.add(ra.getDataPolicy());
      allRegionTtl.add(ra.getRegionTimeToLive());
      allEntryTtl.add(ra.getEntryTimeToLive());
      allCustomTtl.add(ra.getCustomEntryTimeToLive().toString());
      allRegionIdleTimeout.add(ra.getRegionIdleTimeout());
      allEntryIdleTimeout.add(ra.getEntryIdleTimeout());
      allCustomIdle.add(ra.getCustomEntryIdleTimeout().toString());
      allScopes.add(ra.getScope());
      allLoadFactors.add(ra.getLoadFactor());
      allInitialCaps.add(ra.getInitialCapacity());
      allConcLevels.add(ra.getConcurrencyLevel());
      allStatsEnabled.add(ra.getStatisticsEnabled());
      allUserAttributes.add(snap.getUserAttribute());
      allKeyConstraints.add(ra.getKeyConstraint());
      allValueConstraints.add(ra.getValueConstraint());

      // if (constraintClass == null) {
      // Class clazz = ra.getKeyConstraint();
      // if (clazz != null) {
      // constraintClass = clazz.getName();
      // }
      // }
    }

    long modified = snap.getLastModifiedTime();
    if (modified > 0 && modified > lastModifiedTime) {
      lastModifiedTime = modified;
    }

    long access = snap.getLastAccessTime();
    if (access > 0 && access > lastAccessTime) {
      lastAccessTime = access;
    }

    long hitCount = snap.getNumberOfHits();
    if (hitCount > 0) {
      numHits += hitCount;
    }

    long missCount = snap.getNumberOfMisses();
    if (missCount > 0) {
      numMisses += missCount;
    }

    float hitRatio = snap.getHitRatio();
    if (hitRatio >= 0.00) {
      hitResponders++;
      hitRatioSum += hitRatio;
      this.hitRatio = (float) (hitRatioSum / hitResponders);
    }
  }

  /**
   * Returns the name of the <code>Region</code> whose information is amalgamated in this snapshot.
   */
  @Override
  public Object getName() {
    return name;
  }

  /**
   * Since a compound snapshot does not have <code>RegionAttributes</code>, this method returns
   * <code>null</code>.
   */
  @Override
  public RegionAttributes getAttributes() {
    return null;
  }

  /**
   * Since a compound snapshot does not have a <code>userAttributes</code>, this method returns
   * <code>null</code>.
   */
  @Override
  public Object getUserAttribute() {
    return null;
  }

  /**
   * Since a compound snapshot does not really represent a <code>Region</code>, this method returns
   * <code>false</code>.
   */
  public boolean isShared() {
    return false;
  }

  /**
   * Returns an {@link java.util.Iterator} of {@link java.lang.String}
   */
  public Iterator getAllCapacityControllers() {
    return allCapControllers.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of {@link java.lang.String}
   */
  public Iterator getAllListeners() {
    return allListeners.iterator();
  }

  /**
   * Returns an <code>Iterator</code> containing the <code>toString</code> of the
   * <code>CacheWriter</code>s of each instance of this snapshot's <code>region</code>
   */
  public Iterator getAllCacheWriters() {
    return allCacheWriters.iterator();
  }

  /**
   * Returns an <code>Iterator</code> containing the <code>toString</code> of the
   * <code>CacheLoader</code>s of each instance of this snapshot's <code>region</code>
   */
  public Iterator getAllCacheLoaders() {
    return allCacheLoaders.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of {@link org.apache.geode.cache.DataPolicy}
   */
  public Iterator getAllDataPolicies() {
    return allDataPolicies.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of {@link org.apache.geode.cache.ExpirationAttributes}
   */
  public Iterator getAllRegionTtl() {
    return allRegionTtl.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of {@link org.apache.geode.cache.ExpirationAttributes}
   */
  public Iterator getAllEntryTtl() {
    return allEntryTtl.iterator();
  }

  /**
   * Returns an <code>Iterator</code> containing the <code>toString</code> of the custom TTL
   * CustomExpiry's of each instance of this snapshot's <code>region</code>
   */
  public Iterator getAllCustomTtl() {
    return allCustomTtl.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of {@link org.apache.geode.cache.ExpirationAttributes}
   */
  public Iterator getAllRegionIdleTimeout() {
    return allRegionIdleTimeout.iterator();
  }


  /**
   * Returns an {@link java.util.Iterator} of {@link org.apache.geode.cache.ExpirationAttributes}
   */
  public Iterator getAllEntryIdleTimeout() {
    return allEntryIdleTimeout.iterator();
  }

  /**
   * Returns an <code>Iterator</code> containing the <code>toString</code> of the custom idleTimeout
   * CustomExpiry's of each instance of this snapshot's <code>region</code>
   */
  public Iterator getAllCustomIdleTimeout() {
    return allCustomIdle.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of {@link org.apache.geode.cache.Scope}
   */
  public Iterator getAllScopes() {
    return allScopes.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of float
   */
  public Iterator getAllLoadFactors() {
    return allLoadFactors.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of int
   */
  public Iterator getAllInitialCapacities() {
    return allInitialCaps.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of int
   */
  public Iterator getAllConcurrencyLevels() {
    return allConcLevels.iterator();
  }

  /**
   * Returns an {@link java.util.Iterator} of <code>Boolean</code> - which may be silly
   */
  public Iterator getAllStatsEnabled() {
    return allStatsEnabled.iterator();
  }

  /**
   * Returns an <code>Iterator</code> of the <code>userAttribute</code>s (as <code>Objects</code>s)
   * over all instances of this snapshot's <code>Region</code>.
   */
  public Iterator getAllUserAttributes() {
    return allUserAttributes.iterator();
  }

  /**
   * Returns an <code>Iterator</code> of the <code>keyConstraint</code>s (as <code>String</code>s)
   * over all instances of this snapshot's <code>Region</code>.
   */
  public Iterator getAllKeyConstraint() {
    return allKeyConstraints.iterator();
  }

  /**
   * Returns an <code>Iterator</code> of the <code>valueConstraint</code>s (as <code>String</code>s)
   * over all instances of this snapshot's <code>Region</code>.
   */
  public Iterator getAllValueConstraint() {
    return allValueConstraints.iterator();
  }

  /**
   * Returns the most recent <code>lastModifiedTime</code> of any instance of this snapshot's
   * <code>Region</code> across the distributed system.
   */
  @Override
  public long getLastModifiedTime() {
    return lastModifiedTime;
  }

  /**
   * Returns the most recent <code>lastAccessTime</code> of any instance of this snapshot's
   * <code>Region</code> across the distributed system.
   */
  @Override
  public long getLastAccessTime() {
    return lastAccessTime;
  }

  /**
   * Returns the cumulative number of hits across all instances of the snapshot's
   * <code>Region</code>.
   */
  @Override
  public long getNumberOfHits() {
    return numHits;
  }

  /**
   * Returns the cumulative number of misses across all instances of this snapshot's
   * <code>Region</code>.
   */
  @Override
  public long getNumberOfMisses() {
    return numMisses;
  }

  /**
   * Returns the aggregate hit ratio across all instances of this snapshot's <code>Region</code>.
   */
  @Override
  public float getHitRatio() {
    return hitRatio;
  }

}
