/*
 * Copyright 2021 the original author or authors.
 *
 * 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 org.gradle.api.internal.artifacts.transform;

import com.google.common.collect.ImmutableList;

import java.io.File;

/**
 * The result of running a transformation.
 *
 * The result of running a transformation is a list of outputs.
 * There are two kinds of outputs for a transformation:
 * - Produced outputs in the workspace. Those are absolute paths which do not change depending on the input artifact.
 * - Selected parts of the input artifact. These are relative paths of locations selected in the input artifact.
 */
public interface TransformationResult {
    /**
     * Resolves location of the outputs of this results for a given input artifact.
     *
     * Produced outputs don't need to be resolved to locations, since they are absolute paths and can be returned as is.
     * The relative paths of selected parts of the input artifact need to resolved based on the provided input artifact location.
     */
    ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact);

    static Builder builder() {
        return new Builder();
    }

    class Builder {
        private final ImmutableList.Builder<TransformationOutput> builder = ImmutableList.builder();
        boolean onlyProducedOutputs = true;

        public void addInputArtifact(String relativePath) {
            onlyProducedOutputs = false;
            builder.add(new InInputArtifact(relativePath));
        }

        public void addInputArtifact() {
            onlyProducedOutputs = false;
            builder.add(new InputArtifact());
        }

        public void addOutput(File outputLocation) {
            builder.add(new ProducedOutput(outputLocation));
        }

        public TransformationResult build() {
            ImmutableList<TransformationOutput> transformationOutputs = builder.build();
            return onlyProducedOutputs
                ? new OnlyProducedOutputTransformationResult(convertToProducedOutputLocations(transformationOutputs))
                : new DefaultTransformationResult(transformationOutputs);
        }

        private interface TransformationOutput {
            File resolveForInputArtifact(File inputArtifact);
        }

        private static ImmutableList<File> convertToProducedOutputLocations(ImmutableList<TransformationOutput> transformationOutputs) {
            ImmutableList.Builder<File> builder = new ImmutableList.Builder<>();
            transformationOutputs.forEach(output -> builder.add(((ProducedOutput) output).getOutputLocation()));
            return builder.build();
        }

        private static class OnlyProducedOutputTransformationResult implements TransformationResult {
            private final ImmutableList<File> producedOutputLocations;

            public OnlyProducedOutputTransformationResult(ImmutableList<File> producedOutputLocations) {
                this.producedOutputLocations = producedOutputLocations;
            }

            @Override
            public ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact) {
                return producedOutputLocations;
            }
        }

        private static class DefaultTransformationResult implements TransformationResult {
            private final ImmutableList<TransformationOutput> transformationOutputs;

            public DefaultTransformationResult(ImmutableList<TransformationOutput> transformationOutputs) {
                this.transformationOutputs = transformationOutputs;
            }

            @Override
            public ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact) {
                ImmutableList.Builder<File> builder = ImmutableList.builderWithExpectedSize(transformationOutputs.size());
                transformationOutputs.forEach(output -> builder.add(output.resolveForInputArtifact(inputArtifact)));
                return builder.build();
            }
        }

        private static class InInputArtifact implements TransformationOutput {
            private final String relativePath;

            public InInputArtifact(String relativePath) {
                this.relativePath = relativePath;
            }

            @Override
            public File resolveForInputArtifact(File inputArtifact) {
                return new File(inputArtifact, relativePath);
            }
        }

        private static class InputArtifact implements TransformationOutput {
            @Override
            public File resolveForInputArtifact(File inputArtifact) {
                return inputArtifact;
            }
        }

        private static class ProducedOutput implements TransformationOutput {
            private final File outputFile;

            public ProducedOutput(File outputFile) {
                this.outputFile = outputFile;
            }

            public File getOutputLocation() {
                return outputFile;
            }

            @Override
            public File resolveForInputArtifact(File inputArtifact) {
                return outputFile;
            }
        }
    }
}

@@ -1812,16 +1812,24 @@
         
+private 
 boolean 
@@ -1986,18 +1986,22 @@
 add(new 
-In
+PartOf
 InputArt
@@ -2139,20 +2139,22 @@
 der.add(
-new 
+Entire
 InputArt
@@ -2158,18 +2158,25 @@
 Artifact
-()
+.INSTANCE
 );%0A     
@@ -2502,34 +2502,31 @@
   ? new 
-OnlyProducedOutput
+AlreadyResolved
 Transfor
@@ -2617,23 +2617,25 @@
   : new 
-Default
+Resolving
 Transfor
@@ -2674,32 +2674,436 @@
 ts);%0A        %7D%0A%0A
+        /**%0A         * A single output in a transformation result.%0A         *%0A         * Can be either%0A         * - the entire input artifact %7B@link EntireInputArtifact%7D%0A         * - a part of the input artifact %7B@link PartOfInputArtifact%7D%0A         * - a produced output in the workspace %7B@link ProducedOutput%7D%0A         *%0A         * Only outputs related to the input artifact need resolving.%0A         */%0A
         private 
@@ -3615,34 +3615,31 @@
 c class 
-OnlyProducedOutput
+AlreadyResolved
 Transfor
@@ -3780,26 +3780,23 @@
 lic 
-OnlyProducedOutput
+AlreadyResolved
 Tran
@@ -4162,23 +4162,25 @@
 c class 
-Default
+Resolving
 Transfor
@@ -4335,15 +4335,17 @@
 lic 
-Default
+Resolving
 Tran
@@ -4953,18 +4953,22 @@
 c class 
-In
+PartOf
 InputArt
@@ -5074,18 +5074,22 @@
  public 
-In
+PartOf
 InputArt
@@ -5390,16 +5390,22 @@
 c class 
+Entire
 InputArt
@@ -5436,32 +5436,123 @@
 rmationOutput %7B%0A
+            public static final EntireInputArtifact INSTANCE = new EntireInputArtifact();%0A%0A
             @Ove

Address review comments for TransformationResult