/*
 * @(#)MD5Verify.java
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 2003 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDcreator.java for details.
 */

import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.security.MessageDigest;
import javax.swing.*;

/**
 * A MD5 sums checker.
 *
 * @version 20030429
 * @author Jörg P. M. Haeger
 */
class MD5Verify {
	private static boolean stdout = false;
	private static java.util.TreeMap treeMap;
	private static int errors = 0, OKs = 0;

	private static JFrame frame;
	private static JTextArea textArea;
	private static JScrollPane scrollPane;
	private static JLabel errorLabel, OKLabel;

	static String bytesToHex(byte[] bs) {
		String hexStr = "";
		for (int i = 0; i < bs.length; i++) {
			int c1 = (bs[i] & 0xF0) >> 4;
			if (c1 < 10)
				c1 += '0';
			else
				c1 += 'a' - 10;

			int c2 = bs[i] & 0x0F;
			if (c2 < 10)
				c2 += '0';
			else
				c2 += 'a' - 10;

			hexStr = hexStr + (char)c1 + (char)c2;
		}
		return hexStr;
	}

	static JMenuBar createMenuBar() {
		JMenuBar menuBar = new JMenuBar();
		JMenu menu;
		JMenuItem item;
		menu = new JMenu("File");

			item = new JMenuItem("Quit");
			item.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					System.exit(0);
				}
			});
			menu.add(item);

		menuBar.add(menu);
		menu = new JMenu("Help");

			item = new JMenuItem("About");
			item.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					JOptionPane.showMessageDialog(frame,
						new JLabel("<html>"
							+ Version.product
							+ " " + Version.version + "<br>"
							+ Version.copyright + "<br>"
							+ "<a href=\"" + Version.authorHome + "\">"
								+ Version.authorHome + "</a>"
							+ "</html>"));
				}
			});
			menu.add(item);

		menuBar.add(menu);
		return menuBar;
	}

	static JComponent createInfoPanel() {
		MyBox vbox = new MyBox(BoxLayout.Y_AXIS);
		vbox.setAlignmentX(Box.LEFT_ALIGNMENT);
		OKLabel = new JLabel();
		vbox.add(OKLabel);
		errorLabel = new JLabel();
		showErrorNum();
		vbox.add(errorLabel);
		return vbox;
	}

	static String getMD5Sum(File file) throws Exception {
		InputStream inStream = new FileInputStream(file);
		MessageDigest MD5 = MessageDigest.getInstance("MD5");
		while (true) {
			byte[] bs = new byte[4 * 1024];
			int n = inStream.read(bs);
			if (n < 0)
				break;
			MD5.update(bs, 0, n);
		}
		return bytesToHex(MD5.digest());
	}

	static String format(int n, int width) {
		StringBuffer str = new StringBuffer();
		str.append(n);
		for (int i = 0; str.length() < width; i++)
			str.insert(0, ' ');
		return new String(str);
	}

	public static void main(String[] args) throws Exception {
		int arg0 = 0;
		if (args.length > arg0 && args[arg0].equals("--stdout")) {
			stdout = true;
			println(Version.product + " " + Version.version
				+ " " + Version.copyright);
			arg0++;
		}

		frame = new JFrame("MD5Verify");
		Rectangle screenRect
			= frame.getGraphicsConfiguration().getBounds();
		int width = 640;
		int height = 480;
		frame.setBounds(screenRect.x + (screenRect.width - width) / 2,
			screenRect.y + (screenRect.height - height) / 2,
			width, height);
		frame.addWindowListener(new java.awt.event.WindowAdapter() {
			public void windowClosing(java.awt.event.WindowEvent e) {
				System.exit(0);
			}
		});
		frame.setJMenuBar(createMenuBar());
		textArea = new JTextArea();
		scrollPane = new JScrollPane(textArea);

		MyBox vbox = new MyBox(BoxLayout.Y_AXIS);
		vbox.setAlignmentX(Box.LEFT_ALIGNMENT);
		vbox.add(createInfoPanel());
		vbox.add(scrollPane);

		frame.getContentPane().add(vbox);
		if (!stdout)
			frame.setVisible(true);

		treeMap = new java.util.TreeMap();

		if (args.length > arg0)
			for (int i = arg0; i < args.length; i++)
				verify(new File(args[i]));
		else {
/*			File[] roots = File.listRoots();
			for (int i = 0; i < roots.length; i++) {
				print("root " + roots[i] + "\n");
				verify(roots[i]);
*/
			java.net.URL url = new Integer(0).getClass().getClassLoader()
				.getSystemResource("MD5Verify.class");
//			println("" + url);
//			println(url.getPath());

			String URLStr = url.toString();
			if (URLStr.startsWith("jar:"))
				URLStr = URLStr.substring(4);
			if (URLStr.startsWith("file:"))
				URLStr = URLStr.substring(5);
			int pos = URLStr.indexOf(".jar!");
			if (pos > 0)
				URLStr = URLStr.substring(0, pos + 4);

			File file = new File(URLStr);
			verify(file.getParentFile());
		}
	}

	static void print(String str) {
		if (stdout) {
			System.out.print(str);
			System.out.flush();
			return;
		}

		textArea.append(str);

		// scroll down if the user has not moved the cursor
		int p = textArea.getCaretPosition();
		if (p == textArea.getText().length() - str.length())
			textArea.setCaretPosition(p + str.length());
	}

	static void println(String str) {
		print(str + "\n");
	}

	static void showErrorNum() {
		errorLabel.setText(format(errors, 5) + " files differ");
	}

	static void verify(File file) throws Exception {
		if (!file.isDirectory()) {
			verifyFile(file);
			return;
		}

		File dir = file;
		println("directory to verify = " + dir);
		String[] names = dir.list(
			new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.endsWith(".md5");
			}
		});

		if (names == null)
			return;

		java.util.Arrays.sort(names);

		for (int i = names.length - 1; i >= 0; i--) {
			println("");
			println(names[i] + " -->");
			verify(new File(dir, names[i]));
		}

		println("");
		println("" + OKs + " MD5 checksums are identical");
		println("" + errors + " MD5 checksums differ");
	}

	static void verifyFile(File file) throws Exception {
		BufferedReader reader = new BufferedReader(
			new InputStreamReader(
				new FileInputStream(file), "UTF-8"));

		while (true) {
			String line = reader.readLine();
			if (line == null)
				break;

			String MD5 = line.substring(0, 32);
			String fileName = line.substring(34);
			print(fileName + "... ");
			File file2 = new File(file.getParent(), fileName);
			if (treeMap.containsKey(fileName))
				println("hidden version - cannot verify");
			else
			if (!file2.exists()) {
				println("--> does not exist");
				errors++;
				showErrorNum();
			}
			else
			try {
				if (getMD5Sum(file2).equals(MD5)) {
					println("OK");
					OKs++;
					OKLabel.setText(format(OKs, 5) + " files seem to be OK");
				}
				else {
					println("--> error");
					errors++;
					showErrorNum();
				}
			}
			catch (IOException e) {
				println("--> IO error");
				errors++;
				showErrorNum();
			}

			treeMap.put(fileName, null);
		}
	}

	/**
	 * Since pre 1.4 Java does not like a "new Box(BoxLayout.Y_AXIS)"
	 */
	private static class MyBox extends JPanel {
		MyBox(int axis) {
			setLayout(new BoxLayout(this, axis));
		}
	}
}
