/**
 * 
 */
package de.lazyzero.kkMulticopterFlashTool;

import static lu.tudor.santec.i18n.Translatrix._;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.SplashScreen;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import lu.tudor.santec.i18n.Translatrix;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;

import avr8_burn_o_mat.AVR;
import avr8_burn_o_mat.AvrdudeConfigFile;
import avr8_burn_o_mat.AvrdudeControl;
import avr8_burn_o_mat.AvrdudeControl.FileFormat;
import avr8_burn_o_mat.AvrdudeProgrammer;
import avr8_burn_o_mat.InterfaceTextOutput;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import de.lazyzero.kkMulticopterFlashTool.gui.ColorTweaks;
import de.lazyzero.kkMulticopterFlashTool.gui.ControllerPanel;
import de.lazyzero.kkMulticopterFlashTool.gui.EEpromResetPanel;
import de.lazyzero.kkMulticopterFlashTool.gui.EEpromSettingsPanel;
import de.lazyzero.kkMulticopterFlashTool.gui.FirmwarePanel;
import de.lazyzero.kkMulticopterFlashTool.gui.ProgrammerPanel;
import de.lazyzero.kkMulticopterFlashTool.gui.kkMenu;
import de.lazyzero.kkMulticopterFlashTool.utils.FileCorruptException;
import de.lazyzero.kkMulticopterFlashTool.utils.Firmware;
import de.lazyzero.kkMulticopterFlashTool.utils.Icons;
import de.lazyzero.kkMulticopterFlashTool.utils.XmlReaderFirmwares;
import de.lazyzero.kkMulticopterFlashTool.utils.EEprom.EEprom;
import de.lazyzero.kkMulticopterFlashTool.utils.EEprom.EEpromListener;

/**
 * @author moll
 * 
 */
public class KKMulticopterFlashTool extends JFrame implements
		InterfaceTextOutput, PropertyChangeListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static String VERSION = "0.62";
	private static boolean isBeta = false;
	private static int betaVersion = 1;
	public static final String MODE_CHANGED = "changed";
	public static final String KKPLUSBOOT = "kkplusboot";
	public static final String FLYCAM_BLACKBOARD = "flycam_black";
	public static final String FLYCAM_BLACKBOARD_P = "flycam_blackp";
	public static final String SMARTLCD = "smartlcd";
	public static final String ESC = "esc";
	private static KKMulticopterFlashTool kk;

	private Locale						locale;
	private JPanel						mainPanel;
	private Properties					settings		 = new Properties();
	public static File SETTINGS_FILE	= new File(System.getProperty("user.dir"), "kkMulticopterFlashTool.properties");
	public static File LOG_FILE	= new File(System.getProperty("user.dir"), "kkLogging.txt");
	
	private JFrame						mainframe;
	private Dimension preferredSize	= new Dimension(830, 650);
	private AvrdudeConfigFile			 avrConfig;
	private AvrdudeProgrammer			 programmer;
	private AvrdudeControl				control;
	private boolean forceFlashing = false;

	private Vector<AvrdudeProgrammer>	 programmers;
	private static Vector<AVR>				   avrs			 = new Vector<AVR>();
	private AVR						   controller;

	private JTextArea					 output;
	private JScrollPane				   outputPane;

	private ProgrammerPanel			   programmerPanel;
	private ControllerPanel			   controllerPanel;
	private FirmwarePanel				 firmwarePanel;
	private URL firmwareRepositoryURL = null;
	private URL firmwareRepositoryMirrorURL = null;
	private Firmware firmware;
	private XmlReaderFirmwares firmwareReader;
	private boolean offlineMode;
	private String mode;
	
	private FileHandler logFile;
	private Logger logger = Logger.getLogger(KKMulticopterFlashTool.class.getName());
	private boolean enableTweak = true;
	public static boolean ENABLE_PORT_CHECK = true;
	private JTabbedPane tabbedPane = new JTabbedPane();
	//private TestPanel testPanel;
	private JPanel programmingPanel;
	private JPanel eepromPanel;
	private Component eepromResetPanel;
	private EEpromSettingsPanel eepromSettingsPanel;
	protected boolean successful;

	public KKMulticopterFlashTool(String[] args) {

		kk = this;
		evaluateCommandlineOptions(args);
		
		//check if the OS is Mac OS X
		if (System.getProperty("os.name").toLowerCase().contains("mac")) {
			SETTINGS_FILE = new File(System.getProperty("user.home")+"/Library/Preferences/","kkMulticopterFlashTool.properties");
			LOG_FILE = new File(getTempFolder(),"kkLogging.txt");
		}
		
		initLogger();
		
		SplashScreen splash = SplashScreen.getSplashScreen();
		if (splash==null) {
			logger.log(Level.INFO, "Splash not loaded.");
		}
		
		
		logger.log(Level.INFO, System.getProperty("user.home"));
		logger.log(Level.INFO, System.getProperty("os.name"));
		

		loadSettings();
		loadTranslation();

		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				saveSettings();
			}
		});
		
		firmwareReader = new XmlReaderFirmwares(firmwareRepositoryURL, firmwareRepositoryMirrorURL);

		this.init();
		if (enableTweak) ColorTweaks.tweakColors();
		this.initGUI();
		
		this.addPropertyChangeListener(this);
		
		checkVersion();
	}

	
	private void evaluateCommandlineOptions(String[] args) {
		
	        Options options = new Options();
	        
	        options.addOption("h", "help", false, "Show this help message.");
	        options.addOption("c", "color", false, "Use default colors of the Java VM.");
	        options.addOption("p", "portcheck", false, "Disable checking for port checking.");
	     
	        try {
	            CommandLineParser parser = new PosixParser();
	            CommandLine cmd = parser.parse(options, args);
	            
	            if (cmd.hasOption("h")) {
	                HelpFormatter formatter = new HelpFormatter();
	                formatter.printHelp("KKmulticopter Flash Tool Version " + VERSION, options, true);
	                System.exit(0);
	            }
	           
	            if (cmd.hasOption("c")) {
	            	enableTweak = false;
	            }
	            
	            if (cmd.hasOption("p")) {
	            	ENABLE_PORT_CHECK = false;
	            }
	            
	            	
	        } catch (ParseException exp) {
	           
	        }
	}


	private void initLogger() {
		try {
			logFile = new FileHandler(LOG_FILE.getAbsolutePath(), false);
			logFile.setFormatter(new SimpleFormatter());
			logFile.setLevel(Level.INFO);
			logger.addHandler(logFile);
			
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		logger.setLevel(Level.INFO);
	}


	
	
		
	private void checkVersion() {
		logger.log(Level.INFO, "Check the version: " + VERSION + (isBeta?" beta "+betaVersion:"") + " online is version: " + firmwareReader.getActualVersion());
		if (firmwareReader.getActualVersion()>Double.parseDouble(VERSION)) {
//			JOptionPane.showMessageDialog(this, _("update"));
			String[] choices = {_("downloads.download"), _("Cancel")};
			int result = JOptionPane.showOptionDialog(this, _("update"), "",
				      JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, 
				      null, choices, choices[0]);
			if (result == 0) {
				kkMenu.openURL("http://kkflashtool.de");
				System.exit(0);
			}
		}
	}


	public void flashAVR() {
		new Thread(){
			public void run(){
				
				boolean notFailed = true;
				
				try {
					control = new AvrdudeControl(KKMulticopterFlashTool.this);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
					logger.log(Level.WARNING, e1.getMessage());
				}
				control.setPort(programmerPanel.getPort());
				control.setProgrammer(programmer);
				if (programmerPanel.useBaudRate()) {
					control.addAdditionalOption("-b " + programmerPanel.getRate());
				}
				logger.log(Level.INFO, programmer.getType());
//				if (programmer.getType().equals("stk500v2")){
				control.addAdditionalOption("-B 8");
				logger.log(Level.INFO, "set additional Option -B 8");
//				}
				//if (controller.getCaption().equals("m48pa")||controller.getCaption().equals("m168p")||controller.getCaption().equals("m328p")){
				control.addAdditionalOption("-e");
				logger.log(Level.INFO, "set additional Option -e");
//				}
				
				if (forceFlashing) {
					control.addAdditionalOption("-F");
				}
				control.setDisableFuseCheck(false);
				
				//import additionalOptions from firmware
				control.addAndUpdateAdditionalOptions(firmware.getAdditionalOptions());
				//for BL-ESC the fuses are null and the next step is skipped.
				if (controller.getLfuse() != null && controller.getHfuse() != null) {
					//write the fuses.
					try {
						logger.log(Level.INFO, "write fuses: ");
						control.writeFuses(controller);
					} catch (Exception e) {
						notFailed = false; 
						e.printStackTrace();
						logger.log(Level.WARNING, "error.writefuses");
						println(_("error.writefuses"));
					}
					
					try {
						Thread.sleep(1500);
					} catch (InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
						logger.log(Level.WARNING, e1.toString());
					}
				}
				
				try {
					if (notFailed) {
						logger.log(Level.INFO, "flash firmware");
						try {		
							println(_("flash.file") + ": "
									+ firmware.getFile().getAbsolutePath());
							control.writeFlash(controller, firmware.getFile()
									.getAbsolutePath(), FileFormat.INTEL_HEX);
						} catch (FileCorruptException e) {
							println(_("flash.abort") + " " +_("flash.filecorrupt"));
						}
					} else {
						println(_("flash.abort"));
						logger.log(Level.WARNING, "flash.abort");
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					println(_("error.writeflash"));
					logger.log(Level.WARNING, "error.writeflash");
				}
			}
		}.start();
		
		
	}
	
	public void flashEEprom(final EEpromListener eepromListener) {
		logger.info("WRITE the EEprom!!!");
		clearText();
		new Thread(){
			public void run(){
				
				try {
					control = new AvrdudeControl(KKMulticopterFlashTool.this);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
					logger.log(Level.WARNING, e1.getMessage());
				}
				control.setPort(programmerPanel.getPort());
				control.setProgrammer(programmer);
				if (programmerPanel.useBaudRate()) {
					control.addAdditionalOption("-b " + programmerPanel.getRate());
				}
				logger.log(Level.INFO, programmer.getType());
				control.addAdditionalOption("-B 8");
				logger.log(Level.INFO, "set additional Option -B 8");
				if (forceFlashing) {
					control.addAdditionalOption("-F");
				}
				control.setDisableFuseCheck(false);
				
				logger.log(Level.INFO, "flash eeprom");
				try {		
					println(_("flash.eeprom") + ": "
							+ EEprom.getDefaultOutputFile());
					control.writeEEPROM(controller,  EEprom.getDefaultOutputFile(), FileFormat.RAW);
					eepromListener.EEpromState(EEpromListener.WROTE);
				} catch (FileCorruptException e) {
					println(_("flash.abort") + " " +_("flash.filecorrupt"));
					eepromListener.EEpromState(EEpromListener.FILE_CORRUPT);
				} catch (Exception e) {
					eepromListener.EEpromState(EEpromListener.FLASH_FAILED);
					println(_("flash.abort"));
					logger.warning(e.getMessage());
//					e.printStackTrace();
				}
			}
		}.start();
		
	}
	
	public void readEEprom(final EEpromListener eepromListener) {
		logger.info("READ the EEprom!!!");
		clearText();
		new Thread(){
			public void run(){
				try {
					control = new AvrdudeControl(KKMulticopterFlashTool.this);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
					logger.log(Level.WARNING, e1.getMessage());
				}
				control.setPort(programmerPanel.getPort());
				control.setProgrammer(programmer);
				if (programmerPanel.useBaudRate()) {
					control.addAdditionalOption("-b " + programmerPanel.getRate());
				}
				logger.log(Level.INFO, programmer.getType());
				control.addAdditionalOption("-B 8");
				logger.log(Level.INFO, "set additional Option -B 8");
				if (forceFlashing) {
					control.addAdditionalOption("-F");
				}
				control.setDisableFuseCheck(false);
				
				logger.log(Level.INFO, "flash eeprom");
				try {		
					println(_("read.eeprom") + ": "
							+ EEprom.getDefaultInputFile());
					control.readEEPROM(controller,  EEprom.getDefaultInputFile(), FileFormat.RAW);
					eepromListener.EEpromState(EEpromListener.READ);
				} catch (Exception e) {
					eepromListener.EEpromState(EEpromListener.FAILED);
					println(_("read.abort"));
					logger.warning(e.getMessage());
//					e.printStackTrace();
				}
			}
		}.start();
	}
	

	public void loadSettings() {
		try {
			settings.load(new FileInputStream(SETTINGS_FILE));
			
			firmwareRepositoryURL = new URL(settings.getProperty("firmwareRepositoryURL", "http://www.lazyzero.de/_media/firmwares.xml.zip"));
			firmwareRepositoryMirrorURL = new URL(settings.getProperty("firmwareRepositoryMirrorURL", "http://www.michael-rossberg.de/kkmulticopter/firmwares.xml.zip"));
			
			//check for depricated settings!!!
			if (firmwareRepositoryURL.equals("http://www.lazyzero.de/_media/firmwares.xml")) {
				firmwareRepositoryURL = new URL("http://www.lazyzero.de/_media/firmwares.xml.zip");
			}
			
			offlineMode = new Boolean(settings.getProperty("offlineMode","false"));
			
			if (isOfflineMode()) {
				mode = _("offline");
			} else {
				mode = _("online");
			}
			
			
			try {
				this.setLocale((String) settings.getProperty("locale.language",
						"en"), (String) settings.getProperty("locale.country",
						"US"));
			} catch (Exception e) {
			}

		} catch (Exception e) {
			try {
				e.printStackTrace();
				firmwareRepositoryURL = new URL("http://www.lazyzero.de/_media/firmwares.xml.zip");
				firmwareRepositoryMirrorURL = new URL("http://www.michael-rossberg.de/kkmulticopter/firmwares.xml.zip");
				offlineMode = false;
			} catch (MalformedURLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			saveSettings();
			e.printStackTrace();
		}
	}

	private void saveSettings() {
		try {
			settings.put("locale.language", locale.getLanguage());
			settings.put("locale.country", locale.getCountry());

			settings.put("offlineMode", offlineMode+"");
			
			settings.put("programmer", programmer.getId());
			settings.put("port", programmerPanel.getPort());
			settings.put("rate", programmerPanel.getRate());
			settings.put("defaultRate", programmerPanel.isDefaultRate()+"");
			settings.put("controller", controller.getName());

			settings.put("firmwareRepositoryURL", firmwareRepositoryURL.toString());
			settings.put("firmwareRepositoryMirrorURL", firmwareRepositoryMirrorURL.toString());
			try {
				settings.put("last.dir", firmware.getFile().getPath());
			} catch (Exception e) {
				// TODO: handle exception
			}

			settings.store(new FileOutputStream(SETTINGS_FILE), "");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void init() {
		avrConfig = new AvrdudeConfigFile();
		try {
			avrConfig.readAvrdudeConfigFile(AvrdudeConfigFile.getConfigFileByOS());
		} catch (IOException e) {
			JOptionPane.showMessageDialog(this, _("error.avrdudeconfig"));
			e.printStackTrace();
		}
		programmers = avrConfig.getProgrammerList();

		avrs.add(new AVR("KK Blackboard 168", "16kB flash", "m168", 512,"0xe2", "0xdd")); //Niall efuse:FF lfuse:E2 hfuse:DE
		avrs.add(new AVR("KK Blackboard 168P/PA", "16kB flash", "m168p", 512, "0xe2", "0xdd"));
		avrs.add(new AVR("KK Blackboard 328P", "32kB flash", "m328p", 1024, "0xe2", "0xd9"));
		avrs.add(new AVR("KK Plus 5.5d/e", "16kB flash", "m168p", 512, "0xe2", "0xde"));
//		avrs.add(new AVR("KK Plus 5.5d/e Bootloader restore", "16kB flash", KKPLUSBOOT, "0xe2", "0xde"));//Extended : 0xFF High: 0xDE Low: 0xE2
		avrs.add(new AVR("HobbyKing Quadcopter Control Board V3", "32kB flash", "m328p", 1024, "0xe2", "0xd9"));
		avrs.add(new AVR("HobbyKing Quadcopter Control Board V2/V2.1", "16kB flash", "m168p", 512, "0xe2", "0xdd"));
		avrs.add(new AVR("HobbyKing Quadcopter Control Board V1", "4kB flash", "m48p", 256, "0xe2", "0xdd"));
		avrs.add(new AVR("Flycam Blackboard", "16kB flash", FLYCAM_BLACKBOARD, 512, "0xe2", "0xdd"));//Extended : 0xFF High: 0xDE Low: 0xE2
		avrs.add(new AVR("Flycam Blackboard (Goodluckbuy version)", "16kB flash", FLYCAM_BLACKBOARD_P, 512, "0xe2", "0xdd"));//Extended : 0xFF High: 0xDE Low: 0xE2
		avrs.add(new AVR("Korean Blueboard", "16kB flash", "m168", 512, "0xe2", "0xdd"));
		avrs.add(new AVR("Korean Redboard (without chip update)", "4kB flash", "m48", 256, "0xe2", "0xdd"));
		
		avrs.add(new AVR("atmega 8-based brushless ESC", "8kB flash", ESC, 512, null, null));
		avrs.add(new AVR("SmartLCD Pro", "4kB flash", SMARTLCD, 256, null, null));
		
		avrs.add(new AVR("atmega 48", "4kB flash", "m48", 256, "0xe2", "0xdd"));
		avrs.add(new AVR("atmega 48PA", "4kB flash", "m48p", 256, "0xe2", "0xdd"));
		avrs.add(new AVR("atmega 88", "8kB flash", "m88", 512, "0xe2", "0xdd"));
		avrs.add(new AVR("atmega 88P/PA", "8kB flash", "m88p", 512, "0xe2", "0xdd"));
		avrs.add(new AVR("atmega 168", "16kB flash", "m168", 512, "0xe2", "0xdd"));
		avrs.add(new AVR("atmega 168P/PA", "16kB flash", "m168p", 512, "0xe2", "0xdd"));
		avrs.add(new AVR("atmega 328P", "32kB flash", "m328p", 1024, "0xe2", "0xd9"));
		avrs.add(new AVR("atmega 328", "32kB flash", "m328", 1024, "0xe2", "0xd9"));
		
	}

	private void initGUI() {
		mainPanel = new JPanel();

		// create the CellContraints
		CellConstraints cc = new CellConstraints();

		// create the Layout for Panel this
		String panelColumns = "fill:pref:grow";
		String panelRows = "3dlu,pref,3dlu,pref,3dlu,pref:grow";
		FormLayout panelLayout = new FormLayout(panelColumns, panelRows);
		mainPanel.setLayout(panelLayout);
		programmingPanel = ceateProgrammingPanel();
		eepromPanel = createEEpromPanel();
		
		tabbedPane.add(programmingPanel);
		
		programmerPanel = new ProgrammerPanel(this, programmers);
		controllerPanel = new ControllerPanel(this, avrs);

		mainPanel.add(programmerPanel, cc.xy(1, 2));
		mainPanel.add(controllerPanel, cc.xy(1, 4));
		mainPanel.add(tabbedPane, cc.xy(1, 6));

		logger.log(Level.INFO, "Set the port from the properties [usb]: " + settings.getProperty("port", "usb"));
		
		programmerPanel.setPort(settings.getProperty("port", "usb"));
		programmerPanel.setRate(settings.getProperty("rate", "19200"));
		programmerPanel.setDefaultRate(new Boolean(settings.getProperty("defaultRate", "true")));
		programmerPanel.setProgrammer(settings.getProperty("programmer",
				"usbasp"));
		controllerPanel.setController(settings.getProperty("controller",
				"Blackboard"));
		
//		testPanel = new TestPanel();
//		
		tabbedPane.addTab(_("programmingPanel.title"), programmingPanel);
		tabbedPane.setBorder(new LineBorder(Color.WHITE));
		tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);
		
		
		tabbedPane.addTab(_("eepromPanel.title"), eepromPanel);
		tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);
		
		tabbedPane.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent event) {
				JTabbedPane sourceTabbedPane = (JTabbedPane) event.getSource();
				if (sourceTabbedPane.getSelectedComponent().equals(eepromPanel)) {
					logger.info("changed to eepromPanel");
					eepromResetPanel.firePropertyChange(ControllerPanel.CONTROLLER_CHANGED, 0, 1);
				} else if (sourceTabbedPane.getSelectedComponent().equals(programmingPanel)) {
					logger.info("changed to programmingPanel");
				}
				
			}
		});
		
		
		mainframe = new JFrame(_("title") + " " + VERSION + (isBeta?" beta " + betaVersion:"") + " - " + mode);
//		mainframe.setPreferredSize(preferredSize);
//		mainframe.setSize(preferredSize);

//		mainframe.add(tabbedPane);
		mainframe.add(mainPanel);
		mainframe.setJMenuBar((new kkMenu(this, firmwareReader)).getBar());
		mainframe.setIconImage(Icons.getImage(Icons.app, 16));

		mainframe.pack();
		mainframe.setLocationRelativeTo(getRootPane());

		mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		firmwarePanel.firePropertyChange(ControllerPanel.CONTROLLER_CHANGED, 0, 1);
		

	}

	private JPanel createEEpromPanel() {
		JPanel panel = new JPanel();
		CellConstraints cc = new CellConstraints();

		// create the Layout for Panel this
		String panelColumns = "pref:grow";
		String panelRows = "pref,3dlu,fill:pref:grow,3dlu,pref";
		FormLayout panelLayout =  new FormLayout(panelColumns, panelRows);
		panel.setLayout(panelLayout);
		
		eepromResetPanel = new EEpromResetPanel(this);
		eepromSettingsPanel = new EEpromSettingsPanel(this);
		
		panel.add(eepromResetPanel, cc.xy(1, 1));
		panel.add(eepromSettingsPanel, cc.xy(1, 3));
		// TODO Auto-generated method stub
		return panel;
	}


	private JPanel ceateProgrammingPanel() {
		JPanel panel = new JPanel();
		CellConstraints cc = new CellConstraints();

		// create the Layout for Panel this
		String panelColumns = "pref:grow";
		String panelRows = "pref,3dlu,fill:pref";
		FormLayout panelLayout =  new FormLayout(panelColumns, panelRows);
		panel.setLayout(panelLayout);
		
		firmwarePanel = new FirmwarePanel(this, firmwareReader);
		output = new JTextArea();
		output.setColumns(20);
		output.setEditable(false);
		output.setRows(15);
		outputPane = new JScrollPane(output);
		outputPane.setAutoscrolls(true);
		outputPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		
		panel.add(firmwarePanel, cc.xy(1, 1));
		panel.add(outputPane, cc.xy(1, 3));
		
//		panel.setBorder(new LineBorder(Color.WHITE));
		
		return panel;
	}


	public void setLocale(String language, String country) {
		this.locale = new Locale(language, country);
		logger.log(Level.INFO, language + "_" + country);
	}
	
	public Locale getLocale() {
		return locale;
	}

	private void loadTranslation() {
		// load the supported locales and the default locale
		Translatrix
				.loadSupportedLocales("de.lazyzero.kkMulticopterFlashTool.gui.resources.supportedLocales");
		Translatrix.setLocale(Translatrix.getDefaultLocale());
		// load resources for core package
		@SuppressWarnings("rawtypes")
		Vector locales = Translatrix.getSupportedLocales();

		if (locales.contains(locale)) {
			Translatrix.setLocale(locale);
		} else {
			locale = Translatrix.getLocale();
		}
		Translatrix
				.addBundle("de.lazyzero.kkMulticopterFlashTool.gui.resources.translatrix");

	}

	public boolean restart() {
		int response = JOptionPane.showConfirmDialog(mainframe,
				_("restart.dialog"), _("restart.title"),
				JOptionPane.YES_NO_OPTION);
		if (response == 0)
			return true;
		else
			return false;
	}

	/**
	 * @return the mainframe
	 */
	public JFrame getMainframe() {
		return mainframe;
	}

	/**
	 * @return the settings
	 */
	public Properties getSettings() {
		return settings;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new KKMulticopterFlashTool(args);

		kk.mainframe.setVisible(true);
	}

	public void print(final String line) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				output.append(line);
			}
		});
	}

	public void println(final String line) {
		logger.log(Level.INFO, line);

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				output.append(line);
				output.append("\n");
			}
		});
	}

	public void clearText() {

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				output.setText("");
			}
		});
	}
	
	@Override
	public String getText() {
		return output.getText();
	}


	/**
	 * @return the programmer
	 */
	public AvrdudeProgrammer getProgrammer() {
		return programmer;
	}

	/**
	 * @param programmer
	 *            the programmer to set
	 */
	public void setProgrammer(AvrdudeProgrammer programmer) {
		this.programmer = programmer;
	}

	/**
	 * @return the controller
	 */
	public AVR getController() {
		return controller;
	}

	/**
	 * @param controller
	 *            the controller to set
	 */
	public void setController(AVR controller) {
		this.controller = controller;
	}

	public void setFirmware(Firmware firmware) {
		this.firmware = firmware;
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if (evt.getPropertyName().equals(ControllerPanel.CONTROLLER_CHANGED)) {
			firmwarePanel.firePropertyChange(ControllerPanel.CONTROLLER_CHANGED, 0, 1);
			eepromResetPanel.firePropertyChange(ControllerPanel.CONTROLLER_CHANGED, 0, 1);
		}
		if (evt.getPropertyName().equals(MODE_CHANGED)) {
			firmwarePanel.firePropertyChange(ControllerPanel.CONTROLLER_CHANGED, 0, 1);
			if (isOfflineMode()) {
				mode = _("offline");
			} else {
				mode = _("online");
			}
			this.mainframe.setTitle(_("title") + " " + VERSION + " - " + mode);
		}
	}



	public static String getTempFolder() {
		String tmpdir;
		if (System.getProperty("os.name").toLowerCase().contains("windows")) {
            tmpdir = System.getProperty("user.dir")+"\\tmp\\";
        } else if (System.getProperty("os.name").toLowerCase().contains("mac")) {
			tmpdir = System.getProperty("user.home")+"/Library/Preferences/kkMulticopterFlashTool/";
		} else {
        	tmpdir = System.getProperty("user.dir")+"/tmp/";
        }
		KKMulticopterFlashTool.getLogger().log(Level.INFO, tmpdir);
		return tmpdir;
	}


	public static boolean isOfflineMode() {
		try {
			return kk.offlineMode;
		} catch (Exception e) {
			return false;
		}
	}


	public static void setOfflineMode(boolean b) {
		kk.offlineMode = b;
	}


	public Vector<Firmware> getFirmwares() {
		return firmwareReader.getFirmwares();
	}

	public boolean isForceFlashing() {
		return forceFlashing;
	}


	public void setForceFlashing(boolean forceFlashing) {
		this.forceFlashing = forceFlashing;
	}
	
	public static Logger getLogger() {
		try {
			return kk.logger;
		} catch (Exception e) {
			return Logger.getLogger("Default-Logger");
		}
	}

	public static Vector<AVR> getControllers() {
		return avrs;
	}


	public void setSelectedTabIndex(int i) {
		tabbedPane.setSelectedIndex(i);
	}

}
