package org.infinispan.lucene;

import java.util.regex.Pattern;

import org.infinispan.lucene.logging.Log;
import org.infinispan.persistence.keymappers.TwoWayKey2StringMapper;
import org.infinispan.util.logging.LogFactory;

/**
 * To configure a JdbcStringBasedCacheStoreConfig for the Lucene Directory, use this
 * Key2StringMapper implementation.
 *
 * @see org.infinispan.persistence.jdbc.configuration.JdbcStringBasedStoreConfigurationBuilder#key2StringMapper(String)
 *
 * @author Sanne Grinovero
 * @since 4.1
 */
@SuppressWarnings("unchecked")
public final class LuceneKey2StringMapper implements TwoWayKey2StringMapper {

   /**
    * The pipe character was chosen as it's illegal to have a pipe in a filename, so we
    * don't have to escape the filenames generated by Lucene.
    */
   static final Pattern singlePipePattern = Pattern.compile("\\|");

   private static final Log log = LogFactory.getLog(LuceneKey2StringMapper.class, Log.class);

   @Override
   public boolean isSupportedType(Class<?> keyType) {
      return (keyType == ChunkCacheKey.class    ||
              keyType == FileCacheKey.class     ||
              keyType == FileListCacheKey.class ||
              keyType == FileReadLockKey.class);
   }

   @Override
   public String getStringMapping(Object key) {
      return key.toString();
   }

   /**
    * This method has to perform the inverse transformation of the keys used in the Lucene
    * Directory from String to object. So this implementation is strongly coupled to the
    * toString method of each key type.
    *
    * @see ChunkCacheKey#toString()
    * @see FileCacheKey#toString()
    * @see FileListCacheKey#toString()
    * @see FileReadLockKey#toString()
    */
   @Override
   public Object getKeyMapping(String key) {
      if (key == null) {
         throw new IllegalArgumentException("Not supporting null keys");
      }
      // ChunkCacheKey: "C|" + fileName + "|" + chunkId + "|" + bufferSize "|" + indexName  + "|" + affinitySegmentId;
      // FileCacheKey : "M|" + fileName + "|"+ indexName + "|" + affinitySegmentId;
      // FileListCacheKey : "*|" + indexName + "|" + affinitySegmentId;
      // FileReadLockKey : "RL|" + fileName + "|"+ indexName + "|" + affinitySegmentId;
      String[] split = singlePipePattern.split(key);
      switch (split[0]) {
      case "C": {
            if (split.length != 6) {
               throw log.keyMappperUnexpectedStringFormat(key);
            }
            final String indexName = split[4];
            final String fileName = split[1];
            final int chunkId = toInt(split[2], key);
            final int bufferSize = toInt(split[3], key);
            final int affinitySegmentId = toInt(split[5], key);
            return new ChunkCacheKey(indexName, fileName, chunkId, bufferSize, affinitySegmentId);
         }
      case "M": {
            if (split.length != 4) throw log.keyMappperUnexpectedStringFormat(key);
            final String indexName = split[2];
            final String fileName = split[1];
            final int affinitySegmentId = toInt(split[3], key);
            return new FileCacheKey(indexName, fileName, affinitySegmentId);
         }
      case "*": {
            if (split.length != 3) throw log.keyMappperUnexpectedStringFormat(key);
            final String indexName = split[1];
            final int affinitySegmentId = toInt(split[2], key);
            return new FileListCacheKey(indexName, affinitySegmentId);
         }
      case "RL": {
            if (split.length != 4) throw log.keyMappperUnexpectedStringFormat(key);
            final String indexName = split[2];
            final String fileName = split[1];
            final int affinitySegmentId = toInt(split[3], key);
            return new FileReadLockKey(indexName, fileName, affinitySegmentId);
         }
      default:
         throw log.keyMappperUnexpectedStringFormat(key);
      }
   }

   private int toInt(String token, String fullInput) {
      try {
         return Integer.parseInt(token);
      }
      catch (NumberFormatException nfe) {
         throw log.keyMappperUnexpectedStringFormat(fullInput);
      }
   }

}
