package nl.nikhef.safaripark;

import java.awt.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

import nl.nikhef.safaripark.devmgr.BaySelectionListener;
import nl.nikhef.sfp.SFPDevice;
import nl.nikhef.sfp.ddmi.DDMI;
import nl.nikhef.sfp.ddmi.DDMIContext;
import nl.nikhef.sfp.ddmi.DDMILoader;
import nl.nikhef.sfp.ddmi.DataSource;
import nl.nikhef.tools.Utils;


public class ModuleManager implements BaySelectionListener 
{
	
	private Action _binExport;
	private JFileChooser _dirChooser;
	private JRadioButton _binary;
	private JRadioButton _plainhex;
	private JRadioButton _extendedhex;
	
	private DDMI _ddmi;
	private ContextCache _ctxCache;
	private DDMIContext _ctx;
	private String _moduleName;
	private DateFormat _fileDateFormat = new SimpleDateFormat("yyMMdd_HHmmss");

	public ModuleManager(Action binExport, AppContext appCtx)
	{
		_ctxCache = appCtx.ctxCache;
		_binExport = binExport; 
		_binExport.setEnabled(false);
		_dirChooser = new JFileChooser();
		
		
		_ddmi = appCtx.ddmiLdr.getDDMI();
		
		ButtonGroup bg = new ButtonGroup();
		_binary = new JRadioButton("Binary (multiple files)");
		_plainhex = new JRadioButton("Plain Hex (multiple files)");
		_extendedhex = new JRadioButton("Extended Hex (single file)");
		bg.add(_plainhex);
		bg.add(_extendedhex);
		bg.add(_binary);
		_extendedhex.setSelected(true);

		JPanel pnl = new JPanel();
		pnl.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
		pnl.setLayout(new BoxLayout(pnl, BoxLayout.PAGE_AXIS));		
		pnl.add(new JLabel("Export format"));
		pnl.add(_extendedhex);
		pnl.add(_plainhex);
		pnl.add(_binary);
		
		_dirChooser.setAccessory(pnl);
		
		_dirChooser.setDialogTitle("Select directoy to export module contents to");
		_dirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
	}


	@Override
	public void baySelected(SFPDevice dev, int bay) {
		try {
			if (dev.isModulePresent(bay)) 
			{
				_ctx = _ctxCache.getContextFor(dev, bay);
				
				_moduleName = dev.getModuleName(bay);
				_binExport.setEnabled(true);
			} else {
				_ctx = null;
				_binExport.setEnabled(false);
			}
		} catch (IOException e) {
			_ctx = null;
			_binExport.setEnabled(false);
		}

	}
	
	public void exportBinary(Component parent)
	{
		if ((_dirChooser.showDialog(parent, "Select") != JFileChooser.APPROVE_OPTION)) 
		{
			return;
		}
		
		File f = _dirChooser.getSelectedFile();
		
		
		
		String filebasebase =String.format("sfp_%s_%s", _moduleName, _fileDateFormat.format(new Date())); 
		
		List<String> filesWritten = new ArrayList<String>();
		
		List<DataSource> orderedSources = new ArrayList<DataSource>();
		
		orderedSources.addAll(_ddmi.getSources());
		
		Comparator<DataSource> comparator = new Comparator<DataSource>() {
		    @Override
		    public int compare(DataSource left, DataSource right) {
		        return left.getPath().compareTo(right.getPath());
		    }
		};
		
		Collections.sort(orderedSources, comparator);

		
		if (_extendedhex.isSelected()) {
			
			String filename = filebasebase + ".txt";

			try {
				PrintWriter pw = new PrintWriter(new FileWriter(new File(f, filename)));

				
				try {
					for (DataSource ds : orderedSources) 
					{
						if (!ds.isValid(_ctx)) continue;
						// System.out.println("Exporting datasource " + ds);
						
						try {
			
							byte[] data = ds.read(_ctx, ds.start, ds.size());
							
							pw.println("Section: " + ds.getPath());
							exportHex(f, pw, data, true, ds.start);	
							pw.println();
						} catch (IOException e) {
							e.printStackTrace();
							continue;
						}
						
					}
				} finally {
					filesWritten.add(filename);
					try {
						pw.close();
					} catch (Exception e) {};
				}
			} catch (IOException io) {
				
			}
			
		} else {
		
			for (DataSource ds : orderedSources) 
			{
				if (!ds.isValid(_ctx)) continue;
				// System.out.println("Exporting datasource " + ds);
		
				try {
	
					byte[] data = ds.read(_ctx, ds.start, ds.size());
					String filebase = filebasebase + "_" + ds.getPath();
					
					if (_plainhex.isSelected()) {
						exportHex(filesWritten, f, filebase, data, false, 0);	
					}
					if (_binary.isSelected()) {
						exportBinary(filesWritten, f, filebase, data);
					}
	
				} catch (IOException e) {
					e.printStackTrace();
					continue;
				}
				
			}
		}
		
		StringBuilder sb = new StringBuilder();
		sb.append("Following file(s) have been written:\n");
		for (String s : filesWritten) {
			sb.append(" * " + s + "\n");
		}
		sb.append("In directory: \n");
		sb.append("  " + f.getPath());
		
		JOptionPane.showMessageDialog(parent, sb.toString(), "Export success!", JOptionPane.INFORMATION_MESSAGE);	
	
	}

	
	
	private void exportHex(List<String> filesWritten, File f, String filebase, byte[] content, boolean extended, int offset) throws IOException 
	{
		PrintWriter pw = new PrintWriter(new FileWriter(new File(f, filebase + (extended ? ".txt" : ".hex"))));
		try {
			exportHex(f, pw, content, extended, offset);
			filesWritten.add(filebase +  (extended ? ".txt" : ".hex"));
		} finally {
			pw.close();
		}
	}


	private void exportHex(File f, PrintWriter pw, byte[] content, boolean extended, int offset) throws IOException 
	{
		Utils.dumpHex(pw, content, extended, offset);
	}
	
	
	private void exportBinary(List<String> filesWritten, File f, String filebase, byte[] content) throws IOException 
	{
		FileOutputStream fos = new FileOutputStream(new File(f, filebase + ".bin"));
		try {
			fos.write(content);
			filesWritten.add(filebase + ".bin");
		} finally {
			fos.close();
		}
	}
	
	@Override
	public boolean canSelectBay(SFPDevice dev, int bay) {
		return true;
	}

}
