/*
    AVR8 Burn-O-Mat
 
    Copyright (C) 2007  Torsten Brischalle

    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 static lu.tudor.santec.i18n.Translatrix._;

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.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import avr8_burn_o_mat.XmlUtil;
import de.lazyzero.kkMulticopterFlashTool.KKMulticopterFlashTool;


public class XmlReaderFirmwares {
    
    private Vector <Firmware> firmwares = new Vector <Firmware>();
    private double actualVersion;
	private LinkedHashMap<String,String> firmwareRepositories = new LinkedHashMap<String,String>();
	private Logger logger = KKMulticopterFlashTool.getLogger();
	private String changelog;
	
    
    /** Creates a new instance of FuseReader */
    public XmlReaderFirmwares() {
    }
    
    public XmlReaderFirmwares(LinkedHashMap<String,String> firmwareRepositories) {
    	firmwares.removeAllElements();
    	if (KKMulticopterFlashTool.isOfflineMode()){
    		
    		String url = "file://"+KKMulticopterFlashTool.getTempFolder()+"firmwares.xml";
    		this.firmwareRepositories.put("",url); 
    		try {
				readXmlFile(url);
			} catch (Exception e) {
				JOptionPane.showMessageDialog(null, _("error.offlineRepository"));
				e.printStackTrace();
				logger.log(Level.WARNING, e.getMessage());
			}
    	} else { //from the online repository
    		this.firmwareRepositories = firmwareRepositories;
    		try {
    			Iterator<String> keys = firmwareRepositories.keySet().iterator();
    			while (keys.hasNext()) {
    				String key = keys.next();
    				boolean load = false;
    				if (key.equals("tgydaily")) {
    					if (KKMulticopterFlashTool.isShowDailyTGYEnabled()) {
    						load = true;
    					} else {
    						load = false;
    					}
    				} else {    
    					load = true;
    				}
    				if (load) {
    					String url = firmwareRepositories.get(key);
    					System.out.println("repository URL: " + url);
    					
    					downloadFirmwareDescription(new URL(url));
    					readXmlFile(getLocalXMLFile(url.substring(url.lastIndexOf('/')+1)));
    				}
				}
    		} catch (Exception e) {
//    			try {
//    				System.out.println("repository mirror URL: " + firmwareRepositoryMirrorURL.toString());
//    				url = this.firmwareRepositoryMirrorURL;
//    				downloadFirmwareDescription(new URL(url));
//    				readXmlFile(url);
//    			} catch (Exception e2) {
//    				JOptionPane.showMessageDialog(null, _("error.repository"));
//    			}
    			e.printStackTrace();
    		}
    	}
	}
    
    
	public Vector<Firmware> getFirmwares() {
		return firmwares;
	}


	
	public Vector<Firmware> getFirmwares(String controllerName) {
		System.out.println("Adding firmwares for controller: " + controllerName);
		if(controllerName.endsWith("p")){
			controllerName=controllerName.replace("p", "");
			System.out.println(controllerName);
		} else if (controllerName.endsWith("pa")){
			controllerName=controllerName.replace("pa", "");
			System.out.println(controllerName);
		}
		Vector <Firmware> fw = new Vector<Firmware>();
		Enumeration<Firmware> en = firmwares.elements();
		while (en.hasMoreElements()) {
			Firmware firmware = (Firmware) en.nextElement();
			if (firmware.getController().contains(controllerName)){
				if (KKMulticopterFlashTool.isHideDecprecatedEnabled()) {
					if (!firmware.isDeprecated()) {
						fw.add(firmware);
						System.out.println("add firmware to drop down list " + firmware);
					}
				} else {
					fw.add(firmware);
					System.out.println("add firmware to drop down list " + firmware);
				}
			} 
		}
		
		Collections.sort(fw, new Comparator<Firmware>() {

			@Override
			public int compare(Firmware o1, Firmware o2) {
				// TODO Auto-generated method stub
				return o2.toStringSorting().compareTo(o1.toStringSorting());
			}
		});

		
		return fw;
	}
    
    public void readXmlFile(String uri) throws Exception {
    	//test the connection first
    	if(!KKMulticopterFlashTool.isOfflineMode()) {
	    	try {
	    		URL url = new URL(uri);
	    		if (uri.startsWith("http")) {
	    			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
	    			conn.setReadTimeout(5000);
	    		} 
			} catch (Exception e) {
				e.printStackTrace();
				logger.log(Level.WARNING, e.getMessage());
				throw e;
			}
    	}
		
		System.out.println("start to parse: " + uri);
//		if (System.getProperty("os.name").toLowerCase().contains("windows")) {
//			uri = uri.substring(7, uri.length());
//		}
		
        DocumentBuilderFactory factory  = DocumentBuilderFactory.newInstance();
        DocumentBuilder        builder  = factory.newDocumentBuilder();
        Document               document = builder.parse(uri);
        
        Node node = document.getDocumentElement().getFirstChild();
        
        
        while (node != null) {
            if (node.getNodeName().equals("firmware")) {
                readFirmware(node);
            }
            if (node.getNodeName().equals("version")) {
                readVersion(node);
                readChangelog(node);
            }
            
            node = node.getNextSibling();
        }
        
    }
    
    private void readChangelog(Node node) {
    	Node nodeChangelog = node.getFirstChild();
    	changelog = "<ul>";
    	while (nodeChangelog != null) {
    		if (nodeChangelog.getNodeName().equals("change")) {
        		String line = XmlUtil.getAttr(nodeChangelog, "value");
        		changelog = changelog.concat("<li>" + line + "</li>");
            }
    		nodeChangelog = nodeChangelog.getNextSibling();
    	}
    	changelog = changelog.concat("<li>and more...</li>");
		changelog = changelog.concat("</ul>");
	}

	private void readVersion(Node node) {
		actualVersion = Double.parseDouble(XmlUtil.getAttr(node, "name"));
	}

	private void readFirmware(Node node) throws Exception {
        
        Node nodeFirmware = node.getFirstChild();
        String author = new String();
        String versionname = new String();
        String controller = new String();
        String md5 = new String();
        String eepromMd5 = new String();
        String additionalOptions = new String();
        String server = new String();
        URL url = null;
        URL zipurl = null;
        URL eepromUrl = null;
        URL eepromZipurl = null;
        String svnUrl = null;
        String svnEEpromUrl = null;
        String svnName = null;
        String svnEEpromName = null;
        URL commenturl = null;
        int features = -1;
        int target = 0;
        String name = XmlUtil.getAttr(node, "name");
        String version = XmlUtil.getAttr(node, "value");
        
        boolean isDeprecated = false;
		while (nodeFirmware != null) {
            
        	if (nodeFirmware.getNodeName().equals("author")) {
        		author = XmlUtil.getAttr(nodeFirmware, "name");
            }
        	if (nodeFirmware.getNodeName().equals("versionname")) {
        		versionname = XmlUtil.getAttr(nodeFirmware, "name");
            }
        	if (nodeFirmware.getNodeName().equals("controller")) {
        		controller = XmlUtil.getAttr(nodeFirmware, "name");
            }
			try {
	        	if (nodeFirmware.getNodeName().equals("controller")) {
	        		target = Integer.parseInt(XmlUtil.getAttr(nodeFirmware, "target"));
	            }
        	} catch (Exception e) {
        		//if target is missing or NumberformatException the target is set to kk.
				target = 0;
			}
        	if (nodeFirmware.getNodeName().equals("file")) {
        		url = new URL(XmlUtil.getAttr(nodeFirmware, "url"));
            }
        	try {
        		if (nodeFirmware.getNodeName().equals("zipfile")) {
        			zipurl = new URL(XmlUtil.getAttr(nodeFirmware, "url"));
        		}
			} catch (Exception e) {
				zipurl = null;
			}
			if (nodeFirmware.getNodeName().equals("eepromfile")) {
        		eepromUrl = new URL(XmlUtil.getAttr(nodeFirmware, "url"));
            }
        	try {
        		if (nodeFirmware.getNodeName().equals("eepromzipfile")) {
        			eepromZipurl = new URL(XmlUtil.getAttr(nodeFirmware, "url"));
        		}
			} catch (Exception e) {
				eepromZipurl = null;
			}
        	try {
        		if (nodeFirmware.getNodeName().equals("svn")) {
        			svnUrl = new String(XmlUtil.getAttr(nodeFirmware, "url"));
        			svnName = new String(XmlUtil.getAttr(nodeFirmware, "name"));
        		}
			} catch (Exception e) {
				svnUrl = null;
				svnName = null;
			}
			try {
        		if (nodeFirmware.getNodeName().equals("svn")) {
        			svnEEpromUrl = new String(XmlUtil.getAttr(nodeFirmware, "eepromurl"));
        			svnEEpromName = new String(XmlUtil.getAttr(nodeFirmware, "eepromname"));
        		}
			} catch (Exception e) {
				svnEEpromUrl = null;
				svnEEpromName = null;
			}
			try {
        		if (nodeFirmware.getNodeName().equals("commenturl")) {
        			commenturl = new URL(XmlUtil.getAttr(nodeFirmware, "url"));
        		}
			} catch (Exception e) {
				commenturl = null;
			}
			try {
        		if (nodeFirmware.getNodeName().equals("additionalOptions")) {
        			additionalOptions = XmlUtil.getAttr(nodeFirmware, "option");
        		}
			} catch (Exception e) {
				additionalOptions = new String();
			}
        	if (nodeFirmware.getNodeName().equals("md5")) {
        		md5 = new String(XmlUtil.getAttr(nodeFirmware, "value"));
            }
        	if (nodeFirmware.getNodeName().equals("eeprommd5")) {
        		eepromMd5 = new String(XmlUtil.getAttr(nodeFirmware, "value"));
            }
        	if (nodeFirmware.getNodeName().equals("features")) {
        		features = Integer.parseInt(XmlUtil.getAttr(nodeFirmware, "value"));
        	}
        	if (nodeFirmware.getNodeName().equals("server")) {
        		server = new String(XmlUtil.getAttr(nodeFirmware, "name"));
            }
        	if (nodeFirmware.getNodeName().equals("deprecated")) {
        		isDeprecated = Boolean.parseBoolean(XmlUtil.getAttr(nodeFirmware, "value"));
            }
            
            nodeFirmware = nodeFirmware.getNextSibling();
        }
        
        Firmware firmware = new Firmware(url);
        firmware.setName(name);
        firmware.setFileName(url.toString());
        firmware.setVersionName(versionname);
        firmware.setVersion(version);
        firmware.setController(controller);
        firmware.setAuthor(author);
        firmware.setZipURL(zipurl);
        firmware.setEEpromURL(eepromUrl);
        firmware.setEEpromZipURL(eepromZipurl);
        firmware.setSVN(svnUrl);
        firmware.setSVNname(svnName);
        firmware.setEEpromSVN(svnEEpromUrl);
        firmware.setEEpromSVNname(svnEEpromName);
        firmware.setMD5(md5);
        firmware.setEEpromMD5(eepromMd5);
        firmware.setCommentURL(commenturl);
        firmware.setAdditionalOptions(additionalOptions);
        firmware.setFeatures(features);
        firmware.setTargetPlatform(target);
        firmware.setServer(server);
        firmware.setDeprecated(isDeprecated);
        
       //firmware.getFile();
        
        firmwares.add(firmware);
    }
	
	private File downloadFirmwareDescription(URL url){
		String filename = null;
		String tmpdir = null;
		
		String urlPath = url.getPath();
		filename = urlPath.substring(urlPath.lastIndexOf("/")+1);
		System.out.println("Download firmware description file: "+filename);
		System.out.println("from: " + url);
		
        tmpdir = KKMulticopterFlashTool.getTempFolder();
        filename = tmpdir + filename;
        	
		File tmpFile = new File(filename);
		if ((new File(tmpdir)).mkdir()) System.out.println("tmpdir created");
		
		
    		try {
    			long time = System.currentTimeMillis();
//    			ZipInputStream in  = new ZipInputStream(url.openStream());
    			
    			TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
    	            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    	                return null;
    	            }
    	            public void checkClientTrusted(X509Certificate[] certs, String authType) {
    	            }
    	            public void checkServerTrusted(X509Certificate[] certs, String authType) {
    	            }
    	        } };
    	        // Install the all-trusting trust manager
    	        final SSLContext sc = SSLContext.getInstance("SSL");
    	        sc.init(null, trustAllCerts, new java.security.SecureRandom());
    	        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    	        // Create all-trusting host name verifier
    	        HostnameVerifier allHostsValid = new HostnameVerifier() {
    	            public boolean verify(String hostname, SSLSession session) {
    	                return true;
    	            }
    	        };

    	        // Install the all-trusting host verifier
    	        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    			
    			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;
//    			in.getNextEntry();
    			while((x=in.read(data,0,1024))>=0) {
    				bout.write(data,0,x);
    			}
    			bout.close();
    			in.close();
    			System.out.println("Download finished in milli seconds " + (System.currentTimeMillis()-time));
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		//unzip
		try {
			long time = System.currentTimeMillis();
			int BUFFER = 2048;
			BufferedOutputStream dest = null;
			BufferedInputStream is = null;
			ZipEntry entry;
			ZipFile zipfile = new ZipFile(filename);
			@SuppressWarnings("rawtypes")
			Enumeration e = zipfile.entries();
			System.out.println("zip: " + e.hasMoreElements());
			while (e.hasMoreElements()) {
				entry = (ZipEntry) e.nextElement();
				System.out.println("Extracting: " + entry);
				is = new BufferedInputStream(zipfile.getInputStream(entry));
				int count;
				byte data[] = new byte[BUFFER];
				FileOutputStream fos = new FileOutputStream(filename.replace(".zip", ""));
				dest = new BufferedOutputStream(fos, BUFFER);
				while ((count = is.read(data, 0, BUFFER)) != -1) {
					dest.write(data, 0, count);
				}
				dest.flush();
				dest.close();
				is.close();
			}
			System.out.println("Download extracted in milli seconds " + (System.currentTimeMillis()-time));
		} catch (Exception e) {
			e.printStackTrace();
		}
//    	   }
//			try {
//				long time = System.currentTimeMillis();
//				int BUFFER = 2048;
//				ZipInputStream zin = new ZipInputStream(url.openStream());
//				ZipEntry entry;
//				while ((entry = zin.getNextEntry()) != null) {
//					int count;
//					byte data[] = new byte[BUFFER];
//					FileOutputStream fos = new FileOutputStream(entry.getName());
//					BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
//					while ((count = zin.read(data, 0, BUFFER)) != -1) {
//						// System.out.write(x);
//						dest.write(data, 0, count);
//					}
//					dest.flush();
//					dest.close();
//	
//				}
//				zin.close();
//				System.out.println("Download extracted in milli seconds " + (System.currentTimeMillis()-time));
//			} catch (FileNotFoundException e) {
//    			// TODO Auto-generated catch block
//    			e.printStackTrace();
//    		} catch (IOException e) {
//    			// TODO Auto-generated catch block
//    			e.printStackTrace();
//    		}

		System.out.println("Download finished: " + filename);
		return tmpFile;
	}
	
	public static void main(String[] args) {
		XmlReaderFirmwares f = new XmlReaderFirmwares();
		try {
			f.readXmlFile("./firmwares.xml");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public LinkedHashMap<String, String> getURL() {
		return this.firmwareRepositories;
	}

	/**
	 * @return the actualVersion
	 */
	public double getActualVersion() {
		return actualVersion;
	}
	
	public String getChangelog() {
		return changelog;
	}

	public void reloadXmlFile(LinkedHashMap<String, String> urls) throws Exception {
		this.firmwareRepositories = urls;
		this.firmwares.removeAllElements();
	
		Iterator<String> keys = firmwareRepositories.keySet().iterator();
		while (keys.hasNext()) {
			String key = keys.next();
			String url = firmwareRepositories.get(key);
			boolean load = false;
			if (key.equals("tgydaily")) {
				if (KKMulticopterFlashTool.isShowDailyTGYEnabled()) {
					load = true;
				} else {
					load = false;
				}
			} else {    
				load = true;
			}
			if (load) {
				downloadFirmwareDescription(new URL(url));
				String uri = getLocalXMLFile(url.substring(url.lastIndexOf('/') + 1));
				readXmlFile(uri);
			}
		}
	}

	private String getLocalXMLFile(String file) {
		String url = file.substring(0, file.length()-4);
		
		String uri = KKMulticopterFlashTool.getTempFolder()+url;
		uri = "file:////".concat(uri);
		return uri;
	}

	

	
	
    
}
