/* 
 * 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 net.grinder;

import net.grinder.communication.CommunicationException;
import net.grinder.engine.communication.AgentUpdateGrinderMessage;
import net.grinder.util.VersionNumber;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.ngrinder.common.constants.AgentConstants;
import org.ngrinder.common.util.CompressionUtils;
import org.ngrinder.infra.AgentConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

import static org.ngrinder.common.constants.InternalConstants.PROP_INTERNAL_NGRINDER_VERSION;
import static org.ngrinder.common.util.Preconditions.checkTrue;

/**
 * Agent Update Message Handler.
 *
 * @author JunHo Yoon
 * @since 3.1
 */
@SuppressWarnings("ResultOfMethodCallIgnored")
public class AgentUpdateHandler implements Closeable {
	private static final Logger LOGGER = LoggerFactory.getLogger(AgentUpdateHandler.class);

	private final AgentConfig agentConfig;
	private File download;
	@SuppressWarnings("FieldCanBeLocal")
	private int offset = 0;
	private FileOutputStream agentOutputStream;

	/**
	 * Agent Update handler.
	 *
	 * @param agentConfig agentConfig
	 * @param message     AgentUpdateGrinderMessage
	 */
	public AgentUpdateHandler(AgentConfig agentConfig, AgentUpdateGrinderMessage message)
			throws FileNotFoundException {
		if (!agentConfig.getAgentProperties().getPropertyBoolean(AgentConstants.PROP_AGENT_UPDATE_ALWAYS)) {
			checkTrue(isNewer(message.getVersion(), agentConfig.getInternalProperties().getProperty(PROP_INTERNAL_NGRINDER_VERSION)),
					"Update request was sent. But it's the older version " + message.getVersion());
		}
		this.agentConfig = agentConfig;
		this.download = new File(agentConfig.getHome().getTempDirectory(), "ngrinder-agent.tar");
		this.agentOutputStream = new FileOutputStream(download);
		LOGGER.info("AgentUpdateHandler is initialized!");
	}

	boolean isNewer(String newVersion, String installedVersion) {
		if (newVersion.contains("-SNAPSHOT")) {
			return true;
		}
		installedVersion = installedVersion.replaceAll("\\(.*\\)", "").trim();
		newVersion = newVersion.replaceAll("\\(.*\\)", "").trim();
		return new VersionNumber(newVersion).compareTo(new VersionNumber(installedVersion)) > 0;
	}

	public void close() {
		IOUtils.closeQuietly(agentOutputStream);
		FileUtils.deleteQuietly(download);
	}

	/**
	 * Update agent based on the current message.
	 *
	 * @param message message to be sent
	 */
	public void update(AgentUpdateGrinderMessage message) throws CommunicationException {
		if (message.getOffset() != offset) {
			throw new CommunicationException("Expected " + offset + " offset," +
					" but " + message.getOffset() + " was sent. " + ToStringBuilder.reflectionToString(message));
		}
		try {
			IOUtils.write(message.getBinary(), agentOutputStream);
			offset = message.getNext();
		} catch (IOException e) {
			throw new CommunicationException("Error while writing binary", e);
		}
		if (message.getNext() == 0) {
			IOUtils.closeQuietly(agentOutputStream);
			decompressDownloadPackage();
			// Then just exist to run the agent update process.
			System.exit(0);
		}
	}

	void decompressDownloadPackage() {
		File interDir = new File(agentConfig.getHome().getTempDirectory(), "update_package_unzip");
		File toDir = new File(agentConfig.getCurrentDirectory(), "update_package");
		interDir.mkdirs();
		toDir.mkdirs();

		if (FilenameUtils.isExtension(download.getName(), "tar")) {
			File outFile = new File(toDir, "ngrinder-agent.tar");
			CompressionUtils.untar(download, interDir);
			FileUtils.deleteQuietly(outFile);
		} else {
			LOGGER.error("{} is not allowed to be unzipped.", download.getName());
		}

		try {
			FileUtils.deleteQuietly(toDir);
			FileUtils.moveDirectory(new File(interDir, "ngrinder-agent"), toDir);
		} catch (IOException e) {
			LOGGER.error("Error while moving a file ", e);
		}
		FileUtils.deleteQuietly(download);
		FileUtils.deleteQuietly(interDir);
	}
}

@@ -3923,19 +3923,22 @@
 ckage_un
-zip
+packed
 %22);%0A%09%09Fi
@@ -4312,12 +4312,12 @@
 e un
-zipp
+pack
 ed.%22

fix typo error