/*
 * Copyright 2020 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.plugins.jvm.internal;

import org.gradle.api.Named;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConsumableConfiguration;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.internal.component.external.model.ImmutableCapabilities;
import org.jspecify.annotations.Nullable;

/**
 * A Jvm Feature wraps a source set to encapsulate the logic and domain objects required to
 * implement a feature of a JVM component. Features are used to model constructs like
 * production libraries, test suites, test fixtures, applications, etc. While features are not
 * individually consumable themselves for publication or through dependency-management,
 * they can be exposed to consumers via an owning component.
 *
 * <p>Features are classified by their capabilities. Each variant of a feature provides at least
 * the same set of capabilities as the feature itself. Since all variants of a feature are derived
 * from the same sources, they all expose the same API or "content" and thus provide the same
 * capabilities. Some variants may expose additional capabilities than those of its owning feature,
 * for example with fat jars.</p>
 *
 * <p>TODO: The current API is written as if this were a single-target feature. Before we make this API
 * public, we should make this API multi-target aware. Alternatively, we could implement a
 * SingleTargetJvmFeature now and in the future implement a MultiTargetJvmFeature when we're ready.
 * This would allow us to use the JvmFeature interface as a common parent interface.</p>
 */
public interface JvmFeatureInternal extends Named {

    /**
     * Get the capabilities of this feature. All variants exposed by this feature must provide at least
     * the same capabilities as this feature.
     */
    ImmutableCapabilities getCapabilities();

    /**
     * Configures this feature to publish a variant containing a list of this feature's source directories.
     */
    void withSourceElements();

    /**
     * Adds the {@code api} and {@code compileOnlyApi} dependency configurations to this feature.
     *
     * TODO: Should this live on the "base" JVM feature? Should all JVM features know how to add
     * an API? Or should we have subclasses which have APIs and others, which support
     * application features and test suites, which do not have APIs?
     */
    void withApi();

    /**
     * Creates the javadoc elements configuration, which exposes the javadoc jar of this feature, if
     * it has not been created already. Any tasks needed to produce the artifacts of the configuration
     * are also created and configured if necessary.
     */
    NamedDomainObjectProvider<ConsumableConfiguration> maybeRegisterJavadocElements();

    /**
     * Creates the sources elements configuration, which exposes the sources jar of this feature, if
     * it has not been created already. Any tasks needed to produce the artifacts of the configuration
     * are also created and configured if necessary.
     */
    NamedDomainObjectProvider<ConsumableConfiguration> maybeRegisterSourcesElements();

    // TODO: Many of the methods below probably belong on a JvmTarget. Features may have many targets
    // and thus many configurations, jar tasks, compile tasks, etc.

    /**
     * Get the {@link Jar} task which assembles the resources and compilation outputs into
     * a single artifact.
     *
     * @return A provider which supplies the feature's {@link Jar} task.
     */
    TaskProvider<Jar> getJarTask();

    /**
     * Get the {@link JavaCompile} task which compiles the Java source files into classes.
     *
     * @return A provider which supplies the feature's {@link JavaCompile} task.
     */
    TaskProvider<JavaCompile> getCompileJavaTask();

    /**
     * Get this feature's backing source set.
     * <p>
     * {@link SourceSet#getOutput()} and the classpath-returning methods on the returned
     * source set should ideally be avoided in favor of the similarly-named methods on
     * this feature. The concept of source sets having a single set of outputs is only
     * relevant for single-target features.
     *
     * @return This feature's source set.
     */
    SourceSet getSourceSet();

    /**
     * Gets the dependency configuration for which to declare dependencies internal to the feature.
     * Dependencies declared on this configuration are present during compilation and runtime, but are not
     * exposed as part of the feature's API variant.
     *
     * @return The {@code implementation} configuration.
     */
    Configuration getImplementationConfiguration();

    /**
     * Gets the dependency configuration for which to declare runtime-only dependencies.
     * Dependencies declared on this configuration are present only during runtime, are not
     * present during compilation, and are not exposed as part of the feature's API variant.
     *
     * @return The {@code runtimeOnly} configuration.
     */
    Configuration getRuntimeOnlyConfiguration();

    /**
     * Gets the dependency configuration for which to declare compile-only dependencies.
     * Dependencies declared on this configuration are present only during compilation, are not
     * present during runtime, and are not exposed as part of the feature's API variant.
     *
     * @return The {@code compileOnly} configuration.
     */
    Configuration getCompileOnlyConfiguration();

    /**
     * Gets the dependency configuration for which to declare API dependencies.
     * Dependencies declared on this configuration are present during compilation
     * and runtime, and are exposed as part of the feature's API variant.
     *
     * @return null if {@link #withApi()} has not been called.
     */
    @Nullable
    Configuration getApiConfiguration();

    /**
     * Gets the dependency configuration for which to declare compile-only API dependencies.
     * Dependencies declared on this configuration are present during compilation
     * but not runtime, and are exposed as part of the feature's API variant.
     *
     * @return null if {@link #withApi()} has not been called.
     */
    @Nullable
    Configuration getCompileOnlyApiConfiguration();

    /**
     * Get the resolvable configuration containing the resolved runtime dependencies
     * for this feature. This configuration does not contain the artifacts from the
     * feature's compilation itself.
     *
     * @return The {@code runtimeClasspath} configuration.
     */
    Configuration getRuntimeClasspathConfiguration();

    /**
     * Get the resolvable configuration containing the resolved compile dependencies
     * for this feature.
     *
     * @return The {@code compileClasspath} configuration.
     */
    Configuration getCompileClasspathConfiguration();

    /**
     * Get the consumable configuration which produces the {@code apiElements} variant of this feature.
     * This configuration includes all API compilation dependencies as well as the feature's
     * compilation outputs, but does not include {@code implementation}, {@code compileOnly},
     * or {@code runtimeOnly} dependencies.
     *
     * @return The {@code apiElements} configuration.
     */
    NamedDomainObjectProvider<ConsumableConfiguration> getApiElementsConfiguration();

    /**
     * Get the consumable configuration which produces the {@code runtimeElements} variant of this feature.
     * This configuration includes all runtime dependencies as well as the feature's
     * compilation outputs, but does not include {@code compileOnly} dependencies.
     *
     * @return The {@code runtimeElements} configuration.
     */
    NamedDomainObjectProvider<ConsumableConfiguration> getRuntimeElementsConfiguration();

}
