// Copyright (C) 2021 The Android Open Source Project
//
// 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.google.gerrit.server.approval;

import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.converter.PatchSetApprovalProtoConverter;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.proto.Entities;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.proto.Cache;
import com.google.gerrit.server.cache.serialize.ObjectIdCacheSerializer;
import com.google.gerrit.server.cache.serialize.ProtobufSerializer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.google.protobuf.ByteString;
import java.util.concurrent.ExecutionException;

/** @see ApprovalCache */
public class ApprovalCacheImpl implements ApprovalCache {
  private static final String CACHE_NAME = "approvals";

  public static Module module() {
    return new CacheModule() {
      @Override
      protected void configure() {
        bind(ApprovalCache.class).to(ApprovalCacheImpl.class);
        persist(
                CACHE_NAME,
                Cache.PatchSetApprovalsKeyProto.class,
                Cache.AllPatchSetApprovalsProto.class)
            .version(1)
            .loader(Loader.class)
            .keySerializer(new ProtobufSerializer<>(Cache.PatchSetApprovalsKeyProto.parser()))
            .valueSerializer(new ProtobufSerializer<>(Cache.AllPatchSetApprovalsProto.parser()));
      }
    };
  }

  private final LoadingCache<Cache.PatchSetApprovalsKeyProto, Cache.AllPatchSetApprovalsProto>
      cache;

  @Inject
  ApprovalCacheImpl(
      @Named(CACHE_NAME)
          LoadingCache<Cache.PatchSetApprovalsKeyProto, Cache.AllPatchSetApprovalsProto> cache) {
    this.cache = cache;
  }

  @Override
  public Iterable<PatchSetApproval> get(ChangeNotes notes, PatchSet.Id psId) {
    try {
      return fromProto(
          cache.get(
              Cache.PatchSetApprovalsKeyProto.newBuilder()
                  .setChangeId(notes.getChangeId().get())
                  .setPatchSetId(psId.get())
                  .setProject(notes.getProjectName().get())
                  .setId(
                      ByteString.copyFrom(
                          ObjectIdCacheSerializer.INSTANCE.serialize(notes.getMetaId())))
                  .build()));
    } catch (ExecutionException e) {
      throw new StorageException(e);
    }
  }

  @Singleton
  static class Loader
      extends CacheLoader<Cache.PatchSetApprovalsKeyProto, Cache.AllPatchSetApprovalsProto> {
    private final ApprovalInference approvalInference;
    private final ChangeNotes.Factory changeNotesFactory;

    @Inject
    Loader(ApprovalInference approvalInference, ChangeNotes.Factory changeNotesFactory) {
      this.approvalInference = approvalInference;
      this.changeNotesFactory = changeNotesFactory;
    }

    @Override
    public Cache.AllPatchSetApprovalsProto load(Cache.PatchSetApprovalsKeyProto key)
        throws Exception {
      Change.Id changeId = Change.id(key.getChangeId());
      return toProto(
          approvalInference.forPatchSet(
              changeNotesFactory.createChecked(
                  Project.nameKey(key.getProject()),
                  changeId,
                  ObjectIdCacheSerializer.INSTANCE.deserialize(key.getId().toByteArray())),
              PatchSet.id(changeId, key.getPatchSetId()),
              null
              /* revWalk= */ ,
              null
              /* repoConfig= */ ));
    }
  }

  private static Iterable<PatchSetApproval> fromProto(Cache.AllPatchSetApprovalsProto proto) {
    ImmutableList.Builder<PatchSetApproval> builder = ImmutableList.builder();
    for (Entities.PatchSetApproval psa : proto.getApprovalList()) {
      builder.add(PatchSetApprovalProtoConverter.INSTANCE.fromProto(psa));
    }
    return builder.build();
  }

  private static Cache.AllPatchSetApprovalsProto toProto(Iterable<PatchSetApproval> autoValue) {
    Cache.AllPatchSetApprovalsProto.Builder builder = Cache.AllPatchSetApprovalsProto.newBuilder();
    for (PatchSetApproval psa : autoValue) {
      builder.addApproval(PatchSetApprovalProtoConverter.INSTANCE.toProto(psa));
    }
    return builder.build();
  }
}

@@ -2153,17 +2153,17 @@
 version(
-1
+2
 )%0A      

Fix ApprovalCache version