/**
 * KKMulticopterFlashTool, a avrdude GUI for flashing KK boards and other
 *   equipment.
 *   Copyright (C) 2011 Christian Moll
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.lazyzero.kkMulticopterFlashTool.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.Enumeration;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

//import org.json.simple.JSONObject;

import de.lazyzero.kkMulticopterFlashTool.KKMulticopterFlashTool;

public class Firmware {
	
	public static final int POT = 1;
	public static final int LCD = 2;
	public static final int PIEZO = 4;
	public static final int MEMS = 8;
	public static final int ACC = 16;
	public static final int CPPM = 32;
	public static final int LVA = 64;
	
	public static final int TARGET_KK = 0;
	public static final int TARGET_ESC = 1;
	public static final int TARGET_OPENAERO = 3;
	public static final int TARGET_CLOSD = 4;
	public static final int TARGET_RX3S = 5;

	private File file;
	private File eepromFile;
	private String version;
	private String author;
	private String name;
	private String controller;
	private String additionalOptions;
	private URL url;
	private String md5Calculated;
	private String md5;
	private String server;
	private URL zipURL;
	private URL commenturl;
	private String svnUrl;
	private String svnName;
	private int features;
	private int target = 0;
	private String versionName;
	
	private Logger logger = KKMulticopterFlashTool.getLogger();
	private String fileName;
	private URL eepromUrl;
	private URL eepromZipUrl;
	private String svnEEpromUrl;
	private String svnEEpromName;
	private String eepromMD5;
	private String eepromMD5Calculated;
	private boolean isDeprecated = false;
	
	

	public Firmware(){}

	public Firmware(File firmwareFile) {
		this.file = firmwareFile;
		
		try {
			System.out.println(this.getFile().getAbsolutePath());
		} catch (FileCorruptException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public Firmware(URL firmwareURL) throws NullPointerException{
		if (firmwareURL!=null){
			this.url = firmwareURL;
		}

	}
	
	public URL getFileURL() {
		return url;
	}
	
	public URL getZipURL() {
		return zipURL;
	}

	public File getFile() throws FileCorruptException{
		if (KKMulticopterFlashTool.isOfflineMode()) {
			if (zipURL != null) {
				String filename = null;
				String tmpdir = null;
				
				String urlPath = url.getPath();
				filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
				System.out.println("Download file: "+filename);
				
		        tmpdir = KKMulticopterFlashTool.getTempFolder();
		        	
				File tmpFile = new File(tmpdir + filename);
				File fExtract = new File(url.toString().substring(7).trim());
				this.file = Zip.unzipFile(tmpFile, fExtract);
				this.md5Calculated = MD5.getMD5(file);
			} else if (url != null) {
				String filename = null;
				String tmpdir = null;
				
				String urlPath = url.getPath();
				filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
				System.out.println("Load file from temp folder: "+filename);
				
		        tmpdir = KKMulticopterFlashTool.getTempFolder();
		        	
				this.file = new File(tmpdir + filename);
				this.md5Calculated = MD5.getMD5(file);
				System.out.println(md5Calculated);
			} else if (eepromZipUrl != null) {
				String filename = null;
				String tmpdir = null;
				
				//extract file name of eeprom file to know which file has to be extracted
				String urlPath = eepromUrl.getPath();
				filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
				System.out.println("Download file: "+filename);
				
		        tmpdir = KKMulticopterFlashTool.getTempFolder();
		        	
				File tmpFile = new File(tmpdir + filename);
				File fExtract = new File(url.toString().substring(7).trim());
				this.eepromFile = Zip.unzipFile(tmpFile, fExtract);
				this.eepromMD5Calculated = MD5.getMD5(eepromFile);
			} else if (eepromUrl != null) {
				String filename = null;
				String tmpdir = null;
				
				String urlPath = eepromUrl.getPath();
				filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
				System.out.println("Load file from temp folder: "+filename);
				
		        tmpdir = KKMulticopterFlashTool.getTempFolder();
		        	
				this.eepromFile = new File(tmpdir + filename);
				this.eepromMD5Calculated = MD5.getMD5(eepromFile);
				System.out.println(eepromMD5Calculated);
			}
		} else { // this condition is for downloading the files
			if (zipURL != null) {
				File f = this.downloadFile(zipURL);
				File fExtract = new File(url.toString().substring(7).trim());
				this.file = Zip.unzipFile(f, fExtract);
				this.md5Calculated = MD5.getMD5(file);
			} else if (svnUrl != null) {
				System.out.println("debug svn: " + svnUrl.toString());
				try {
					SVN svn = new SVN(svnUrl.toString(), svnName);
					this.file = svn.getFile();
					this.md5 = svn.getFileMD5();
					this.md5Calculated = MD5.getMD5(file);
					logger.log(Level.INFO, "MD5 of SVN: " + md5 + " MD5 calculated from file: " + md5Calculated);
				} catch (Exception e) {
					logger.log(Level.SEVERE, "Problem during SVN action");
					e.printStackTrace();
					throw new FileCorruptException();
				}
			} else if (url != null) {
				this.file=downloadFirmware(url);
				this.md5Calculated = MD5.getMD5(file);
				System.out.println(md5Calculated);
			} else if (eepromZipUrl != null) {
				File f = this.downloadFile(eepromZipUrl);
				File fExtract = new File(eepromUrl.toString().substring(7).trim());
				this.eepromFile = Zip.unzipFile(f, fExtract);
				this.eepromMD5Calculated = MD5.getMD5(eepromFile);
			} else if (svnEEpromUrl != null) { 
				System.out.println("debug svn: " + svnEEpromUrl.toString());
				try {
					SVN svn = new SVN(svnEEpromUrl.toString(), svnEEpromName);
					this.eepromFile = svn.getFile();
					this.eepromMD5 = svn.getFileMD5();
					this.eepromMD5Calculated = MD5.getMD5(eepromFile);
					logger.log(Level.INFO, "MD5 of SVN: " + eepromMD5 + " MD5 calculated from EEPROM file: " + eepromMD5Calculated);
				} catch (Exception e) {
					logger.log(Level.SEVERE, "Problem during SVN action");
					e.printStackTrace();
					throw new FileCorruptException();
				}
			} else if (eepromUrl != null) {
				this.eepromFile=downloadFirmware(eepromUrl);
				this.eepromMD5Calculated = MD5.getMD5(eepromFile);
				System.out.println(eepromMD5Calculated);
			}
		}
		
		//check the md5
		if(null != md5 && !md5.equals("")) {
			logger.log(Level.INFO, "check MD5");
			logger.log(Level.INFO, "MD5: " + md5 + " MD5 calculated from file: " + md5Calculated);
			if (!md5.equals(md5Calculated)){
				logger.log(Level.SEVERE, "The file is corrupted.");
				throw new FileCorruptException();
			} else {
				logger.log(Level.INFO, "File is okay.");
			}
		} else {
			logger.log(Level.INFO, "check MD5: no MD5 available. Check aborted.");
		}
		if (null != eepromMD5 && !eepromMD5.equals("")) {
			logger.log(Level.INFO, "check EEPROM MD5");
			if (!eepromMD5.equals(eepromMD5Calculated)){
				logger.log(Level.SEVERE, "The EEPROM file is corrupted.");
				throw new FileCorruptException();
			} else {
				logger.log(Level.INFO, "EEPROM File is okay.");
			}
		} else {
			logger.log(Level.INFO, "check EEPROM MD5: no MD5 available. Check aborted.");
		}
		
		return file;
	}

	private File downloadFile(URL url){
		String filename = null;
		String tmpdir = null;
		boolean reload = false;
		
		String urlPath = url.getPath();
		filename = urlPath.substring(urlPath.lastIndexOf("/")+1).trim();
		System.out.println("Download file: "+filename);
		
        tmpdir = KKMulticopterFlashTool.getTempFolder();
        	
        filename = tmpdir + filename;
        File tmpFile = new File(filename);
		if (filename.endsWith("tgy-daily.zip")) {
			reload = true;
			System.out.println("reload file: " + tmpFile.getName());
		} 
		
		if ((new File(tmpdir)).mkdir()) System.out.println("tmpdir created");
		
		if (!tmpFile.exists() || reload) {
    		try {
    			BufferedInputStream in = new BufferedInputStream(url.openStream());
    			FileOutputStream fos = new FileOutputStream(tmpFile);
    			BufferedOutputStream bout = new BufferedOutputStream(fos,1024);
    			byte[] data = new byte[1024];
    			int x=0;
    			while((x=in.read(data,0,1024))>=0) {
    				bout.write(data,0,x);
    			}
    			bout.close();
    			in.close();
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
		} else {
			System.out.println("skip download. File already loaded.");
		}

		System.out.println("Download finished: " + filename);
		return tmpFile;
	}
	
	private File downloadFirmware(URL firmwareURL){
		String filename, tmpdir;
		
		String urlPath = firmwareURL.getPath();
		filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
		System.out.println("Download file: "+filename);
		
        tmpdir = KKMulticopterFlashTool.getTempFolder();
        filename = tmpdir+filename;
		
		File tmpFile = new File(filename);
		if ((new File(tmpdir)).mkdir()) System.out.println("tmpdir created");
		
		if (!tmpFile.exists()){
			try {
				BufferedInputStream in = new BufferedInputStream(firmwareURL.openStream());
				FileOutputStream fos = new FileOutputStream(tmpFile);
				BufferedOutputStream bout = new BufferedOutputStream(fos,1024);
				byte[] data = new byte[1024];
				int x=0;
				while((x=in.read(data,0,1024))>=0) {
					bout.write(data,0,x);
				}
				bout.close();
				in.close();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			System.out.println("skip download. File already loaded.");
		}

		System.out.println("Download finished: " + filename);
		return tmpFile;
	}
	
	public String toString() {
		return name + " ( V" + version + " [" + controller + "]" + " " + author + " )";
	}
	
	public String toStringSorting() {
		//return name+version+author; //old
		if (target == TARGET_ESC) return name+version+author;
		StringBuilder sortableVersion = new StringBuilder();
		String[] v = version.split(".");
		for (String part : v) {
			new Formatter(sortableVersion, Locale.US).format("00000000000000000000000000", part);
		}
		
		return author+name+sortableVersion;
	}
	
	public String toHTMLString() {
		return "<html>" + name + " (V" + version + " [" + controller + "])" + "<br>" + author ;
//		return "<html>" + name + " V" + version + " by " + author + (server.equals("")?"":("<br>" + server)) ;
	}
	
	public String getController() {
		return controller;
	}
	
	public URL getCommentURL() {
		return commenturl;
	}
	
	public boolean hasCommentURL() {
		System.out.println(commenturl);
		return (null != commenturl);
	}

	/**
	 * @param version the version to set
	 */
	public void setVersion(String version) {
		this.version = version;
	}

	/**
	 * @param author the author to set
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}

	/**
	 * @param controller the controller to set
	 */
	public void setController(String controller) {
		this.controller = controller;
	}
	
	public void setMD5(String md5) {
		this.md5 = md5; 
	}
	
	public String getMD5() {
		return this.md5;
	}

	public void setZipURL(URL zipurl) {
		this.zipURL = zipurl;
	}
	
	public void setCommentURL(URL commenturl) {
		this.commenturl = commenturl;
	}
	
	public static void main(String[] args) {
		Firmware f = null;
		
		try {
			f = new Firmware();
			File ff = f.downloadFirmware(new URL("http://www.kkmulticopter.kr/multicopter/firmware/XXcontrol_KR_DualCopter_v1_5.hex"));
			MD5.getMD5(ff);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
//		try {
//			f = new Firmware(new URL("http://www.lazyzero.de/_media/firmware.hex"));
//			f.setAuthor("ich");
//			f.setController("m48");
//			f.setName("Quad");
//			f.setVersion("0.3");
//		} catch (NullPointerException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (MalformedURLException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		f.getFile();
		//f.downloadFirmware(f.url);
	}

	public static Vector<Firmware> getOfflineAvailableFirmwares(Vector<Firmware> inputFirmwares) {
		Vector<Firmware> firmwares = new Vector<Firmware>();
		String tmpFolder = KKMulticopterFlashTool.getTempFolder();
		
		for (Firmware firmware : inputFirmwares) {
			
			if (firmware.zipURL != null) {
				String urlPath = firmware.zipURL.getPath();
				String filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
				if (new File(tmpFolder+filename).exists()) {
					firmwares.add(firmware);
				}
			} else if (firmware.url != null) {
				String urlPath = firmware.url.getPath();
				String filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
				if (new File(tmpFolder+filename).exists()) {
					firmwares.add(firmware);
				}
			}
		}
		
		
		return firmwares;
	}

	public static Vector<String> getCategories(Vector<Firmware> firmwares) {
		Vector<String>firmwareTyps = new Vector<String>();
		Enumeration<Firmware> en = firmwares.elements();
		while (en.hasMoreElements()) {
			Firmware firmware = (Firmware) en.nextElement();
			if (!firmwareTyps.contains(firmware.getName())) {
				firmwareTyps.add(firmware.getName());
			}
		}
		return firmwareTyps;
	}

	public static HashMap<String,Boolean> getCategoriesBooleanHashMap(Vector<Firmware> firmwares) {
		HashMap<String, Boolean>firmwareTyps = new HashMap<String,Boolean>();
		Enumeration<Firmware> en = firmwares.elements();
		while (en.hasMoreElements()) {
			Firmware firmware = (Firmware) en.nextElement();
			if (!firmwareTyps.containsKey(firmware.getName())) {
				firmwareTyps.put(firmware.getName(), false);
			}
		}
		return firmwareTyps;
	}

	public static Vector<Firmware> filter(String category, Vector<Firmware> firmwares) {
		Vector<Firmware> f = new Vector<Firmware>();
		for (Iterator<Firmware> iterator = firmwares.iterator(); iterator.hasNext();) {
			Firmware firmware = iterator.next();
			if (category.toString().equals(firmware.getName())) {
				f.add(firmware);
			}
		}
		
		return f;
	}

	public void setAdditionalOptions(String additionalOptions) {
		this.additionalOptions = additionalOptions;
	}

	
	public String getAdditionalOptions() {
		return this.additionalOptions;
	}
	
	public void setFeatures(int features) {
		this.features = features;
	}

	public int getFeatures() {
		return this.features;
	}

	public String getServer() {
		return server;
	}
	
	public void setServer(String server) {
		this.server = server;
	}

	public void setTargetPlatform(int target) {
		this.target = target;
	}
	
	public int getTarget() {
		return target;
	}

	public String getAuthor() {
		return author;
	}

	public String getVersion() {
		return version;
	}

	public void setVersionName(String versionName) {
		this.versionName = versionName;
	}
	
	public String getVersionName() {
		return this.versionName;
	}
	
	public String getSVN() {
		return this.svnUrl;
	}

	public void setSVN(String svnUrl) {
		this.svnUrl = svnUrl;
	}
	
	public String getSVNname() {
		return this.svnName;
	}

	public void setSVNname(String svnName) {
		this.svnName = svnName;
	}

	public String getFileName() {
		return fileName ;
	}
	
	public void setFileName(String fileName) {
		this.fileName = fileName.substring(fileName.lastIndexOf('/')+1);
	}

	public void setEEpromURL(URL eepromUrl) {
		this.eepromUrl = eepromUrl;
	}

	public void setEEpromZipURL(URL eepromZipUrl) {
		this.eepromZipUrl = eepromZipUrl;
	}

	public void setEEpromSVN(String svnEEpromUrl) {
		this.svnEEpromUrl = svnEEpromUrl;
	}

	public void setEEpromMD5(String md5) {
		this.eepromMD5 = md5;
	}

	public void setEEpromSVNname(String svnEEpromName) {
		this.svnEEpromName = svnEEpromName;
	}

	public void setDeprecated(boolean isDeprecated) {
		this.isDeprecated = isDeprecated;
	}

	public boolean isDeprecated() {
		return isDeprecated;
	}

//	public String toJSON() {
//		JSONObject obj = new JSONObject();
//		obj.put("author", author);
//		obj.put("controller", controller);
//		return obj.toJSONString();
//	}
	
}
