// Copyright 2020 Google LLC
//
// 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.api.generator.gapic.composer;

import com.google.api.generator.engine.ast.ClassDefinition;
import com.google.api.generator.engine.ast.ScopeNode;
import com.google.api.generator.gapic.model.GapicClass;
import com.google.api.generator.gapic.model.GapicClass.Kind;
import com.google.api.generator.gapic.model.GapicContext;
import com.google.api.generator.gapic.model.GapicServiceConfig;
import com.google.api.generator.gapic.model.Message;
import com.google.api.generator.gapic.model.ResourceName;
import com.google.api.generator.gapic.model.Service;
import com.google.common.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class Composer {
  public static List<GapicClass> composeServiceClasses(GapicContext context) {
    List<GapicClass> clazzes = new ArrayList<>();
    for (Service service : context.services()) {
      clazzes.addAll(
          generateServiceClasses(
              service, context.serviceConfig(), context.resourceNames(), context.messages()));
    }
    clazzes.addAll(generateResourceNameHelperClasses(context.helperResourceNames()));
    return addApacheLicense(clazzes);
  }

  public static List<GapicClass> generateServiceClasses(
      @Nonnull Service service,
      @Nullable GapicServiceConfig serviceConfig,
      @Nonnull Map<String, ResourceName> resourceNames,
      @Nonnull Map<String, Message> messageTypes) {
    List<GapicClass> clazzes = new ArrayList<>();
    clazzes.addAll(generateStubClasses(service, serviceConfig, messageTypes));
    clazzes.addAll(generateClientSettingsClasses(service, messageTypes));
    clazzes.addAll(generateMocksAndTestClasses(service, resourceNames, messageTypes));
    // TODO(miraleung): Generate test classes.
    return clazzes;
  }

  public static List<GapicClass> generateResourceNameHelperClasses(
      Set<ResourceName> resourceNames) {
    return resourceNames.stream()
        .filter(r -> !r.isOnlyWildcard())
        .map(r -> ResourceNameHelperClassComposer.instance().generate(r))
        .collect(Collectors.toList());
  }

  public static List<GapicClass> generateStubClasses(
      Service service, GapicServiceConfig serviceConfig, Map<String, Message> messageTypes) {
    List<GapicClass> clazzes = new ArrayList<>();
    clazzes.add(ServiceStubClassComposer.instance().generate(service, messageTypes));
    clazzes.add(
        ServiceStubSettingsClassComposer.instance().generate(service, serviceConfig, messageTypes));
    clazzes.add(GrpcServiceCallableFactoryClassComposer.instance().generate(service, messageTypes));
    clazzes.add(GrpcServiceStubClassComposer.instance().generate(service, messageTypes));
    return clazzes;
  }

  public static List<GapicClass> generateClientSettingsClasses(
      Service service, Map<String, Message> messageTypes) {
    List<GapicClass> clazzes = new ArrayList<>();
    clazzes.add(ServiceClientClassComposer.instance().generate(service, messageTypes));
    clazzes.add(ServiceSettingsClassComposer.instance().generate(service, messageTypes));
    return clazzes;
  }

  public static List<GapicClass> generateMocksAndTestClasses(
      Service service, Map<String, ResourceName> resourceNames, Map<String, Message> messageTypes) {
    List<GapicClass> clazzes = new ArrayList<>();
    clazzes.add(MockServiceClassComposer.instance().generate(service, messageTypes));
    clazzes.add(MockServiceImplClassComposer.instance().generate(service, messageTypes));
    clazzes.add(
        ServiceClientTestClassComposer.instance().generate(service, resourceNames, messageTypes));
    return clazzes;
  }

  /** ====================== HELPERS ==================== */
  // TODO(miraleung): Add method list.
  private static GapicClass generateGenericClass(Kind kind, String name, Service service) {
    String pakkage = service.pakkage();
    if (kind.equals(Kind.STUB)) {
      pakkage += ".stub";
    }

    ClassDefinition classDef =
        ClassDefinition.builder()
            .setPackageString(pakkage)
            .setName(name)
            .setScope(ScopeNode.PUBLIC)
            .build();
    return GapicClass.create(kind, classDef);
  }

  @VisibleForTesting
  protected static List<GapicClass> addApacheLicense(List<GapicClass> gapicClassList) {
    return gapicClassList.stream()
        .map(
            gapicClass -> {
              ClassDefinition classWithHeader =
                  gapicClass
                      .classDefinition()
                      .toBuilder()
                      .setFileHeader(CommentComposer.APACHE_LICENSE_COMMENT)
                      .build();
              return GapicClass.create(gapicClass.kind(), classWithHeader);
            })
        .collect(Collectors.toList());
  }
}

@@ -1229,24 +1229,50 @@
 .ArrayList;%0A
+import java.util.HashMap;%0A
 import java.
@@ -1278,24 +1278,24 @@
 .util.List;%0A
-
 import java.
@@ -1579,24 +1579,255 @@
 ayList%3C%3E();%0A
+    Map%3CString, ResourceName%3E availableResourceNames = new HashMap%3C%3E();%0A    for (ResourceName resourceName : context.helperResourceNames()) %7B%0A      availableResourceNames.put(resourceName.resourceTypeString(), resourceName);%0A    %7D%0A
     for (Ser
@@ -1971,17 +1971,18 @@
 (), 
-context.r
+availableR
 esou
@@ -1989,18 +1989,16 @@
 rceNames
-()
 , contex

use only generated resnames in codegen (#409)