package sim.lib.memory.hex;

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import gjt.BulletinLayout;
import javax.swing.JFileChooser;
import javax.swing.UIManager;

import sim.util.DialogButton;
import sim.MainWindow;
import sim.SaveLoadShortcut;

public class HexEditor extends Frame implements ActionListener, FocusListener {

	private HexEditorParent parent;
	private HexArea buffer;
	private ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
	private int addressSpace;
	private int addressWidth;
	private int dataWidth;
	private String targetFile;
	private Label content = new Label("Memory Content");
	private TextField whereToGo = new TextField(10);
	private TextField editFileName = new TextField();
	private DialogButton goTo = new DialogButton("Go to...");
	private DialogButton browse = new DialogButton("Browse...");
	private DialogButton reload = new DialogButton("Reload");
	private DialogButton reset = new DialogButton("Clear All");
	private DialogButton save = new DialogButton("Save");
	private DialogButton discard = new DialogButton("Close");

	public HexEditor(HexEditorParent hep, char[][] content, String fileName, int busSize, int space, Font f, boolean isSimulating) {
		super("Memory Content - " + fileName);

		this.setLayout(new GridBagLayout());
		this.setBackground(MainWindow.BACKGROUND);

		this.parent = hep;
		this.addressSpace = space;
		this.addressWidth = Integer.toHexString(this.addressSpace - 1).length();
		this.dataWidth = Integer.toHexString((int) Math.pow(2, busSize) - 1).length();
		this.targetFile = fileName;

		Panel p = new Panel(new FlowLayout(FlowLayout.LEFT, 10, 10));

		//Goto
		p.add(this.goTo);
		this.goTo.addActionListener(this);
		p.add(this.whereToGo);

		GridBagConstraints c = new GridBagConstraints();

		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 1;
		c.insets = new Insets(0, 0, 0, 0);
		c.anchor = GridBagConstraints.WEST;
		c.fill = GridBagConstraints.NONE;

		this.add(p, c);

		//Hex area
		c.gridx = 0;
		c.gridy = 1;
		c.gridwidth = 2;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 1;
		c.insets = new Insets(0, 10, 0, 10);
		c.anchor = GridBagConstraints.CENTER;
		c.fill = GridBagConstraints.BOTH;

		this.scroller.setBackground(Color.white);
		this.add(this.scroller, c);

		this.buffer = new HexArea(f, content, busSize, this.addressSpace, this.scroller, isSimulating);
		TextPanel tp = new TextPanel();
		tp.add(this.buffer);
		this.scroller.add(tp);

		//browse

		c.gridx = 0;
		c.gridy = 2;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 1;
		c.insets = new Insets(0, 10, 0, 10);
		c.anchor = GridBagConstraints.WEST;
		c.fill = GridBagConstraints.NONE;
		this.add(this.content, c); // label

		c.gridx = 0;
		c.gridy = 3;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 0;
		c.weighty = 0;
		c.insets = new Insets(0, 10, 0, 10);
		c.anchor = GridBagConstraints.WEST;
		c.fill = GridBagConstraints.BOTH;
		this.editFileName.setText(targetFile);
		this.editFileName.addActionListener(this);
		this.editFileName.addFocusListener(this);
		this.add(this.editFileName, c); //textfield

		c.gridx = 1;
		c.gridy = 3;
		c.gridheight = 1;
		c.weightx = 0;
		c.weighty = 0;
		c.insets = new Insets(0, 10, 0, 10);
		c.anchor = GridBagConstraints.WEST;
		c.fill = GridBagConstraints.BOTH;
		this.browse.addActionListener(this);
		this.add(this.browse, c); //browse button

		//reset, reload
		p = new Panel(new FlowLayout(FlowLayout.LEFT, 30, 0));

		this.reset.addActionListener(this);
		p.add(reset);

		this.reload.addActionListener(this);
		p.add(this.reload);

		//save, discard
		this.save.addActionListener(this);
		if (MainWindow.MASTER != null) {
			this.save.setEnabled(false);
		}
		p.add(this.save);

		this.discard.addActionListener(this);
		p.add(this.discard);

		c.gridx = 0;
		c.gridy = 4;
		c.gridwidth = 2;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 1;
		c.insets = new Insets(0, 10, 0, 10);
		c.anchor = GridBagConstraints.CENTER;
		c.fill = GridBagConstraints.NONE;

		this.add(p, c);

		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent event) {
				parent.respondToClosing();
				setVisible(false);
				dispose();
			}
		});

		this.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {

				if (getSize().width < 320) {
					if (getSize().height < 240) {
						setSize(320, 240);
					} else {
						setSize(320, getSize().height);
					}
				} else if (getSize().height < 240) {
					setSize(getSize().width, 240);
				} else {
					System.out.println(scroller.getViewportSize().width);
					if (buffer.needNewBuffer(scroller.getViewportSize().width)) {
						buffer.paintBakground(scroller.getViewportSize().width);
					}

					scroller.validate();
				}
			}
		});

		if (isSimulating) {
			this.reload.setEnabled(false);
			this.save.setEnabled(false);
		}
	}

	public void addNotify() {
		super.addNotify();
		this.setSize(530, 450);
	}

	public void removeNotify() {
		super.removeNotify();

		System.gc();
	}

	public void actionPerformed(ActionEvent e) {
		char[][] content;
		int loop;

		if (e.getSource() == this.editFileName) {
			String newFile = this.editFileName.getText();
			if (!targetFile.equals(newFile)) {
				this.changeFile(newFile);
			}
		} else if (e.getSource() == this.browse) {
			JFileChooser chooser = new JFileChooser(this.editFileName.getText());
			chooser.setDialogTitle("Please choose a file");
			if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				this.changeFile(chooser.getSelectedFile().toString());
			}
		} else if (e.getSource() == this.goTo) {
			try {
				int where = Integer.valueOf(this.whereToGo.getText(), 16).intValue();

				if ((where >= 0) && (where < this.addressSpace)) {
					if (this.reload.isEnabled()) {
						this.buffer.skipToAddress(where);
					} else {
						this.buffer.showAddress(where);
					}

				}
			} catch (NumberFormatException nfe) {
			}
		} else if (e.getSource() == this.reset) {
			this.buffer.setContent(this.parent.clearContent());
			this.buffer.deselectCursor();

			this.buffer.repaintBakground();
			this.buffer.paint(this.buffer.getGraphics());
		} else if (e.getSource() == this.reload) {
			this.parent.reloadMemory();
			this.buffer.deselectCursor();

			this.buffer.repaintBakground();
			this.buffer.paint(this.buffer.getGraphics());
		} else if (e.getSource() == this.save) {
			try {
				BufferedWriter outStream = new BufferedWriter(new FileWriter(this.targetFile));

				content = this.buffer.getContent();

				for (loop = 0; loop < this.addressSpace; loop++) {
					outStream.write(content[loop], 0, this.dataWidth);
				}

				outStream.close();
			} catch (IOException exc) {
				MainWindow.OK_WINDOW.setDescription("Exception occur during saving");
				MainWindow.OK_WINDOW.setMessage(exc.getMessage());
				MainWindow.OK_WINDOW.setVisible(true);
			}
			this.parent.respondToClosing();
			this.setVisible(false);
			this.dispose();
		} else if (e.getSource() == this.discard) {
			this.parent.respondToClosing();
			this.setVisible(false);
			this.dispose();
		}
	}

	public void focusLost(FocusEvent e) {
		TextField source = (TextField) e.getSource();

		if (source == this.editFileName) {
			String newFile = this.editFileName.getText();
			if (!targetFile.equals(newFile)) {
				this.changeFile(newFile);
			}
		}
	}

	public String changeFile(String newFile) {
		try {
			FileWriter f = new FileWriter(newFile, true);
			f.close();

			this.targetFile = newFile;
			this.parent.setFile(targetFile);
			this.editFileName.setText(targetFile);

			this.buffer.deselectCursor();

			this.buffer.repaintBakground();
			this.buffer.paint(this.buffer.getGraphics());
		} catch (IOException ex) {
		}
		return this.targetFile;
	}

	public void changeParameters(char[][] content, String fileName, int busSize, int space) {
		this.setTitle("Memory Content - " + fileName);

		this.addressSpace = space;
		this.addressWidth = Integer.toHexString(this.addressSpace - 1).length();
		this.dataWidth = Integer.toHexString((int) Math.pow(2, busSize) - 1).length();
		this.targetFile = fileName;

		this.buffer.changeParameters(content, busSize, this.addressSpace);
		this.scroller.validate();
		((TextPanel) this.scroller.getComponent(0)).realUpdate();
	}

	public void eliminateHighlights() {
		this.buffer.eliminateHighlights();
	}

	public void readFrom(int address) {
		this.buffer.highlightData(address, Color.black, Color.white);
		this.buffer.showAddress(address);
	}

	public void writeTo(int address) {
		this.buffer.eliminateHighlights();
		this.buffer.highlightData(address, Color.red, Color.white);
		this.buffer.showAddress(address);
	}

	public void writeToEverywhere() {
		this.buffer.eliminateHighlights();
		this.buffer.repaintBakground();
		this.buffer.showAddress(0);
	}

	public void prepareForSimulation() {
		this.buffer.prepareForSimulation();
		this.reload.setEnabled(false);
		this.save.setEnabled(false);
	}

	public void prepareForEditing() {
		this.buffer.prepareForEditing();

		this.reload.setEnabled(true);

		if (MainWindow.MASTER == null) {
			this.save.setEnabled(true);
		}
	}

	@Override
	public void focusGained(FocusEvent e) {
	}

	/* ======================================================================
	 Implement Panel to hold Text
	 ===================================================================== */
	public class TextPanel extends Panel {

		public TextPanel() {
			super();

			this.setLayout(new BulletinLayout());
		}

		public Dimension getPrefferedSize() {
			return this.getComponent(0).getSize();
		}

		public void update(Graphics g) {
		}

		public void realUpdate() {
			super.update(this.getGraphics());
		}
	}
}
