/*
 * Copyright 2015-present Facebook, Inc.
 *
 * 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.facebook.buck.features.dotnet;

import com.facebook.buck.core.build.buildable.context.BuildableContext;
import com.facebook.buck.core.build.context.BuildContext;
import com.facebook.buck.core.model.BuildTarget;
import com.facebook.buck.core.model.impl.BuildTargetPaths;
import com.facebook.buck.core.rulekey.AddToRuleKey;
import com.facebook.buck.core.rules.BuildRule;
import com.facebook.buck.core.rules.BuildRuleParams;
import com.facebook.buck.core.rules.impl.AbstractBuildRuleWithDeclaredAndExtraDeps;
import com.facebook.buck.core.sourcepath.ExplicitBuildTargetSourcePath;
import com.facebook.buck.core.sourcepath.SourcePath;
import com.facebook.buck.core.sourcepath.resolver.SourcePathResolverAdapter;
import com.facebook.buck.core.toolchain.tool.Tool;
import com.facebook.buck.io.BuildCellRelativePath;
import com.facebook.buck.io.filesystem.ProjectFilesystem;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.util.types.Either;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;

public class CsharpLibrary extends AbstractBuildRuleWithDeclaredAndExtraDeps {

  @AddToRuleKey(stringify = true)
  private final Path output;

  @AddToRuleKey private final Tool csharpCompiler;
  @AddToRuleKey private final ImmutableSortedSet<SourcePath> srcs;
  @AddToRuleKey private final ImmutableList<Either<BuildRule, String>> refs;
  @AddToRuleKey private final ImmutableMap<String, SourcePath> resources;
  @AddToRuleKey private final FrameworkVersion version;
  @AddToRuleKey private final ImmutableList<String> compilerFlags;

  protected CsharpLibrary(
      BuildTarget buildTarget,
      ProjectFilesystem projectFilesystem,
      BuildRuleParams params,
      Tool csharpCompiler,
      String dllName,
      ImmutableSortedSet<SourcePath> srcs,
      ImmutableList<Either<BuildRule, String>> refs,
      ImmutableMap<String, SourcePath> resources,
      FrameworkVersion version,
      ImmutableList<String> compilerFlags) {
    super(buildTarget, projectFilesystem, params);

    Preconditions.checkArgument(dllName.endsWith(".dll"));

    this.csharpCompiler = csharpCompiler;
    this.srcs = srcs;
    this.refs = refs;
    this.resources = resources;
    this.version = version;

    this.output = BuildTargetPaths.getGenPath(getProjectFilesystem(), buildTarget, "%s/" + dllName);
    this.compilerFlags = compilerFlags;
  }

  @Override
  public ImmutableList<Step> getBuildSteps(
      BuildContext context, BuildableContext buildableContext) {
    ProjectFilesystem filesystem = getProjectFilesystem();

    ImmutableSortedSet<Path> sourceFiles =
        context.getSourcePathResolver().getAllAbsolutePaths(srcs);

    ImmutableListMultimap.Builder<Path, String> resolvedResources = ImmutableListMultimap.builder();
    for (Map.Entry<String, SourcePath> resource : resources.entrySet()) {
      resolvedResources.put(
          context.getSourcePathResolver().getAbsolutePath(resource.getValue()), resource.getKey());
    }

    ImmutableList<Either<Path, String>> references =
        resolveReferences(context.getSourcePathResolver(), refs);

    ImmutableList.Builder<Step> steps = ImmutableList.builder();

    steps.addAll(
        MakeCleanDirectoryStep.of(
            BuildCellRelativePath.fromCellRelativePath(
                context.getBuildCellRootPath(), getProjectFilesystem(), output.getParent())));
    steps.add(
        new CsharpLibraryCompile(
            context.getSourcePathResolver(),
            csharpCompiler,
            filesystem.resolve(output),
            sourceFiles,
            references,
            resolvedResources.build(),
            version,
            compilerFlags));

    buildableContext.recordArtifact(output);

    return steps.build();
  }

  private ImmutableList<Either<Path, String>> resolveReferences(
      SourcePathResolverAdapter pathResolver, ImmutableList<Either<BuildRule, String>> refs) {
    ImmutableList.Builder<Either<Path, String>> resolved = ImmutableList.builder();

    for (Either<BuildRule, String> ref : refs) {
      if (ref.isLeft()) {
        // TODO(simons): Do this in the constructor? Or the Description?
        BuildRule rule = ref.getLeft();
        Preconditions.checkArgument(
            rule instanceof CsharpLibrary || rule instanceof PrebuiltDotnetLibrary);

        SourcePath outputPath = Objects.requireNonNull(rule.getSourcePathToOutput());
        resolved.add(Either.ofLeft(pathResolver.getAbsolutePath(outputPath)));
      } else {
        resolved.add(Either.ofRight(ref.getRight()));
      }
    }

    return resolved.build();
  }

  @Override
  public SourcePath getSourcePathToOutput() {
    return ExplicitBuildTargetSourcePath.of(getBuildTarget(), output);
  }
}

@@ -1118,16 +1118,90 @@
 raDeps;%0A
+import com.facebook.buck.core.rules.impl.RuleAnalysisLegacyBuildRuleView;%0A
 import c
@@ -2014,16 +2014,43 @@
 Objects;
+%0Aimport java.util.Optional;
 %0A%0Apublic
@@ -5204,37 +5204,538 @@
     
-Preconditions.checkArgument(%0A
+if (rule instanceof RuleAnalysisLegacyBuildRuleView) %7B%0A          Optional%3CDotnetLibraryProviderInfo%3E dotnet =%0A              ((RuleAnalysisLegacyBuildRuleView) rule)%0A                  .getProviderInfos()%0A                  .get(DotnetLibraryProviderInfo.PROVIDER);%0A          Preconditions.checkArgument(dotnet.isPresent());%0A%0A          resolved.add(%0A              Either.ofLeft(%0A                  pathResolver.getAbsolutePath(dotnet.get().dll().asBound().getSourcePath())));%0A        %7D else %7B%0A          Preconditions.checkArgument(%0A  
     
@@ -5812,24 +5812,26 @@
 tLibrary);%0A%0A
+  
         Sour
@@ -5904,32 +5904,34 @@
 put());%0A        
+ 
+ 
 resolved.add(Eit
@@ -5981,24 +5981,35 @@
 putPath)));%0A
+        %7D%0A%0A
       %7D else

Make CsharpLibrary handle providers coming up from dotnet