package com.intellij.util.indexing;

import com.intellij.openapi.vfs.InvalidVirtualFileAccessException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.FileAttribute;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.util.containers.SLRUCache;
import com.intellij.util.io.DataInputOutputUtil;
import gnu.trove.TObjectLongHashMap;
import gnu.trove.TObjectLongProcedure;
import org.jetbrains.annotations.NotNull;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/**
 * @author Eugene Zhuravlev
 *         Date: Dec 25, 2007
 */
public class IndexingStamp {
  private IndexingStamp() {
  }

  private static class Timestamps {
    private static FileAttribute PERSISTENCE = new FileAttribute("__index_stamps__", 1);
    private final TObjectLongHashMap<ID<?, ?>> myIndexStamps = new TObjectLongHashMap<ID<?, ?>>(20);

    public void readFromStream(DataInputStream stream) throws IOException {
      int count = DataInputOutputUtil.readINT(stream);
      for (int i = 0; i < count; i++) {
        ID<?, ?> id = ID.findById(stream.readLong());
        long timestamp = stream.readLong();
        myIndexStamps.put(id, timestamp);
      }
    }

    public void writeToStream(final DataOutputStream stream) throws IOException {
      DataInputOutputUtil.writeINT(stream, myIndexStamps.size());
      myIndexStamps.forEachEntry(new TObjectLongProcedure<ID<?, ?>>() {
        public boolean execute(final ID<?, ?> id, final long timestamp) {
          try {
            stream.writeLong(id.getUniqueId());
            stream.writeLong(timestamp);
            return true;
          }
          catch (IOException e) {
            throw new RuntimeException(e);
          }
        }
      });
    }

    public long get(ID<?, ?> id) {
      return myIndexStamps.get(id);
    }

    public void set(ID<?, ?> id, long tmst) {
      myIndexStamps.put(id, tmst);
    }
  }

  private static final SLRUCache<VirtualFile, Timestamps> myTimestampsCache = new SLRUCache<VirtualFile, Timestamps>(5, 5) {
    @NotNull
    public Timestamps createValue(final VirtualFile key) {
      try {
        Timestamps result = new Timestamps();
        final DataInputStream stream = Timestamps.PERSISTENCE.readAttribute(key);
        if (stream != null) {
          result.readFromStream(stream);
        }
        return result;
      }
      catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    @Override
    protected void onDropFromCache(final VirtualFile key, final Timestamps value) {
      try {
        final DataOutputStream sink = Timestamps.PERSISTENCE.writeAttribute(key);
        value.writeToStream(sink);
        sink.close();
      }
      catch (InvalidVirtualFileAccessException ignored /*ok to ignore it here*/) {
      }
      catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  };

  public static boolean isFileIndexed(VirtualFile file, ID<?, ?> indexName, final long indexCreationStamp) {
    if (file instanceof NewVirtualFile && file.isValid()) {
      synchronized (myTimestampsCache) {
        return myTimestampsCache.get(file).get(indexName) == indexCreationStamp;
      }
    }

    return false;
  }

  public static void update(final VirtualFile file, final ID<?, ?> indexName, final long indexCreationStamp) {
    try {
      if (file instanceof NewVirtualFile && file.isValid()) {
        synchronized (myTimestampsCache) {
          myTimestampsCache.get(file).set(indexName, indexCreationStamp);
        }
      }
    }
    catch (InvalidVirtualFileAccessException ignored /*ok to ignore it here*/) {
    }
  }

  public static void flushCache() {
    synchronized (myTimestampsCache) {
      myTimestampsCache.clear();
    }
  }

}

@@ -1180,24 +1180,52 @@
 readLong();%0A
+        if (id != null) %7B%0A  
         myIn
@@ -1250,24 +1250,34 @@
 timestamp);%0A
+        %7D%0A
       %7D%0A    
@@ -1357,24 +1357,109 @@
 Exception %7B%0A
+      final int size = myIndexStamps.size();%0A      final int%5B%5D count = new int%5B%5D%7B0%7D;%0A
       DataIn
@@ -1489,36 +1489,20 @@
 stream, 
-myIndexStamps.
 size
-()
 );%0A     
@@ -1739,32 +1739,56 @@
 ong(timestamp);%0A
+            count%5B0%5D++;%0A
             retu
@@ -1903,24 +1903,24 @@
 %7D%0A        %7D%0A
-
       %7D);%0A  
@@ -1917,16 +1917,47 @@
     %7D);%0A
+      assert count%5B0%5D == size;%0A
     %7D%0A%0A 

Correct stamp read/write when repertoire of the indices changed (plugins add/remove)