package nl.nikhef.safaripark;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.LogManager;

import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.xml.stream.XMLStreamException;

import org.codejargon.feather.Feather;
import org.codejargon.feather.Provides;

import nl.nikhef.safaripark.devmgr.BaySelectionListener;
import nl.nikhef.safaripark.devmgr.DeviceManager;
import nl.nikhef.safaripark.editpane.EditPane;
import nl.nikhef.safaripark.extra.GuiUtils;
import nl.nikhef.safaripark.monitor.Monitor;
import nl.nikhef.safaripark.res.Resources;
import nl.nikhef.sfp.SFPDevice;
import nl.nikhef.sfp.SFPManager;
import nl.nikhef.sfp.ddmi.DDMILoader;
import nl.nikhef.sfp.ddmi.DDMI;

@SuppressWarnings("serial")
public class SaFariPark extends JFrame implements BaySelectionListener, WindowListener, DeferredListener {

	
	private static final int SCAN_DEVICE_DELAY = 2;
	
	private DeviceManager 	_devMgr;
	private EditPane   		_tabEdit;
	private ActiveLogArea   _logOut;
	private Monitor			_monitor;
	private StatusBar       _status;
	private Timer			_timer;
	private int  			_scanForNewDevices = SCAN_DEVICE_DELAY;
	
	private SFPManager		_sfpManager;
	private DDMILoader		_loader;
	private ContextCache	_ctxCache;
	private OverlayManager	_ovlMgr;
	private ModuleManager   _modMgr;
	
	private Action _selectOverlays = new ExtendedAbstractAction("Select overlays", Resources.getIcon("books"), "Select which SFP+ overlays to load") {
		
		@Override
		public void actionPerformed(ActionEvent e) 
		{
			
			_ovlMgr.showDialog(SaFariPark.this);
		}
	};
	
	private Action _saveBinary = new ExtendedAbstractAction("Save module contents", Resources.getIcon("document-export-4"), "Save a binary export") {
		
		@Override
		public void actionPerformed(ActionEvent e) 
		{
			_modMgr.exportBinary(SaFariPark.this);
		}
	};
	
	
	

	
	

	@Inject
	public SaFariPark(SFPManager sfpMgr, DDMILoader loader, OverlayManager ovlMgr) throws IOException, XMLStreamException 
	{
		

		setLayout(new BorderLayout());
		setTitle("SaFariPark");
		setSize(640, 480);
		setIconImage(loadImage("res/appicon.png"));
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		
		_sfpManager = sfpMgr;	
		_loader = loader;
		_ovlMgr = ovlMgr;
		_ctxCache = new ContextCache(_sfpManager, _loader);
		
		_devMgr = new DeviceManager(_sfpManager);
		_devMgr.addDeviceSelectedListener(this);
		
		_tabEdit = new EditPane(_ctxCache);
		_logOut = new ActiveLogArea();
		_monitor = new Monitor(_loader, _ctxCache, _sfpManager);
		_status = new StatusBar();
		_modMgr = new ModuleManager(_saveBinary, _loader, _ctxCache);
		_devMgr.addDeviceSelectedListener(_modMgr);
		makeToolbar();
		JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		split.setLeftComponent(_devMgr);
		JSplitPane split2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
		split.setRightComponent(split2);
		split2.setTopComponent(_tabEdit);
		split2.setResizeWeight(0.9);
		
		JTabbedPane tp = new JTabbedPane();
		tp.addTab("Monitor", _monitor);
		tp.addTab("Logging", _logOut);
		
		split2.setBottomComponent(tp);
		split.setResizeWeight(0.1);
		add(split, BorderLayout.CENTER);
		add(_status, BorderLayout.SOUTH);
		setExtendedState(JFrame.MAXIMIZED_BOTH);
		addWindowListener(this);
		
		
		_timer = new Timer(1000, new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				if (_scanForNewDevices == 0) { 
					_sfpManager.update(true);
					_scanForNewDevices = SCAN_DEVICE_DELAY;
				} else {
					_sfpManager.update(false);
					_scanForNewDevices--;
				}
				
			}
		});
		_timer.setRepeats(true);
	}
	
	
	
	private void makeToolbar() 
	{
		JToolBar tb = new JToolBar();
		tb.add(_selectOverlays);
		tb.add(_saveBinary);
		
		GuiUtils.toolbarTextButtions(tb);
		add(tb, BorderLayout.NORTH);
	}
	
	
	private static class SaFariParkModule 
	{
		
		@Provides
		@Singleton
		OverlayManager ovlMgr() {
			OverlayManager ovlMgr = new OverlayManager(); 
			ovlMgr.scanDirectory(new File("overlays"));
			return ovlMgr;
		}
		
		@Provides
		@Singleton
		DDMILoader ddmiLoader(OverlayManager ovl) throws IOException, XMLStreamException {
			DDMILoader loader = new DDMILoader();
			loader.loadOverlays(ovl.getOverlays());
			return loader;
		}
		
	}
	
	public static void main(String[] args) 
	{
		System.out.printf("Starting SaFariPark on %s Java %s (%s bit) and %s (OS=%s, %s)\n",
				System.getProperty("java.vendor"),
				System.getProperty("java.version"),
				System.getProperty("sun.arch.data.model") ,
				System.getProperty("os.name"),
				System.getProperty("os.version"),
				System.getProperty("os.arch"));
		
		Locale.setDefault(Locale.US);
		
		try {
			UIManager.setLookAndFeel(
			            UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e) {
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		} catch (UnsupportedLookAndFeelException e) {
		}

		LogManager.getLogManager().getLogger("").setLevel(Level.INFO);
		
		
		
		if (args.length == 2 && args[0].equals("--setdpi")){
			
			
			if (args[1].equals("auto")) {
				setDpi(-1);
			} else {
				setDpi(Integer.parseInt(args[1]));
			}
		}
		
		/*
		SaFariPark sfpE;
		try {
			sfpE = new SaFariPark();
			sfpE.setVisible(true);
			sfpE.init();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XMLStreamException e) {
			e.printStackTrace();
		}*/
		
		Feather feather = Feather.with(new SaFariParkModule());
		SaFariPark sfpE = feather.instance(SaFariPark.class);
		sfpE.setVisible(true);
		sfpE.init();
	}
				 
	
	public static void setDpi(int dpi) {
		
		
		if (dpi == -1) {
			dpi = Toolkit.getDefaultToolkit().getScreenResolution();
		}
		
		
		// no high dpi screen, exit.
		//if (dpi < 100) return;
		//dpi = 45;
		
		float scale = (float)dpi / 96f;
		
		UIDefaults d = UIManager.getLookAndFeel().getDefaults();
		
		
		
		for (Entry<Object, Object> x : d.entrySet() )
		{
			
			Object o = UIManager.get(x.getKey());
			if (o == null) continue;
			if ((o instanceof Font)) {
				Font f = Font.class.cast(o);
				
				UIManager.put(x.getKey(), f.deriveFont(f.getSize2D() * scale));
			}
			if (x.getKey().toString().equals("Tree.rowHeight")) {
				UIManager.put(x.getKey(), (int)( Integer.class.cast(o).intValue() * scale));
			}
		}
	}

	public void init() {
		
		SwingUtilities.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				_tabEdit.initEditor(_loader);
				_timer.start();
			}
		});

	}
	
	public void deinit() {
		_timer.stop();
	}

	
	@Override
	public void baySelected(SFPDevice dev, int bay) {
		try {
			_tabEdit.populate(dev, bay);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public boolean canSelectBay(SFPDevice dev, int bay) {
		if (_tabEdit.getContext().someAreDirty()) {
			if (JOptionPane.showConfirmDialog(this, 
					"Editor contains changes which are not yet applied\n"
					+ "Press OK to discard the changes and edit the selected device\n"
					+ "Press CANCEL to return to keep editing the current device", 
					"Warning", JOptionPane.OK_CANCEL_OPTION,
					JOptionPane.WARNING_MESSAGE) == JOptionPane.CANCEL_OPTION) {
				return false;
			}
		}
		return true;
	}

	public static ImageIcon loadImageIcon(String path,
            String description) {
		java.net.URL imgURL = SaFariPark.class.getResource(path);
		if (imgURL != null) {
				return new ImageIcon(imgURL, description);
		} else {
			System.err.println("Couldn't find file: " + path);
			return null;
		}
	}

	public static BufferedImage loadImage(String path) {
		java.net.URL imgURL = SaFariPark.class.getResource(path);
		try {
			return ImageIO.read(imgURL);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}




	@Override
	public void windowOpened(WindowEvent e) {
	}



	@Override
	public void windowClosing(WindowEvent e) {
		deinit();
	}



	@Override
	public void windowClosed(WindowEvent e) {
	}



	@Override
	public void windowIconified(WindowEvent e) {
	}


	@Override
	public void windowDeiconified(WindowEvent e) {
	}

	@Override
	public void windowActivated(WindowEvent e) {
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
	}



	private int _max = 0;
	
	@Override
	public synchronized void deferredTasksUpdated(int tasksQueued)
	{
		if (tasksQueued > _max) {
			_max = tasksQueued;
		}
		_status.setProgress(_max - tasksQueued, _max);
		//else _status.setDone();
	}

	


	
}
