/*
 * Copyright 2000-2013 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.clouds.vmware.vmrun.local;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import jetbrains.buildServer.clouds.CloudInstanceUserData;
import jetbrains.buildServer.clouds.vmware.VMWareConstants;
import jetbrains.buildServer.clouds.vmware.settings.VMImageInfo;
import jetbrains.buildServer.clouds.vmware.vmrun.VMRunParameters;
import jetbrains.buildServer.util.PropertiesUtil;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;

/**
 * @author Eugene Petrenko
 *         Created: 20.03.2010 14:49:08
 */
public class WMMachineInjection {
  private static final Logger LOG = Logger.getLogger(WMMachineInjection.class);
  private final VMRunSettings mySettings;


  public WMMachineInjection(final VMRunSettings settings) {
    mySettings = settings;
  }

  public void injectSettings(final VMImageInfo info, final VMRunParameters params, final CloudInstanceUserData data, final File file) {
    waitInstanceToStart(info, params);

    //We have no chance to wait for WMWareTools loaded in the VM
    //Thus the only chance is to try patching files several times
    //with big timeout.
    //http://communities.vmware.com/message/895012
    try {
      getAgentPropertiesFile(info, file);

      setupProperties(params, data, file);

      pushAgentPropertiesFile(info, file);

    } catch (Throwable t) {
      LOG.warn("Failed to update build agent properties. " + info + ".  Machine will be stopped.");

      throw new VMRunException("Failed to start machine. " + info + ", " + t.getMessage(), t);
    }
  }

  private void waitInstanceToStart(@NotNull final VMImageInfo info, @NotNull final VMRunParameters params) {
    sleep(30, TimeUnit.SECONDS);
    for (int _ = 15; _-- > 0;) {
      final ListVMCommand cmd = new ListVMCommand(mySettings, Collections.singletonList(params.getImageInfo()));
      cmd.startVMRun();

      if (!cmd.getInfos().isEmpty()) {
        return;
      }

      LOG.info("Instance " + params.getImageInfo().getId() + " is not started. Waiting... " + info);
      sleep(30, TimeUnit.SECONDS);
    }
    throw new VMRunException("Timout waiting for " + info + " to start");
  }

  private void sleep(long time, TimeUnit unit) {
    try {
      Thread.sleep(unit.toMillis(time));
    } catch (InterruptedException e) {
      //NOP
    }
  }

  private void getAgentPropertiesFile(final VMImageInfo info, final File file) {
    VMRunException getError = null;
    for (int _ = 10; _-- > 0;) {
      try {
        new GetFileCommand(mySettings, info, info.getImageAgentConfigLocation(), file).startVMRun();
        return;
      } catch (VMRunException e) {
        LOG.debug("Get command failed for machine " + info.getImagePath() + ". Will try again after timeout...");
        getError = e;
        sleep(15, TimeUnit.SECONDS);
      }
    }
    if (getError != null) {
      LOG.warn("Failed to download " + info.getImageAgentConfigLocation() + " file from machine: " + info.getImagePath() + ". " +
               "Check latest VMWare Tools installed on the machine and started. " + getError.getMessage(), getError);
      throw new VMRunException("Failed to download buildAgent.proprties file. " + getError.getMessage());
    }
  }

  private void pushAgentPropertiesFile(final VMImageInfo info, final File file) {
    //TODO: Should we retry here too?
    try {
      new PushFileCommand(mySettings, info, info.getImageAgentConfigLocation(), file).startVMRun();
    } catch (VMRunException e) {
      LOG.warn("Failed to upload file to " + info.getImageAgentConfigLocation() + " for machine: "
               + info.getImagePath() + ". " + e.getMessage(), e);
      throw new VMRunException("Failed to update patched buildAgent.properties file. " + e.getMessage());
    }
  }

  private void setupProperties(final VMRunParameters params, final CloudInstanceUserData data, final File file) {
    Map<String, String> props = new HashMap<String, String>();
    props.putAll(data.getCustomAgentConfigurationParameters());
    props.put("name", data.getAgentName());
    props.put("authorizationToken", "");
    props.put("serverUrl", data.getServerAddress());
    props.put(VMWareConstants.WM_IMAGE_ID, params.getImageId());
    props.put(VMWareConstants.WM_INSTANCE_ID, params.getInstanceId());
    props.put(VMWareConstants.WM_LOCATION_ID, params.getLocationId());

    try {
      PropertiesUtil.updatePropertiesFromFile(file, props, Collections.<String>emptySet());
    } catch (IOException e) {
      throw new VMRunException("Failed to update properties file locally.", e);
    }
  }
}
