/**
 * Copyright 2018 The original 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 io.dekorate.project;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import io.dekorate.DekorateException;

public class SbtInfoReader implements BuildInfoReader {

  private static final String BUILD_SBT = "build.sbt";
  private static final String SBT = "sbt";

  private static final String NAME = "name";
  private static final String VERSION = "version";
  private static final String SCALA_VERSION = "scalaVersion";
  private static final String SET = ":=";
  private static final String TARGET = "target";
  private static final String CLASSES = "classes";

  private static final String DOUBLE_QUOTE = "\"";
  private static final String DASH = "-";
  private static final String DOT = ".";
  private static final String UNDERSCORE = "_";
  private static final String NEWLINE = "\n";

  protected static final String JAR = "jar";

  protected static final String DEFAULT_VERSION = "0.1.0-SNAPSHOT";
  protected static final String DEFAULT_SCALA_VERSION = "2.12";

  private static final String[] SCALA_VERSION_CMD = new String[] { "scala", "-version" };
  private static final String VERSION_PATTERN = "^(\\d+\\.\\d+\\.\\d+).*";

  private static final int MAJOR = 0;
  private static final int MINOR = 1;

  @Override
  public int order() {
    return 400;
  }

  @Override
  public boolean isApplicable(Path root) {
    return root.resolve(BUILD_SBT).toFile().exists();
  }

  @Override
  public BuildInfo getInfo(Path root) {
    Path gradlePath = root.resolve(BUILD_SBT);
    Map<String, String> properties = new HashMap<>();

    try {
      Files.lines(gradlePath).map(l -> l.replaceAll("[ ]*", "")).filter(l -> l.contains(SET)).forEach(l -> {
        String key = l.substring(0, l.lastIndexOf(SET));
        String value = l.substring(l.lastIndexOf(SET) + 2).replaceAll(DOUBLE_QUOTE, "");
        properties.put(key, value);
      });
    } catch (IOException e) {
      throw DekorateException.launderThrowable(e);
    }

    String name = properties.getOrDefault(NAME, root.getFileName().toString());
    String version = properties.getOrDefault(VERSION, DEFAULT_VERSION);
    String scalaVersion = properties.getOrDefault(SCALA_VERSION, getSystemScalaVersion());
    String extension = JAR;
    Path outputFile = root.resolve(TARGET).resolve(name + UNDERSCORE + scalaVersion + DASH + version + DOT + extension);
    Path resourceOutputDir = root.resolve(TARGET).resolve(CLASSES);

    return new BuildInfoBuilder()
        .withName(name)
        .withVersion(version)
        .withPackaging(JAR)
        .withBuildTool(SBT)
        .withBuildToolVersion(null) //TODO: Implement at some point
        .withOutputFile(outputFile)
        .withResourceDir(resourceOutputDir)
        .build();
  }

  /**
   * Get the system scala version.
   * 
   * @return The scala version found using exec, or fallback to the default version.
   */
  protected static String getSystemScalaVersion() {
    try {
      Process p = new ProcessBuilder()
          .command(SCALA_VERSION_CMD)
          .redirectErrorStream(true)
          .start();

      BufferedReader buffer = new BufferedReader(new InputStreamReader(p.getInputStream()));
      String output = buffer.lines().collect(Collectors.joining(NEWLINE));
      String fullVersion = Arrays.stream(output.split(" ")).filter(w -> w.matches(VERSION_PATTERN)).findFirst()
          .orElse(DEFAULT_VERSION);
      String[] version = fullVersion.split("\\.");
      if (version.length >= 2) {
        return version[MAJOR] + DOT + version[MINOR];
      }
    } catch (IOException e) {
      return DEFAULT_SCALA_VERSION;
    }
    return DEFAULT_VERSION;
  }
}
