package amidst.gui.menu;

import MoF.*;
import amidst.Options;
import amidst.Util;
import amidst.gui.LicenseWindow;
import amidst.logging.Log;
import amidst.map.MapObjectPlayer;
import amidst.map.layers.StrongholdLayer;
import amidst.minecraft.MinecraftUtil;
import amidst.preferences.BiomeColorProfile;
import amidst.preferences.SelectPrefModel.SelectButtonModel;
import amidst.resources.ResourceLoader;

import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/** Structured menubar-creation to alleviate the huge mess that it would be elsewise
 */

// TODO: This class is a mess-- it should be split into pieces.
public class AmidstMenu extends JMenuBar {
	final JMenu fileMenu;
	//final JMenu scriptMenu;
	public final JMenu mapMenu; //TODO: protected
	final JMenu optionsMenu;
	final JMenu helpMenu;
	
	private final FinderWindow window;
	
	public AmidstMenu(FinderWindow window) {
		this.window = window;
		
		fileMenu = add(new FileMenu());
		mapMenu = add(new MapMenu());
		optionsMenu = add(new OptionsMenu());
		helpMenu = add(new HelpMenu());
	}
	
	private class FileMenu extends JMenu {
		private FileMenu() {
			super("File");
			setMnemonic(KeyEvent.VK_F);
			
			add(new JMenu("New") {{
				setMnemonic(KeyEvent.VK_N);
				add(new SeedMenuItem());
				add(new FileMenuItem());
				add(new RandomSeedMenuItem());
				//add(new JMenuItem("From Server"));
			}});
			
			add(new JMenuItem("Save player locations") {{
				setEnabled(MinecraftUtil.getVersion().saveEnabled());
				setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent arg0) {
						if (window.curProject.saveLoaded) {
							for (MapObjectPlayer player : window.curProject.save.getPlayers()) {
								if (player.needSave) {
									window.curProject.save.movePlayer(player.getName(), player.globalX, player.globalY);
									player.needSave = false;
								}
							}
						}
					}
				});
			}});
			
			add(new JMenuItem("Exit") {{
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
					System.exit(0);
					}
				});
			}});
		}
		private String showSeedPrompt(String title) {
			final String blankText = "A random seed will be generated if left blank.";
			final String leadingSpaceText = "Warning: There is a space at the start!";
			final String trailingSpaceText = "Warning: There is a space at the end!";
			
			final JTextField inputText = new JTextField();

			inputText.addAncestorListener( new AncestorListener() {
				@Override
				public void ancestorAdded(AncestorEvent arg0) {
					inputText.requestFocus();
				}
				@Override
				public void ancestorMoved(AncestorEvent arg0) {
					inputText.requestFocus();
				}
				@Override
				public void ancestorRemoved(AncestorEvent arg0) {
					inputText.requestFocus();
				}
			});
			
			final JLabel inputInformation = new JLabel(blankText);
			inputInformation.setForeground(Color.red);
			inputInformation.setFont(new Font("arial", Font.BOLD, 10));
			inputText.getDocument().addDocumentListener(new DocumentListener() {
				@Override
				public void changedUpdate(DocumentEvent e) {
					update();
				}
				@Override
				public void insertUpdate(DocumentEvent e) {
					update();
				}
				@Override
				public void removeUpdate(DocumentEvent e) {
					update();
				}
				public void update() {
					String text = inputText.getText();
					if (text.equals("")) {
						inputInformation.setText(blankText);
						inputInformation.setForeground(Color.red);
					} else if (text.startsWith(" ")) {
						inputInformation.setText(leadingSpaceText);
						inputInformation.setForeground(Color.red);
					} else if (text.endsWith(" ")) {
						inputInformation.setText(trailingSpaceText);
						inputInformation.setForeground(Color.red);
					} else {
						try {
							Long.parseLong(text);
							inputInformation.setText("Seed is valid.");
							inputInformation.setForeground(Color.gray);
						} catch (NumberFormatException e) {
							inputInformation.setText("This seed's value is " + text.hashCode() + ".");
							inputInformation.setForeground(Color.black);
						}
					}
				}
			});
			
			final JComponent[] inputs = new JComponent[] {
					new JLabel("Enter your seed: "),
					inputInformation,
					inputText
			};
			int result = JOptionPane.showConfirmDialog(window, inputs, title, JOptionPane.OK_CANCEL_OPTION);
			return (result == 0)?inputText.getText():null;
		}
		
		private class SeedMenuItem extends JMenuItem {
			private SeedMenuItem() {
				super("From seed");
				setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent arg0) {
						String seed = showSeedPrompt("New Project");
						if (seed != null) {
							String worldTypePreference = Options.instance.worldType.get();
							SaveLoader.Type worldType = null;
							if (worldTypePreference.equals("Prompt each time")) {
								worldType = choose("New Project", "Enter world type\n", SaveLoader.selectableTypes);
							} else {
								worldType = SaveLoader.Type.fromMixedCase(worldTypePreference);
							}
							
							if (seed.equals(""))
								seed = "" + (new Random()).nextLong();
							if (worldType != null) {
								window.clearProject();
								window.setProject(new Project(seed, worldType.getValue()));
							}
						}
					}
				});
			}
		}
		
		private class RandomSeedMenuItem extends JMenuItem {
			private RandomSeedMenuItem() {
				super("From random seed");
				setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK));
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent arg0) {
						//Create the JOptionPane.
							Random random = new Random();
							long seed = random.nextLong();
							String worldTypePreference = Options.instance.worldType.get();
							SaveLoader.Type worldType = null;
							if (worldTypePreference.equals("Prompt each time")) {
								worldType = choose("New Project", "Enter world type\n", SaveLoader.selectableTypes);
							} else {
								worldType = SaveLoader.Type.fromMixedCase(worldTypePreference);
							}
							
							//If a string was returned, say so.
							if (worldType != null) {
								window.clearProject();
								window.setProject(new Project(seed, worldType.getValue()));
							}
						}
					
				});
			}
		}
		
		private class FileMenuItem extends JMenuItem {
			private FileMenuItem() {
				super("From file or folder");
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent arg0) {
						JFileChooser fc = new JFileChooser();
						fc.setFileFilter(SaveLoader.getFilter());
						fc.setAcceptAllFileFilterUsed(false);
						fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
						File savesDir = null;
						if (Util.profileDirectory != null)
							savesDir = new File(Util.profileDirectory, "saves");
						else
							savesDir = new File(Util.minecraftDirectory, "saves");
						//if (!savesDir.mkdirs()) {
						//	Log.w("Unable to create save directory!");
						//	return;
						//}
						fc.setCurrentDirectory(savesDir);
						fc.setFileHidingEnabled(false);
						if (fc.showOpenDialog(window) == JFileChooser.APPROVE_OPTION) {
							File f = fc.getSelectedFile();
							
							SaveLoader s = null;
							if (f.isDirectory())
								s = new SaveLoader(new File(f.getAbsoluteFile() + "/level.dat"));
							else
								s = new SaveLoader(f);
							window.clearProject();
							window.setProject(new Project(s));
						}
					}
				});
			}
		}
	}

	public class DisplayingCheckbox extends JCheckBoxMenuItem {
		private DisplayingCheckbox(String text, BufferedImage icon, int key, JToggleButton.ToggleButtonModel model) {
			super(text, (icon != null) ? new ImageIcon(icon) : null);
			if (key != -1)
				setAccelerator(KeyStroke.getKeyStroke(key, InputEvent.CTRL_DOWN_MASK));
			setModel(model);
		}
	}
	private class MapMenu extends JMenu {
		private MapMenu() {
			super("Map");
			setEnabled(false);
			setMnemonic(KeyEvent.VK_M);
			add(new FindMenu());
			add(new GoToMenu());
			add(new LayersMenu());
			add(new CopySeedMenuItem());
			add(new CaptureMenuItem());
		
		}
		
		private class FindMenu extends JMenu {
			private FindMenu() {
				super("Find");
				//add(new JMenuItem("Biome"));
				//add(new JMenuItem("Village"));
				add(new JMenuItem("Stronghold") {{
					setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
					addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent arg0) {
							goToChosenPoint(StrongholdLayer.instance.getStrongholds(), "Stronghold");
						}
					});
				}});
			}
		}
		
		private class GoToMenu extends JMenu {
			private GoToMenu() {
				super("Go to");
				add(new JMenuItem("Coordinate") {{
					setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, InputEvent.CTRL_DOWN_MASK));addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent arg0) {
							String s = JOptionPane.showInputDialog(null, "Enter coordinates: (Ex. 123,456)", "Go To", JOptionPane.QUESTION_MESSAGE);
							if (s != null) {
								String[] c = s.replaceAll(" ", "").split(",");
								try {
									long x = Long.parseLong(c[0]);
									long y = Long.parseLong(c[1]);
									window.curProject.moveMapTo(x, y);
								} catch (NumberFormatException e1) {
									Log.w("Invalid location entered, ignoring.");
									e1.printStackTrace();
								}
							}
						}
					});
				}});
				
				add(new JMenuItem("Player") {{
					addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent arg0) {
							if (window.curProject.saveLoaded) {
								List<MapObjectPlayer> playerList = window.curProject.save.getPlayers();
								MapObjectPlayer[] players = playerList.toArray(new MapObjectPlayer[playerList.size()]);
								goToChosenPoint(players, "Player");
								MapObjectPlayer p = choose("Go to", "Select player:", players);
								if (p != null)
									window.curProject.moveMapTo(p.globalX, p.globalY);
							}
						}
					});
				}});
				//add(new JMenuItem("Spawn"));
				//add(new JMenuItem("Chunk"));
			}
		}
		
		private class LayersMenu extends JMenu {
			private LayersMenu() {
				super("Layers");

				add(new DisplayingCheckbox("Grid",
					ResourceLoader.getImage("grid.png"),
					KeyEvent.VK_1,
					Options.instance.showGrid));
				
				add(new DisplayingCheckbox("Slime chunks",
					ResourceLoader.getImage("slime.png"),
					KeyEvent.VK_2,
					Options.instance.showSlimeChunks));
				
				add(new DisplayingCheckbox("Village Icons",
						ResourceLoader.getImage("village.png"),
						KeyEvent.VK_3,
						Options.instance.showVillages));
					
				add(new DisplayingCheckbox("Ocean Monument Icons",
						ResourceLoader.getImage("ocean_monument.png"),
						KeyEvent.VK_4,
						Options.instance.showOceanMonuments));
					
				add(new DisplayingCheckbox("Temple/Witch Hut Icons",
					ResourceLoader.getImage("desert.png"),
					KeyEvent.VK_5,
					Options.instance.showTemples));
				
				add(new DisplayingCheckbox("Stronghold Icons",
					ResourceLoader.getImage("stronghold.png"),
					KeyEvent.VK_6,
					Options.instance.showStrongholds));
				
				add(new DisplayingCheckbox("Player Icons",
					ResourceLoader.getImage("player.png"),
					KeyEvent.VK_7,
					Options.instance.showPlayers));
				
				add(new DisplayingCheckbox("Nether Fortress Icons",
					ResourceLoader.getImage("nether_fortress.png"),
					KeyEvent.VK_8,
					Options.instance.showNetherFortresses));
				
				add(new DisplayingCheckbox("Spawn Location Icon",
						ResourceLoader.getImage("spawn.png"),
						KeyEvent.VK_9,
						Options.instance.showSpawn));
				
			}
			

		}
		private class CaptureMenuItem extends JMenuItem {
			private CaptureMenuItem() {
				super("Capture");
				
				setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK));
				
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						JFileChooser fc = new JFileChooser();
						fc.setFileFilter(new PNGFileFilter());
						fc.setAcceptAllFileFilterUsed(false);
						int returnVal = fc.showSaveDialog(window);
						
						if (returnVal == JFileChooser.APPROVE_OPTION) {
							String s = fc.getSelectedFile().toString();
							if (!s.toLowerCase().endsWith(".png"))
								s += ".png";
							window.curProject.map.saveToFile(new File(s));
						}
					}
				});
			}
		}
		private class CopySeedMenuItem extends JMenuItem {
			private CopySeedMenuItem() {
				super("Copy Seed to Clipboard");
				
				setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
				
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						StringSelection stringSelection = new StringSelection(Options.instance.seed + "");
						Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
						clipboard.setContents(stringSelection, new ClipboardOwner() {
							@Override
							public void lostOwnership(Clipboard arg0, Transferable arg1) {
								// TODO Auto-generated method stub
								
							}
						});
					}
				});
			}
		}
	}
	
	private class OptionsMenu extends JMenu {
		private OptionsMenu() {
			super("Options");
			add(new MapOptionsMenu());
			if (BiomeColorProfile.isEnabled)
				add(new BiomeColorMenu());
			add(new WorldTypeMenu());
			setMnemonic(KeyEvent.VK_M);
		}
		private class BiomeColorMenu extends JMenu {
			private ArrayList<JCheckBoxMenuItem> profileCheckboxes = new ArrayList<JCheckBoxMenuItem>();
			private JMenuItem reloadMenuItem;
			private class BiomeProfileActionListener implements ActionListener {
				private BiomeColorProfile profile;
				private ArrayList<JCheckBoxMenuItem> profileCheckboxes;
				private JCheckBoxMenuItem checkBox;
				public BiomeProfileActionListener(BiomeColorProfile profile, JCheckBoxMenuItem checkBox, ArrayList<JCheckBoxMenuItem> profileCheckboxes) {
					this.profile = profile;
					this.checkBox = checkBox;
					this.profileCheckboxes = profileCheckboxes;
				}
				@Override
				public void actionPerformed(ActionEvent e) {
					for (int i = 0; i < profileCheckboxes.size(); i++)
						profileCheckboxes.get(i).setSelected(false);
					checkBox.setSelected(true);
					profile.activate();
				}
			}
			private BiomeColorMenu() {
				super("Biome profile");
				reloadMenuItem = new JMenuItem("Reload Menu");
				final BiomeColorMenu biomeColorMenu = this;
				reloadMenuItem.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent arg) {
						profileCheckboxes.clear();
						Log.i("Reloading additional biome color profiles.");
						File colorProfileFolder = new File("./biome");
						biomeColorMenu.removeAll();
						scanAndLoad(colorProfileFolder, biomeColorMenu);
						biomeColorMenu.add(reloadMenuItem);
					}
				});
				reloadMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl B"));
				Log.i("Checking for additional biome color profiles.");
				File colorProfileFolder = new File("./biome");
				scanAndLoad(colorProfileFolder, this);
				profileCheckboxes.get(0).setSelected(true);
				add(reloadMenuItem);
			}
			
			private boolean scanAndLoad(File folder, JMenu menu) {
				File[] files = folder.listFiles();
				BiomeColorProfile profile;
				boolean foundProfiles = false;
				for (int i = 0; i < files.length; i++) {
					if (files[i].isFile()) {
						if ((profile = BiomeColorProfile.createFromFile(files[i])) != null) {
							JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(profile.name);
							menuItem.addActionListener(new BiomeProfileActionListener(profile, menuItem, profileCheckboxes));
							if (profile.shortcut != null) {
								KeyStroke accelerator = KeyStroke.getKeyStroke(profile.shortcut);
								if (accelerator != null)
									menuItem.setAccelerator(accelerator);
								else
									Log.i("Unable to create keyboard shortcut from: " + profile.shortcut);
							}
							menu.add(menuItem);
							profileCheckboxes.add(menuItem);
							foundProfiles = true;
						}
					} else {
						JMenu subMenu = new JMenu(files[i].getName());
						if (scanAndLoad(files[i], subMenu)) {
							menu.add(subMenu);
						}
					}
				}
				return foundProfiles;
			}
			
		}
		private class MapOptionsMenu extends JMenu {
			private MapOptionsMenu() {
				super("Map");

				add(new DisplayingCheckbox("Map Flicking (Smooth Scrolling)",
						null,
						KeyEvent.VK_I,
						Options.instance.mapFlicking));
				
				add(new DisplayingCheckbox("Restrict Maximum Zoom",
						null,
						KeyEvent.VK_Z,
						Options.instance.maxZoom));
				
				add(new DisplayingCheckbox("Show Framerate",
						null,
						KeyEvent.VK_L,
						Options.instance.showFPS));

				add(new DisplayingCheckbox("Show Scale",
						null,
						KeyEvent.VK_K,
						Options.instance.showScale));
				
				add(new DisplayingCheckbox("Use Fragment Fading",
						null,
						-1,
						Options.instance.mapFading));
				
				add(new DisplayingCheckbox("Show Debug Info",
						null,
						-1,
						Options.instance.showDebug));
			}
			
		}
		private class WorldTypeMenu extends JMenu {
			private WorldTypeMenu() {
				super("World type");

				SelectButtonModel[] buttonModels = Options.instance.worldType.getButtonModels();
				
				for (int i = 0; i < buttonModels.length; i++) {
					add(new DisplayingCheckbox(buttonModels[i].getName(),
							null,
							-1,
							buttonModels[i]));
				}
			}
			
		}
	}
	
	private class HelpMenu extends JMenu {
		private HelpMenu() {
			super("Help");
			
			add(new JMenuItem("Check for updates") {{
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						new UpdateManager(window).start();
					}
				});
			}});

			add(new JMenuItem("View licenses") {{
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						new LicenseWindow();
					}
				});
			}});
			
			add(new JMenuItem("About") {{
				addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						JOptionPane.showMessageDialog(window,
							"Advanced Minecraft Interfacing and Data/Structure Tracking (AMIDST)\n" +
							"By Skidoodle ([email protected])");
					}
				});
			}});
			
		}
	}
	
	/** Allows the user to choose one of several things.
	 * 
	 * Convenience wrapper around JOptionPane.showInputDialog
	 */
	private <T> T choose(String title, String message, T[] choices) {
		return (T) JOptionPane.showInputDialog(
			window,
			message,
			title,
			JOptionPane.PLAIN_MESSAGE,
			null,
			choices,
			choices[0]);
	}
	
	/** Lets the user decide one of the given points and go to it
	 * @param points Given points to choose from
	 * @param name name displayed in the choice
	 */
	private <T extends Point> void goToChosenPoint(T[] points, String name) {

		T p = choose("Go to", "Select " + name + ":", points);
		if (p != null)
			window.curProject.moveMapTo(p.x, p.y);
	}
}