/*
 * 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 com.intellij.vcs.log.data;

import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Interner;
import com.intellij.util.io.*;
import com.intellij.vcs.log.VcsUser;
import com.intellij.vcs.log.VcsUserRegistry;
import com.intellij.vcs.log.impl.VcsUserImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

/**
 *
 */
public class VcsUserRegistryImpl implements Disposable, VcsUserRegistry {

  private static final File USER_CACHE_APP_DIR = new File(PathManager.getSystemPath(), "vcs-users");
  private static final Logger LOG = Logger.getInstance(VcsUserRegistryImpl.class);
  private static final int STORAGE_VERSION = 2;
  private static final PersistentEnumeratorBase.DataFilter ACCEPT_ALL_DATA_FILTER = id -> true;

  @Nullable private final PersistentEnumeratorBase<VcsUser> myPersistentEnumerator;
  @NotNull private final Interner<VcsUser> myInterner;

  VcsUserRegistryImpl(@NotNull Project project) {
    final File mapFile = new File(USER_CACHE_APP_DIR, project.getLocationHash() + "." + STORAGE_VERSION);
    myPersistentEnumerator = initEnumerator(mapFile);
    myInterner = new Interner<>();
  }

  @Nullable
  private PersistentEnumeratorBase<VcsUser> initEnumerator(@NotNull final File mapFile) {
    try {
      return IOUtil.openCleanOrResetBroken(() -> new PersistentBTreeEnumerator<>(mapFile, new MyDescriptor(), Page.PAGE_SIZE, null,
                                                                                 STORAGE_VERSION), mapFile);
    }
    catch (IOException e) {
      LOG.warn(e);
      return null;
    }
  }

  @NotNull
  @Override
  public VcsUser createUser(@NotNull String name, @NotNull String email) {
    synchronized (myInterner) {
      return myInterner.intern(new VcsUserImpl(name, email));
    }
  }

  public void addUser(@NotNull VcsUser user) {
    try {
      if (myPersistentEnumerator != null) {
        myPersistentEnumerator.enumerate(user);
      }
    }
    catch (IOException e) {
      LOG.warn(e);
    }
  }

  public void addUsers(@NotNull Collection<VcsUser> users) {
    for (VcsUser user : users) {
      addUser(user);
    }
  }

  @Override
  @NotNull
  public Set<VcsUser> getUsers() {
    try {
      Collection<VcsUser> users = myPersistentEnumerator != null ?
                                  myPersistentEnumerator.getAllDataObjects(ACCEPT_ALL_DATA_FILTER) :
                                  Collections.emptySet();
      return ContainerUtil.newHashSet(users);
    }
    catch (IOException e) {
      LOG.warn(e);
      return Collections.emptySet();
    }
  }

  public void flush() {
    if (myPersistentEnumerator != null) {
      myPersistentEnumerator.force();
    }
  }

  @Override
  public void dispose() {
    try {
      if (myPersistentEnumerator != null) {
        myPersistentEnumerator.close();
      }
    }
    catch (IOException e) {
      LOG.warn(e);
    }
  }

  public int getUserId(@NotNull VcsUser user) throws IOException {
    return myPersistentEnumerator.enumerate(user);
  }

  @Nullable
  public VcsUser getUserById(Integer userId) throws IOException {
    return myPersistentEnumerator.valueOf(userId);
  }

  private class MyDescriptor implements KeyDescriptor<VcsUser> {
    @Override
    public void save(@NotNull DataOutput out, VcsUser value) throws IOException {
      IOUtil.writeUTF(out, value.getName());
      IOUtil.writeUTF(out, value.getEmail());
    }

    @Override
    public VcsUser read(@NotNull DataInput in) throws IOException {
      String name = IOUtil.readUTF(in);
      String email = IOUtil.readUTF(in);
      return createUser(name, email);
    }

    @Override
    public int getHashCode(VcsUser value) {
      return value.hashCode();
    }

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

@@ -3951,32 +3951,76 @@
 s IOException %7B%0A
+    if (myPersistentEnumerator != null) %7B%0A  
     return myPer
@@ -4054,16 +4054,37 @@
 (user);%0A
+    %7D%0A    return -1;%0A
   %7D%0A%0A  @
@@ -4154,24 +4154,68 @@
 Exception %7B%0A
+    if (myPersistentEnumerator != null) %7B%0A  
     return m
@@ -4253,16 +4253,39 @@
 serId);%0A
+    %7D%0A    return null;%0A
   %7D%0A%0A  p

check nullable field for null