package FolderSplit;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

public class MainPanel extends JPanel {
	private static final long serialVersionUID = 7343170110719276986L;

	JFrame frame;
	JTree preview = new JTree(new Object[0]);
	
	boolean running;
	
	JTextField rootPath = new JTextField(new File(".").getAbsolutePath(), 10);
	JButton selectRootPath = new JButton(new AbstractAction("Select") {
		private static final long serialVersionUID = 115515209554809420L;
		@Override public void actionPerformed(ActionEvent e) {
			JFileChooser chooser = new JFileChooser();
			chooser.setDialogTitle("Select Direcotry");
			chooser.setCurrentDirectory(new java.io.File(rootPath.getText()));
			chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			chooser.setAcceptAllFileFilterUsed(false);
			if (chooser.showOpenDialog(MainPanel.this) == JFileChooser.APPROVE_OPTION) {
				rootPath.setText(chooser.getSelectedFile().getAbsolutePath());
			}
		}
	});

	JTextField filter = new JTextField("*", 10);
	JComboBox filterType = new JComboBox(new String[] {"Simple", "RegEx"});

	JCheckBox orderInverse = new JCheckBox("Inverse");
	JComboBox orderType = new JComboBox(new String[] {"Name", "Extension", "Size", "Modification"});

	JTextField pattern = new JTextField("Files{count /7 +1 %03d}", 10);
	JComboBox prePattern = new JComboBox(new ComboBoxEntry[] {
			new ComboBoxEntry("Files{count /7 +1 %03d}", "7 Files per Directory"),
			new ComboBoxEntry("Files{count /10 +1 %03d}", "10 Files per Directory"),
			new ComboBoxEntry("Files{count /100 +1 %03d}", "100 Files per Directory"),
			new ComboBoxEntry("Files{count /1000 +1 %03d}", "1000 Files per Directory"),
			new ComboBoxEntry("{name substr0,1 lower fb_}", "First Letter"),
			new ComboBoxEntry("{name substr0,1 lower fb_}/{name substr1,2 lower fb_}", "First Letter/Second Letter"),
			new ComboBoxEntry("{name substr0,1 lower fb_}/{name substr1,2 lower fb_}/{name substr2,3 lower fb_}", "First Letter/Second Letter/Third Letter"),
			new ComboBoxEntry("{modified %1$tY-%1$tm-%1$td}", "Year-Month-Day"),
			new ComboBoxEntry("{modified %1$tY/%1$tm/%1$td}", "Year/Month/Day"),
			new ComboBoxEntry("{extension}", "Extension"),
			new ComboBoxEntry("{extension lower}", "Extension lowercase"),
			new ComboBoxEntry("{extension upper}", "Extension uppercase"),
			new ComboBoxEntry(null, "Custom"),
	});
	
	JButton refresh = new JButton(new AbstractAction("Update Preview") {
		private static final long serialVersionUID = 115515209554809420L;
		@Override public void actionPerformed(ActionEvent e) {
			updatePreview();
		}
	});
	JButton move = new JButton(new AbstractAction("Move Files") {
		private static final long serialVersionUID = 115515209554809420L;
		@Override public void actionPerformed(ActionEvent e) {
			performMovements(false);
		}
	});
	JButton copy = new JButton(new AbstractAction("Copy Files") {
		private static final long serialVersionUID = 115515209554809420L;
		@Override public void actionPerformed(ActionEvent e) {
			performMovements(true);
		}
	});
	
	private class ComboBoxEntry {
		final String value, display;
		public ComboBoxEntry(String value, String display) {
			this.value = value;
			this.display = display;
		}
		@Override
		public String toString() {
			return display;
		}
	}
	
	public MainPanel(JFrame frame) {
		super(new BorderLayout());
		this.frame = frame;
		
		JPanel leftPanel = new JPanel(new GridLayout(0,3));
		JPanel southPanel = new JPanel();
		
		JPanel panel = new JPanel();
		panel.add(leftPanel);
		add(panel, BorderLayout.NORTH);
		add(new JScrollPane(preview), BorderLayout.CENTER);
		add(southPanel, BorderLayout.SOUTH);
		
		leftPanel.add(new JLabel("Directory:"));
		leftPanel.add(rootPath);
		leftPanel.add(selectRootPath);

		leftPanel.add(new JLabel("Filter:"));
		leftPanel.add(filter);
		leftPanel.add(filterType);

		leftPanel.add(new JLabel("Order:"));
		leftPanel.add(orderType);
		leftPanel.add(orderInverse);

		leftPanel.add(new JLabel("Pattern:"));
		leftPanel.add(pattern);
		leftPanel.add(prePattern);
		
		southPanel.add(refresh);
		southPanel.add(move);
		southPanel.add(copy);
		
		pattern.setEnabled(false);
		prePattern.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				String val = ((ComboBoxEntry) prePattern.getSelectedItem()).value;
				if (val == null) {
					pattern.setEnabled(true);
				} else {
					pattern.setText(val);
					pattern.setEnabled(false);
				}
			}
		});
	}
	
	public String getFilter() {
		String f = filter.getText();
		if ("Simple".equals(filterType.getSelectedItem().toString())) {
			for (String c : new String[] {"\\", ".", "[", "]", "{", "}", "(", ")", "$", "^", "|"})
				f = f.replaceAll("\\" + c, "\\\\" + c);
			f = f.replaceAll("\\*", ".*");
			f = f.replaceAll("\\?", ".");
			f = "^" + f + "$";
		}
		return f;
	}
	
	public void updatePreview() {
		File rootFolder = new File(rootPath.getText());
		Map<File, File> movs = getFileMovements();
		Map<File, DefaultMutableTreeNode> nodes = new HashMap<File, DefaultMutableTreeNode>();
		DefaultMutableTreeNode root = new DefaultMutableTreeNode(rootFolder.getName());
		nodes.put(rootFolder, root);
		for (Entry<File, File> mov : movs.entrySet()) {
			DefaultMutableTreeNode node = new DefaultMutableTreeNode(mov.getValue().getName());
			File parent = mov.getValue().getParentFile();
			DefaultMutableTreeNode parentNode = nodes.get(parent); 
			while (parentNode == null) {
				parentNode = new DefaultMutableTreeNode(parent.getName());
				nodes.put(parent, parentNode);
				parentNode.add(node);
				node = parentNode;
				parent = parent.getParentFile();
				parentNode = nodes.get(parent);
			}
			parentNode.add(node);
		}
		preview.setModel(new DefaultTreeModel(root));
	}
	
	public Map<File, File> getFileMovements() {
		Map<File, File> map = new LinkedHashMap<File, File>();
		try {
			File rootFolder = new File(rootPath.getText());
			
	        FileCollector fc = new FileCollector();
	        fc.scanDirectory(rootFolder);
	        List<FileInfo> fileInfos = fc.getFileInfos(getFilter());
	        Collections.sort(fileInfos, FileSorter.valueOf(orderType.getSelectedItem().toString()));
	        if (orderInverse.isSelected())
	        	Collections.reverse(fileInfos);
	        FolderChooser folderC = new FolderChooser(pattern.getText());
	        int num = 0;
	        for (FileInfo fileInfo : fileInfos) {
	        	String newFile = rootFolder.getAbsolutePath() + '/' + folderC.getFolder(fileInfo, num++) + '/' + fileInfo.name;
	        	map.put(fileInfo.file, new File(newFile));
	        }
		} catch (Exception e) {
			JOptionPane.showMessageDialog(this, e.getMessage(), "An " + e.getClass().getSimpleName() + " has occured", JOptionPane.ERROR_MESSAGE);
		}
		return map;
	}
	
	public synchronized void performMovements(final boolean copy) {
		running = true;
		final Map<File, File> movs = getFileMovements();
		final JDialog dialog = new JDialog(frame, "Splitting Folder");
		final JProgressBar progressBar = new JProgressBar(0, movs.size());
		dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
		dialog.setModal(true);
		dialog.add(new JButton(new AbstractAction("Abort") {
			private static final long serialVersionUID = -1437821380687571964L;
			@Override
			public void actionPerformed(ActionEvent e) {
				running = false;
			}
		}), BorderLayout.CENTER);
		dialog.add(progressBar, BorderLayout.SOUTH);
		dialog.pack();
		dialog.setLocationRelativeTo(frame);
		new Thread() {
			@Override
			public void run() {
				int i = 0;
				try {
					for (Entry<File, File> mov : movs.entrySet()) {
						System.out.println(mov.getKey() + " -> " + mov.getValue());
						if (copy) {
							mov.getValue().getParentFile().mkdirs();
								copyfile(mov.getKey(), mov.getValue());
						} else {
							mov.getValue().getParentFile().mkdirs();
							mov.getKey().renameTo(mov.getValue());
						}
						progressBar.setValue(i++);
						if (!running)
							break;
					}
				} catch (Exception e) {
					JOptionPane.showMessageDialog(MainPanel.this, e.getMessage(), "An " + e.getClass().getSimpleName() + " has occured", JOptionPane.ERROR_MESSAGE);
				} finally {
					dialog.setVisible(false);
				}
			}
		}.start();
		dialog.setVisible(true);
	}
	
	private static void copyfile(File from, File to) throws IOException {
		InputStream in = new FileInputStream(from);
		OutputStream out = new FileOutputStream(to);
		byte[] buf = new byte[16 * 1024 * 1024];
		int len;
		while ((len = in.read(buf)) > 0) {
			out.write(buf, 0, len);
		}
		in.close();
		out.close();
	}
	
	public static void main(String[] args) {
		JFrame frame = new javax.swing.JFrame("Folder Splitter V1.0 by Max Nagl (http://siebn.de)");
		frame.add(new MainPanel(frame));
		frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
		frame.pack();
		frame.setSize(frame.getWidth(), 600);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
	}
}
