/**
 * Copyright (c) 2014 Etherios, a Division of Digi International, Inc.
 * All rights not expressly granted are reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Etherios 11001 Bren Road East, Minnetonka, MN 55343
 * =======================================================================
 */
package com.etherios.connector.firmwareupdate;

import com.etherios.connector.core.facilities.firmware.FirmwareConstants;
import com.etherios.connector.core.facilities.firmware.FirmwareTarget;
import com.etherios.connector.core.utils.ByteUtils;
import com.etherios.connector.core.utils.ChecksumUtils;
import com.etherios.connector.emulator.firmware.IGeneralFirmwareImplementation;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;

/**
 * Class to manage the Firmware Update process.
 *
 * <p>It listens to the Firmware update events, by implementing the
 * {@link com.etherios.connector.emulator.firmware.IGeneralFirmwareImplementation}
 * interface.</p>
 *
 * <p>When a firmware update request is received, it downloads the firmware image
 * and simulates the update process.</p>
 */
public class FirmwareHook implements IGeneralFirmwareImplementation {

	/* Constants */

	private final static String FILE_SEPARATOR = "/";
	private static final String FILE_PREFIX = "file://";

	/* Variables */

	// Size of the firmware image.
	private long size = 0;

	// Amount of bytes received.
	private long received = 0;
	
	// Firmware file.
	private FileConnection fwFile;

	// Output stream.
	private OutputStream os;

	@Override
	public int firmwareUpdateStart(FirmwareTarget target, int size, String fileName) {
		this.size = size;
		this.received = 0;
		// Create the file in the path of the target.
		String filePath = FILE_SEPARATOR + fileName;
		String path = target.getTargetPath();
		if (path != null) {
			if (path.length() > 0 && !path.endsWith(FILE_SEPARATOR))
				path = path + FILE_SEPARATOR;
			filePath = path + fileName;
		}
		try {
			fwFile = (FileConnection) Connector.open(FILE_PREFIX + filePath);
			if (fwFile.exists())
				fwFile.delete();
			fwFile.create();
			os = fwFile.openOutputStream();
		} catch (IOException ex) {
			Logger.getLogger(FirmwareHook.class.getName()).log(Level.SEVERE, null, ex);
			return FirmwareConstants.DOWNLOAD_RESPONSE_TYPE_ERROR;
		}
		return FirmwareConstants.DOWNLOAD_RESPONSE_TYPE_OK;
	}

	@Override
	public boolean fileReceived(String file) {
		// Do not handle this method because it uses the data chunk received notifications.
		return false;
	}

	@Override
	public boolean dataChunkReceived(byte[] bytes) {
		if (os == null)
			return false;
		try {
			// Write this data chunk into a file.
			os.write(bytes);
			received = received + bytes.length;
			// Calculate the download progress and notify it.
			long progress = received * 100 / size;
			System.out.println("Progress: " + progress + "% (" + received + "/" + size + ")");
		} catch (IOException ex) {
			Logger.getLogger(FirmwareHook.class.getName()).log(Level.SEVERE, null, ex);
			return false;
		}
		return true;
	}
	
	@Override
	public int dataChunkDownloadComplete(byte[] checksum) {
		System.out.println("Firmware downloaded!");
		try {
			os.close();
		} catch (IOException ex) {
			Logger.getLogger(FirmwareHook.class.getName()).log(Level.SEVERE, null, ex);
		}
		// Verify the checksum.
		if (ByteUtils.byteArrayToInt(checksum, 0) != 0) {
			try {
				byte[] calculatedCRC32 = ChecksumUtils.getCRC32Checksum(fwFile.openInputStream());
				if (!ByteUtils.compareByteArrays(checksum, calculatedCRC32))
					return FirmwareConstants.DOWNLOAD_COMPLETE_STATUS_CHECKSUM_ERROR;
			} catch (Exception ex) {
				Logger.getLogger(FirmwareHook.class.getName()).log(Level.SEVERE, null, ex);
				return FirmwareConstants.DOWNLOAD_COMPLETE_STATUS_CHECKSUM_ERROR;
			}
		}
		System.out.println("\nStarting the firmware update...");
		updateFirmware();
		return FirmwareConstants.DOWNLOAD_COMPLETE_STATUS_OK;
	}
	
	@Override
	public void firmwareDownloadAbort(int reason) {
		System.out.println("Firmware Update aborted");
	}

	@Override
	public void firmwareUpdateError() {
		System.out.println("Firmware Update Error");
	}

	/**
	 * Method which simulates the firmware update process. Since it's neither
	 * possible to update the PC firmware nor reboot it, simulate the firmware
	 * update.
	 */
	private void updateFirmware() {
		System.out.println("Firmware updated!\n");
	}
}
