/**
 * Copyright 2019 LinkedIn Corporation. All rights reserved.
 * Licensed under the BSD-2 Clause license.
 * See LICENSE in the project root for license information.
 */
package com.linkedin.transport.plugin.tasks;

import com.linkedin.transport.codegen.WrapperGenerator;
import com.linkedin.transport.codegen.WrapperGeneratorContext;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import java.util.stream.StreamSupport;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;


/**
 * A Gradle task which generates Transport UDF wrappers given a UDF metadata file and wrapper generator class
 */
public class GenerateWrappersTask extends DefaultTask {

  private final Property<String> _generatorClass;

  private final Property<FileCollection> _inputClassesDirs;

  private final DirectoryProperty _sourcesOutputDir;

  private final DirectoryProperty _resourcesOutputDir;

  public GenerateWrappersTask() {
    _generatorClass = getProject().getObjects().property(String.class);
    _inputClassesDirs = getProject().getObjects().property(FileCollection.class);
    _sourcesOutputDir = getProject().getObjects().directoryProperty();
    _resourcesOutputDir = getProject().getObjects().directoryProperty();
  }

  @Input
  public Property<String> getGeneratorClass() {
    return _generatorClass;
  }

  @InputFiles
  public Property<FileCollection> getInputClassesDirs() {
    return _inputClassesDirs;
  }

  @OutputDirectory
  public DirectoryProperty getSourcesOutputDir() {
    return _sourcesOutputDir;
  }

  @OutputDirectory
  public DirectoryProperty getResourcesOutputDir() {
    return _resourcesOutputDir;
  }

  @TaskAction
  public void generateWrappers() {
    // TODO: Use Gradle worker API to generate wrappers concurrently
    WrapperGenerator generator;
    try {
      generator = (WrapperGenerator) Class.forName(_generatorClass.get()).getConstructor().newInstance();
    } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
      throw new RuntimeException("Could not create object of class: " + _generatorClass.get(), e);
    }
    generator.generateWrappers(
        new WrapperGeneratorContext(getUDFMetadataFile(_inputClassesDirs.get()), _sourcesOutputDir.getAsFile().get(),
            _resourcesOutputDir.getAsFile().get()));
  }

  /**
   * Finds the location for the UDF metadata file generated by the annotation processor in the classes directories of
   * the input sources
   */
  private File getUDFMetadataFile(FileCollection inputClassesDirs) {
    Optional<File> udfMetadataFile = StreamSupport.stream(inputClassesDirs.spliterator(), true)
        .map(folder -> folder.toPath().resolve("META-INF/transport-udfs/metadata.json").toFile())
        .filter(File::exists)
        .findFirst();

    if (udfMetadataFile.isPresent()) {
      return udfMetadataFile.get();
    } else {
      throw new RuntimeException(
          "Could not find UDF metadata file in the input directories: " + inputClassesDirs.getFiles().toString());
    }
  }
}
