/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * Licensed 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.jetbrains.jps.backwardRefs.index;

import com.intellij.openapi.util.io.DataInputOutputUtilRt;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.IndexExtension;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.DataInputOutputUtil;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.VoidDataExternalizer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.backwardRefs.LightRef;
import org.jetbrains.jps.backwardRefs.LightRefDescriptor;
import org.jetbrains.jps.backwardRefs.SignatureData;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class CompilerIndices {
  //TODO manage version separately
  public final static int VERSION = 3;

  public final static ID<LightRef, Integer> BACK_USAGES = ID.create("back.refs");
  public final static ID<LightRef, Collection<LightRef>> BACK_HIERARCHY = ID.create("back.hierarchy");
  public final static ID<LightRef, Void> BACK_CLASS_DEF = ID.create("back.class.def");
  public final static ID<SignatureData, Collection<LightRef>> BACK_MEMBER_SIGN = ID.create("back.member.sign");

  public static List<IndexExtension<?, ?, CompiledFileData>> getIndices() {
    return Arrays.asList(createBackwardClassDefinitionExtension(),
                         createBackwardUsagesExtension(),
                         createBackwardHierarchyExtension(),
                         createBackwardSignatureExtension());
  }

  private static IndexExtension<LightRef, Integer, CompiledFileData> createBackwardUsagesExtension() {
    return new IndexExtension<LightRef, Integer, CompiledFileData>() {
      @Override
      public int getVersion() {
        return VERSION;
      }

      @NotNull
      public ID<LightRef, Integer> getName() {
        return BACK_USAGES;
      }

      @NotNull
      public DataIndexer<LightRef, Integer, CompiledFileData> getIndexer() {
        return CompiledFileData::getReferences;
      }

      @NotNull
      public KeyDescriptor<LightRef> getKeyDescriptor() {
        return LightRefDescriptor.INSTANCE;
      }

      @NotNull
      public DataExternalizer<Integer> getValueExternalizer() {
        return new UnsignedByteExternalizer();
      }
    };
  }

  private static class UnsignedByteExternalizer implements DataExternalizer<Integer> {
    @Override
    public void save(@NotNull DataOutput out, Integer value) throws IOException {
      int v = value;
      if (v > 255) {
        v = 255;
      }
      out.writeByte(v);
    }

    @Override
    public Integer read(@NotNull DataInput in) throws IOException {
      return in.readByte() & 0xFF;
    }
  }

  private static IndexExtension<LightRef, Collection<LightRef>, CompiledFileData> createBackwardHierarchyExtension() {
    return new IndexExtension<LightRef, Collection<LightRef>, CompiledFileData>() {
      @Override
      public int getVersion() {
        return VERSION;
      }

      @NotNull
      public ID<LightRef, Collection<LightRef>> getName() {
        return BACK_HIERARCHY;
      }

      @NotNull
      public DataIndexer<LightRef, Collection<LightRef>, CompiledFileData> getIndexer() {
        return CompiledFileData::getBackwardHierarchy;
      }

      @NotNull
      public KeyDescriptor<LightRef> getKeyDescriptor() {
        return LightRefDescriptor.INSTANCE;
      }

      @NotNull
      public DataExternalizer<Collection<LightRef>> getValueExternalizer() {
        return createLightRefSeqExternalizer();
      }
    };
  }

  private static IndexExtension<LightRef, Void, CompiledFileData> createBackwardClassDefinitionExtension() {
    return new IndexExtension<LightRef, Void, CompiledFileData>() {
      @Override
      public int getVersion() {
        return VERSION;
      }

      @NotNull
      public ID<LightRef, Void> getName() {
        return BACK_CLASS_DEF;
      }

      @NotNull
      public DataIndexer<LightRef, Void, CompiledFileData> getIndexer() {
        return CompiledFileData::getDefinitions;
      }

      @NotNull
      public KeyDescriptor<LightRef> getKeyDescriptor() {
        return LightRefDescriptor.INSTANCE;
      }

      @NotNull
      public DataExternalizer<Void> getValueExternalizer() {
        return VoidDataExternalizer.INSTANCE;
      }
    };
  }

  private static IndexExtension<SignatureData, Collection<LightRef>, CompiledFileData> createBackwardSignatureExtension() {
    return new IndexExtension<SignatureData, Collection<LightRef>, CompiledFileData>() {
      @NotNull
      @Override
      public ID<SignatureData, Collection<LightRef>> getName() {
        return BACK_MEMBER_SIGN;
      }

      @NotNull
      @Override
      public DataIndexer<SignatureData, Collection<LightRef>, CompiledFileData> getIndexer() {
        return CompiledFileData::getSignatureData;
      }

      @NotNull
      @Override
      public KeyDescriptor<SignatureData> getKeyDescriptor() {
        return createSignatureDataDescriptor();
      }

      @NotNull
      @Override
      public DataExternalizer<Collection<LightRef>> getValueExternalizer() {
        return createLightRefSeqExternalizer();
      }

      @Override
      public int getVersion() {
        return VERSION;
      }
    };
  }

  @NotNull
  private static DataExternalizer<Collection<LightRef>> createLightRefSeqExternalizer() {
    return new DataExternalizer<Collection<LightRef>>() {
      @Override
      public void save(@NotNull final DataOutput out, Collection<LightRef> value) throws IOException {
        DataInputOutputUtilRt.writeSeq(out, value, lightRef -> LightRefDescriptor.INSTANCE.save(out, lightRef));
      }

      @Override
      public Collection<LightRef> read(@NotNull final DataInput in) throws IOException {
        return DataInputOutputUtilRt.readSeq(in, () -> LightRefDescriptor.INSTANCE.read(in));
      }
    };
  }

  private static KeyDescriptor<SignatureData> createSignatureDataDescriptor() {
    return new KeyDescriptor<SignatureData>() {
      @Override
      public int getHashCode(SignatureData value) {
        return value.hashCode();
      }

      @Override
      public boolean isEqual(SignatureData val1, SignatureData val2) {
        return val1.equals(val2);
      }

      @Override
      public void save(@NotNull DataOutput out, SignatureData value) throws IOException {
        DataInputOutputUtil.writeINT(out, value.getRawReturnType());
        out.writeBoolean(value.isStatic());
      }

      @Override
      public SignatureData read(@NotNull DataInput in) throws IOException {
        return new SignatureData(DataInputOutputUtil.readINT(in), in.readBoolean());
      }
    };
  }
}

@@ -1490,9 +1490,9 @@
 N = 
-3
+4
 ;%0A%0A 

increment index version