package org.infinispan.configuration.cache;

import java.util.List;

import org.infinispan.commons.configuration.attributes.AttributeDefinition;
import org.infinispan.commons.configuration.attributes.AttributeSet;
import org.infinispan.commons.configuration.attributes.ConfigurationElement;
import org.infinispan.commons.util.TimeQuantity;
import org.infinispan.configuration.parsing.Attribute;
import org.infinispan.configuration.parsing.Element;

/**
 * Configuration for stores.
 */
public class PersistenceConfiguration extends ConfigurationElement<PersistenceConfiguration> {
   public static final AttributeDefinition<Boolean> PASSIVATION = AttributeDefinition.builder(org.infinispan.configuration.parsing.Attribute.PASSIVATION, false).immutable().build();
   public static final AttributeDefinition<TimeQuantity> AVAILABILITY_INTERVAL = AttributeDefinition.builder(org.infinispan.configuration.parsing.Attribute.AVAILABILITY_INTERVAL, TimeQuantity.valueOf("30s")).parser(TimeQuantity.PARSER).immutable().build();
   public static final AttributeDefinition<Integer> CONNECTION_ATTEMPTS = AttributeDefinition.builder(org.infinispan.configuration.parsing.Attribute.CONNECTION_ATTEMPTS, 10).build();
   @Deprecated(forRemoval=true, since = "15.0")
   public static final AttributeDefinition<TimeQuantity> CONNECTION_INTERVAL = AttributeDefinition.builder(Attribute.CONNECTION_INTERVAL, TimeQuantity.valueOf("50s")).parser(TimeQuantity.PARSER).immutable().deprecated(15, 0).build();

   static AttributeSet attributeDefinitionSet() {
      return new AttributeSet(PersistenceConfiguration.class, PASSIVATION, AVAILABILITY_INTERVAL, CONNECTION_ATTEMPTS, CONNECTION_INTERVAL);
   }

   private final List<StoreConfiguration> stores;


   PersistenceConfiguration(AttributeSet attributes, List<StoreConfiguration> stores) {
      super(Element.PERSISTENCE, attributes, asChildren(stores));
      this.stores = stores;
   }

   private static ConfigurationElement<?>[] asChildren(List<StoreConfiguration> stores) {
      return stores.stream().filter(store -> store instanceof ConfigurationElement).toArray(ConfigurationElement[]::new);
   }

   /**
    * If true, data is only written to the cache store when it is evicted from memory, a phenomenon known as
    * 'passivation'. Next time the data is requested, it will be 'activated' which means that data will be brought back
    * to memory and removed from the persistent store. This gives you the ability to 'overflow' to disk, similar to
    * swapping in an operating system. <br /> <br /> If false, the cache store contains a copy of the contents in
    * memory, so writes to cache result in cache store writes. This essentially gives you a 'write-through'
    * configuration.
    */
   public boolean passivation() {
      return attributes.attribute(PASSIVATION).get();
   }

   public int availabilityInterval() {
      return (int) attributes.attribute(AVAILABILITY_INTERVAL).get().longValue();
   }

   public int connectionAttempts() {
      return attributes.attribute(CONNECTION_ATTEMPTS).get();
   }

   @Deprecated(forRemoval=true, since = "15.0")
   public int connectionInterval() {
      return -1;
   }

   public List<StoreConfiguration> stores() {
      return stores;
   }

   /**
    * Loops through all individual cache loader configs and checks if fetchPersistentState is set on any of them
    *
    * @deprecated since 14.0. This will always return false
    */
   @Deprecated(forRemoval=true, since = "14.0")
   public Boolean fetchPersistentState() {
      return false;
   }

   /**
    * Loops through all individual cache loader configs and checks if preload is set on any of them
    */
   public Boolean preload() {
      return stores.stream().anyMatch(StoreConfiguration::preload);
   }

   public boolean usingStores() {
      return !stores.isEmpty();
   }

   public boolean usingAsyncStore() {
      return stores.stream().map(StoreConfiguration::async).anyMatch(AsyncStoreConfiguration::enabled);
   }

   /**
    * Returns if any store is {@link StoreConfiguration#segmented()}
    *
    * @return true if any configured store is segmented, otherwise false
    */
   public boolean usingSegmentedStore() {
      return stores.stream().anyMatch(StoreConfiguration::segmented);
   }
}
