package org.infinispan.server.hotrod;

import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;

import org.infinispan.AdvancedCache;
import org.infinispan.commons.CacheException;
import org.infinispan.commons.configuration.ClassWhiteList;
import org.infinispan.commons.marshall.Marshaller;
import org.infinispan.commons.marshall.jboss.GenericJBossMarshaller;
import org.infinispan.container.versioning.NumericVersion;
import org.infinispan.metadata.EmbeddedMetadata.Builder;
import org.infinispan.metadata.Metadata;
import org.infinispan.upgrade.SourceMigrator;

/**
 * An implementation of Migrator, that understands the Hot Rod key and value formats.
 *
 * @author Manik Surtani
 * @since 5.2
 */
class HotRodSourceMigrator implements SourceMigrator {
   private final static String KNOWN_KEY = "___MigrationManager_HotRod_KnownKeys___";

   private final AdvancedCache<byte[], byte[]> cache;
   private final Marshaller marshaller;

   @Override
   public String getCacheName() {
      return cache.getName();
   }

   HotRodSourceMigrator(AdvancedCache<byte[], byte[]> cache, ClassWhiteList classWhiteList) {
      this.cache = cache;
      this.marshaller = new GenericJBossMarshaller(classWhiteList); // TODO: Hard coded!  Yuck!  Assumes the Synchronizer service will use the same marshaller.  Doesn't matter what actual clients use to store/retrieve data.
   }

   @Override
   public void recordKnownGlobalKeyset() {
      try {
         // TODO: the bulk of this code is reusable across different implementations of Migrator
         byte[] bak = marshaller.objectToByteBuffer(KNOWN_KEY);

         Set<byte[]> allKeys = cache.keySet();
         Set<byte[]> copy = allKeys.stream().collect(Collectors.toSet());

         // Remove KNOWN_KEY from the key set - just in case it is there from a previous run.
         copy.remove(bak);

         // we cannot store the Set as it is; it will break if attempting to be read via Hot Rod.  This should be wrapped
         // in a CacheValue.
         Metadata metadata = new Builder().version(new NumericVersion(1)).build();
         cache.put(bak, marshaller.objectToByteBuffer(copy), metadata);
      } catch (InterruptedException | IOException e) {
         throw new CacheException(e);
      }
   }
}
