/*
 * Copyright 2000-2010 JetBrains s.r.o.
 *
 * 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 jetbrains.buildServer.eclipse.agent.builder;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import jetbrains.buildServer.RunBuildException;
import jetbrains.buildServer.agent.BuildRunnerContext;
import jetbrains.buildServer.agent.runner.ProgramCommandLine;
import jetbrains.buildServer.agent.runner.SimpleProgramCommandLine;
import jetbrains.buildServer.eclipse.EclipseConstants;
import jetbrains.buildServer.eclipse.agent.EclipseBuilderFactory;
import jetbrains.buildServer.eclipse.agent.IBuilder;
import jetbrains.buildServer.eclipse.java.BuildDescriptor;
import jetbrains.buildServer.eclipse.java.PlatformDescriptor;
import jetbrains.buildServer.util.FileUtil;

import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;

public class FeatureBasedProjectBuilder implements IBuilder {

  private final static Logger LOG = Logger.getLogger(FeatureBasedProjectBuilder.class);

  private PlatformDescriptor myPlatform;
  private BuildDescriptor myBuildDescriptor;
  private BuildRunnerContext myContext;

  private File myOutputFolder;

  private File myCopiedPropertiesFile;

  private HashMap<String, String> myCommandLineSystemProperties;

  private String myQualifier;

  public FeatureBasedProjectBuilder(final @NotNull BuildRunnerContext buildRunnerContext, final @NotNull PlatformDescriptor platform, final @NotNull BuildDescriptor buildDescriptor) {
    myPlatform = platform;
    myBuildDescriptor = buildDescriptor;
    myContext = buildRunnerContext;
  }

  public String getName() {
    return "PDE builder";
  }

  protected PlatformDescriptor getBuildPlatform() {
    return myPlatform;
  }

  protected BuildDescriptor getBuildDescriptor() {
    return myBuildDescriptor;
  }

  protected File getOutputFolder() {
    if (myOutputFolder == null) {
      final String outputParam = myContext.getRunnerParameters().get(EclipseConstants.SETTINGS_ARTIFACTS_OUTPUT_PATH);
      myOutputFolder = new File(myContext.getWorkingDirectory(), outputParam);
    }
    return myOutputFolder;
  }

  protected String getQualifier() {
    if (myQualifier == null) {
      final String qualifier = myContext.getRunnerParameters().get(EclipseConstants.SETTINGS_OPTIONS_CONTEXT_QUALIFIER);
      if (qualifier != null && qualifier.trim().length() > 0) {
        myQualifier = qualifier.trim();
      }
    }
    return myQualifier;
  }

  protected String getJavacSource() {
    return myBuildDescriptor.getJavacSource();
  }

  protected String getJavacTarget() {
    return myBuildDescriptor.getJavacTarget();
  }

  protected Map<String, String> getCommandLineSystemProperties() {
    if (myCommandLineSystemProperties == null) {
      myCommandLineSystemProperties = new HashMap<String, String>();
    }
    return myCommandLineSystemProperties;
  }

  protected ProgramCommandLine makeCommandLinesFor(final @NotNull String topLevelType, final @NotNull String topLevelTypeId) {
    final LinkedList<String> args = new LinkedList<String>();
    //launching
    args.add("-jar");
    args.add(String.format("\"%s\"", myPlatform.getEquinoxLauncherJar().getAbsolutePath()));
    LOG.debug(String.format("Use '%s' as Launcher", myPlatform.getEquinoxLauncherJar().getAbsolutePath()));
    args.add("-application");
    args.add("org.eclipse.ant.core.antRunner");
    args.add("-buildfile");
    args.add(String.format("\"%s/scripts/build.xml\"", myPlatform.getPDEBuilderFolder()));
    LOG.debug(String.format("Use '%s' as Builder", myPlatform.getPDEBuilderFolder()));
    //TODO: move next to default properties of UI settings?
    final Map<String, String> cliSysProps = loadUIDefined(loadCustom(loadDefault(getCommandLineSystemProperties())));
    //target element
    cliSysProps.put("topLevelElementType", topLevelType);
    cliSysProps.put("topLevelElementId", topLevelTypeId);
    //fit command line
    populateSystemPropertiesParams(args, cliSysProps);
    return new SimpleProgramCommandLine(myContext, EclipseBuilderFactory.getJavaExecutable(myContext), args);

  }

  public ProgramCommandLine[] makeCommandLines() {
    return new ProgramCommandLine[] { makeCommandLinesFor(myBuildDescriptor.getType(), myBuildDescriptor.getTypeId()[0]) };
  }

  private Map<String, String> loadDefault(Map<String, String> props) {
    //load build.properties file of PDE builder
    return props;
  }

  private Map<String, String> loadCustom(Map<String, String> props) {
    //load build.properties from a working folder
    return props;
  }

  private Map<String, String> loadUIDefined(Map<String, String> props) {
    props.put("generate.p2.metadata", Boolean.TRUE.toString());
    props.put("p2.append", Boolean.TRUE.toString());
    props.put("p2.publish.artifacts", Boolean.TRUE.toString());
    props.put("p2.compress", Boolean.TRUE.toString());
    props.put("p2.flavor", "tooling");
    //output part
    final String repoPath = getOutputFolder().getAbsolutePath();
    props.put("p2.metadata.repo", String.format("file:%s", repoPath));//TODO: UrlEncoder ?
    props.put("p2.artifact.repo", String.format("file:%s", repoPath));//TODO: UrlEncoder ?
    //project related properties
    final String workingDirectory = myContext.getWorkingDirectory().getAbsolutePath();
    props.put("builder", String.format("\"%s\"", workingDirectory));
    //  outputUpdateJars = false
    props.put("buildDirectory", workingDirectory);
    props.put("forceContextQualifier", getQualifier());
    //    generateFeatureVersionSuffix=true
    //    args.add(String.format("-DgenerateFeatureVersionSuffix=%s", Boolean.TRUE));
    //    base=C:/eclipse/eclipse-PDE-build
    final File eclipseHome = myPlatform.getHome().getParentFile();
    props.put("base", String.format("\"%s\"", eclipseHome.getAbsolutePath()));//MUST skip 'eclipse' segment which past in a script
    //    #pluginPath=Z:/tests/ws-PDERunner/pfeature/;Z:/tests/ws-PDERunner/pplugin.core;Z:/tests/ws-PDERunner/pplugin.ui

    if (Boolean.parseBoolean(myContext.getRunnerParameters().get(EclipseConstants.SETTINGS_ARTIFACTS_OUTPUT_ZIP))) {
      //TODO: setup archive
    }
    //javac's ...
    if (Boolean.parseBoolean(myContext.getRunnerParameters().get(EclipseConstants.SETTINGS_OPTIONS_INCLUDE_DEBUG_INFO))) {
      props.put(EclipseConstants.SETTINGS_OPTIONS_INCLUDE_DEBUG_INFO, Boolean.TRUE.toString());
    }
    props.put(EclipseConstants.PDE_BUILD_PROP_JAVAC_SOURCE, getJavacSource());
    props.put(EclipseConstants.PDE_BUILD_PROP_JAVAC_TARGET, getJavacTarget());
    return props;
  }

  private void populateSystemPropertiesParams(final @NotNull Collection<String> args, Map<String, String> properties) {
    for (Map.Entry<String, String> entry : properties.entrySet()) {
      args.add(String.format("-D%s=%s", entry.getKey(), entry.getValue()));
      LOG.debug(String.format("set %s='%s'", entry.getKey(), entry.getValue()));
    }
  }

  public void init() throws RunBuildException {
    try {
      //copy default 'build.properties' into work directory if one does not exist
      final File customBuildProperties = new File(myContext.getWorkingDirectory(), "build.properties");
      if (customBuildProperties.exists()) {
        LOG.debug(String.format("Found custom build properties file: '%s'. Use this one for PDE build", customBuildProperties));
      } else {
        final File defaultBuildPropertiesFile = new File(myPlatform.getPDEBuilderFolder(), "templates/headless-build/build.properties");
        LOG.debug(String.format("Custom build properties is not found. Use '%s' as default one", defaultBuildPropertiesFile));
        FileUtil.copy(defaultBuildPropertiesFile, customBuildProperties);
        LOG.debug(String.format("Default '%s' copied to '%s'", defaultBuildPropertiesFile, customBuildProperties));
        myCopiedPropertiesFile = customBuildProperties;
      }
    } catch (IOException e) {
      throw new RunBuildException(e);
    }

  }

  public void destroy() {
    //cleanup copied properties file
    if (myCopiedPropertiesFile != null && myCopiedPropertiesFile.exists()) {
      LOG.debug(String.format("Remove copied to default propertues '%s'", myCopiedPropertiesFile));
      FileUtil.delete(myCopiedPropertiesFile);
      myCopiedPropertiesFile = null;
    }
    //TODO: pack output folder if required
  }

}
