// Copyright (C) 2017 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.project;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.gerrit.entities.BooleanProjectConfig;
import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInfo.InheritedBooleanInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
import java.util.Arrays;
import java.util.HashSet;

/** Provides transformations to get and set BooleanProjectConfigs from the API. */
public class BooleanProjectConfigTransformations {

  private static final ImmutableMap<BooleanProjectConfig, Mapper> MAPPER =
      ImmutableMap.<BooleanProjectConfig, Mapper>builder()
          .put(
              BooleanProjectConfig.USE_CONTRIBUTOR_AGREEMENTS,
              new Mapper(i -> i.useContributorAgreements, (i, v) -> i.useContributorAgreements = v))
          .put(
              BooleanProjectConfig.USE_SIGNED_OFF_BY,
              new Mapper(i -> i.useSignedOffBy, (i, v) -> i.useSignedOffBy = v))
          .put(
              BooleanProjectConfig.USE_CONTENT_MERGE,
              new Mapper(i -> i.useContentMerge, (i, v) -> i.useContentMerge = v))
          .put(
              BooleanProjectConfig.REQUIRE_CHANGE_ID,
              new Mapper(i -> i.requireChangeId, (i, v) -> i.requireChangeId = v))
          .put(
              BooleanProjectConfig.CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET,
              new Mapper(
                  i -> i.createNewChangeForAllNotInTarget,
                  (i, v) -> i.createNewChangeForAllNotInTarget = v))
          .put(
              BooleanProjectConfig.ENABLE_SIGNED_PUSH,
              new Mapper(i -> i.enableSignedPush, (i, v) -> i.enableSignedPush = v))
          .put(
              BooleanProjectConfig.REQUIRE_SIGNED_PUSH,
              new Mapper(i -> i.requireSignedPush, (i, v) -> i.requireSignedPush = v))
          .put(
              BooleanProjectConfig.REJECT_IMPLICIT_MERGES,
              new Mapper(i -> i.rejectImplicitMerges, (i, v) -> i.rejectImplicitMerges = v))
          .put(
              BooleanProjectConfig.PRIVATE_BY_DEFAULT,
              new Mapper(i -> i.privateByDefault, (i, v) -> i.privateByDefault = v))
          .put(
              BooleanProjectConfig.ENABLE_REVIEWER_BY_EMAIL,
              new Mapper(i -> i.enableReviewerByEmail, (i, v) -> i.enableReviewerByEmail = v))
          .put(
              BooleanProjectConfig.MATCH_AUTHOR_TO_COMMITTER_DATE,
              new Mapper(
                  i -> i.matchAuthorToCommitterDate, (i, v) -> i.matchAuthorToCommitterDate = v))
          .put(
              BooleanProjectConfig.REJECT_EMPTY_COMMIT,
              new Mapper(i -> i.rejectEmptyCommit, (i, v) -> i.rejectEmptyCommit = v))
          .put(
              BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT,
              new Mapper(i -> i.workInProgressByDefault, (i, v) -> i.workInProgressByDefault = v))
          .build();

  static {
    // Verify that each BooleanProjectConfig has to/from API mappers in
    // BooleanProjectConfigTransformations
    if (!Sets.symmetricDifference(
            MAPPER.keySet(), new HashSet<>(Arrays.asList(BooleanProjectConfig.values())))
        .isEmpty()) {
      throw new IllegalStateException(
          "All values of BooleanProjectConfig must have transformations associated with them");
    }
  }

  @FunctionalInterface
  private interface ToApi {
    void apply(ConfigInfo info, InheritedBooleanInfo val);
  }

  @FunctionalInterface
  private interface FromApi {
    InheritableBoolean apply(ConfigInput input);
  }

  public static void set(BooleanProjectConfig cfg, ConfigInfo info, InheritedBooleanInfo val) {
    MAPPER.get(cfg).set(info, val);
  }

  public static InheritableBoolean get(BooleanProjectConfig cfg, ConfigInput input) {
    return MAPPER.get(cfg).get(input);
  }

  private static class Mapper {
    private final FromApi fromApi;
    private final ToApi toApi;

    private Mapper(FromApi fromApi, ToApi toApi) {
      this.fromApi = fromApi;
      this.toApi = toApi;
    }

    public void set(ConfigInfo info, InheritedBooleanInfo val) {
      toApi.apply(info, val);
    }

    public InheritableBoolean get(ConfigInput input) {
      return fromApi.apply(input);
    }
  }
}
