package speiger.src;

import java.awt.BorderLayout;
import java.awt.Checkbox;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.io.BufferedReader;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Duration;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.prefs.Preferences;

import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

import speiger.src.data.CollectableType;
import speiger.src.data.Registry;
import speiger.src.ui.DownloadTask;
import speiger.src.ui.MapPanel;
import speiger.src.ui.VerticalFlowLayout;

public class MarioKartWorldTracker {
	private static final JFileChooser CHOOSER = new JFileChooser(new File(Preferences.userRoot().node(MapPanel.class.getName()).get("Last Folder", new File(".").getAbsolutePath())));
	private static Checkbox P_SWITCH;
	private static Checkbox MEDALS;
	private static Checkbox PANEL;
	private static Checkbox CHUCKS;
	
	public static void main(String...args) {
		Toolkit.getDefaultToolkit().setDynamicLayout(false);
		JFrame frame = new JFrame();
		frame.setIconImage(new ImageIcon(MapPanel.class.getResource("/assets/images/icon.png")).getImage());
		frame.setLayout(new BorderLayout());
		frame.setBounds(0, 0, 800, 600);
		frame.setTitle("Mario Kart World Progress Tracker");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationRelativeTo(null);
		MapPanel panel = new MapPanel(frame);
		frame.add(panel, BorderLayout.CENTER);
		JMenuBar bar = new JMenuBar();
		frame.setJMenuBar(bar);
		JMenu menu = bar.add(new JMenu("File"));
		menu.add(item("Import Save", T -> {
			Preferences prefs = Preferences.userRoot().node(MapPanel.class.getName());
			if(CHOOSER.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
				File file = CHOOSER.getSelectedFile();
				if(!file.getName().endsWith(".json")) return;
				try(BufferedReader reader = Files.newBufferedReader(file.toPath())) {
					List<UUID> ids = new ArrayList<>();
					for(JsonElement element : JsonParser.parseReader(reader).getAsJsonObject().getAsJsonArray("completed")) {
						ids.add(UUID.fromString(element.getAsString()));
					}
					Registry.INSTANCE.importSave(ids);
					panel.onImported();
				}
				catch(Exception e) {
					e.printStackTrace(); 
					JOptionPane.showMessageDialog(frame, "Importing caused an error \n"+e.toString(), "Importing Error", JOptionPane.ERROR_MESSAGE);
				}
				
				prefs.put("Last Folder", file.getParent());
			}
		}, null));
		menu.add(item("Export Save", T -> {		
			Preferences prefs = Preferences.userRoot().node(MapPanel.class.getName());
			if(CHOOSER.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
				File file = CHOOSER.getSelectedFile();
			    String name = file.getName();
			    int index = name.lastIndexOf(".");
			    if(index > -1) name = name.substring(0, index);
				name += ".json";
				Path save = Registry.getOrigin().resolve("data/save.json");
				if(Files.notExists(save)) return;
				
				try { Files.copy(save, file.toPath().getParent().resolve(name), StandardCopyOption.REPLACE_EXISTING); }
				catch(Exception e) {
					e.printStackTrace();
					JOptionPane.showMessageDialog(frame, "Exporting caused an error \n"+e.toString(), "Exporting Error", JOptionPane.ERROR_MESSAGE);
				}
				
				prefs.put("Last Folder", file.getParent());
			    
			}
		}, null));
		menu.add(item("Download all Previews", T -> {
			if(JOptionPane.showConfirmDialog(frame, "Are you sure you want to download all missing images?", "Download Images", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
				new DownloadTask(frame);				
			}
		}, null));
		menu.add(item("Credits", T -> {
			JOptionPane.showMessageDialog(frame, "Credits Go to:\nhttps://mkw.techtangents.net and\nhttps://www.gamerguides.com/mario-kart-world/maps/world \nwhich are used as Resource Providers", "Credits", JOptionPane.INFORMATION_MESSAGE);
		}, null));
		
		JMenu controls = new JMenu("Controls");
		controls.add("Left Button: Toggle Completion/Finish Bulk Action");
		controls.add("Right Button: Show Location");
		controls.add("Left Button Dragging: Move Map");
		controls.add("Mouse Scrolling: Zoom");
		controls.add("Press 'm' when mouse is on map to start bulk action");
		
		
		JMenu bulk = new JMenu("Bulk Actions");
		bulk.add(item("Complete Visible", T -> {
			panel.setCompletionState(true);
		}, null));
		bulk.add(item("Uncomplete Visible", T -> {
			panel.setCompletionState(false);
		}, null));
		bulk.add(item("Bulk Mark", T -> {
			panel.startBulkMarking(panel.getMousePosition());
		}, KeyStroke.getKeyStroke('m')));
		bar.add(bulk);
		JMenu fuckups = new JMenu("I Fucked up");
		fuckups.add(item("Reset Zoom", T -> {
			panel.resetZoom();
		}, KeyStroke.getKeyStroke('z')));
		fuckups.add(item("Reset Offset", T -> {
			panel.resetMap();
		}, KeyStroke.getKeyStroke('r')));
		
		bar.add(fuckups);
		bar.add(controls);
		
		JPanel sideMenu = new JPanel();
		sideMenu.setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 5, 0));
		
		sideMenu.add(new JLabel("Filter"));
		P_SWITCH = (Checkbox)sideMenu.add(box("P-Switches", T -> onFilterChanged(panel::updateFilter)));
		MEDALS = (Checkbox)sideMenu.add(box("Medals", T -> onFilterChanged(panel::updateFilter)));
		PANEL = (Checkbox)sideMenu.add(box("Panels", T -> onFilterChanged(panel::updateFilter)));
		CHUCKS = (Checkbox)sideMenu.add(box("Chucks", T -> onFilterChanged(panel::updateFilter)));
		JLabel PSwitchProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.PSWITCH)+" / "+Registry.INSTANCE.total(CollectableType.PSWITCH));
		JLabel MedalsProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.MEDAL)+" / "+Registry.INSTANCE.total(CollectableType.MEDAL));
		JLabel PanelProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.PANEL)+" / "+Registry.INSTANCE.total(CollectableType.PANEL));
		JLabel ChucksProgress = new JLabel(Registry.INSTANCE.completed(CollectableType.CHUCKS)+" / "+Registry.INSTANCE.total(CollectableType.CHUCKS));
		panel.setProgressListener(() -> {
			PSwitchProgress.setText(Registry.INSTANCE.completed(CollectableType.PSWITCH)+" / "+Registry.INSTANCE.total(CollectableType.PSWITCH));
			MedalsProgress.setText(Registry.INSTANCE.completed(CollectableType.MEDAL)+" / "+Registry.INSTANCE.total(CollectableType.MEDAL));
			PanelProgress.setText(Registry.INSTANCE.completed(CollectableType.PANEL)+" / "+Registry.INSTANCE.total(CollectableType.PANEL));
			ChucksProgress.setText(Registry.INSTANCE.completed(CollectableType.CHUCKS)+" / "+Registry.INSTANCE.total(CollectableType.CHUCKS));
			EventQueue.invokeLater(sideMenu::repaint);
		});
		
		sideMenu.add(new JLabel("P-Switches:"));
		sideMenu.add(PSwitchProgress);
		sideMenu.add(new JLabel("Medals:"));
		sideMenu.add(MedalsProgress);
		sideMenu.add(new JLabel("Panels:"));
		sideMenu.add(PanelProgress);
		sideMenu.add(new JLabel("Chucks:"));
		sideMenu.add(ChucksProgress);
		
		frame.add(sideMenu, BorderLayout.WEST);
		
		panel.resetMap();
		frame.setVisible(true);
		
		while(true) {
			try {
				Registry.INSTANCE.processQueue();
				Thread.sleep(Duration.ofMillis(50));
			}
			catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	private static Checkbox box(String name, ItemListener listener) {
		Checkbox box = new Checkbox(name, true);
		box.addItemListener(listener);
		return box;
	}
	
	private static void onFilterChanged(Consumer<EnumSet<CollectableType>> consumer) {
		EnumSet<CollectableType> types = EnumSet.noneOf(CollectableType.class);
		if(P_SWITCH.getState()) types.add(CollectableType.PSWITCH);
		if(MEDALS.getState()) types.add(CollectableType.MEDAL);
		if(PANEL.getState()) types.add(CollectableType.PANEL);
		if(CHUCKS.getState()) types.add(CollectableType.CHUCKS);
		consumer.accept(types);
	}
	
	private static JMenuItem item(String name, ActionListener action, KeyStroke stroke) {
		JMenuItem item = new JMenuItem(name);
		item.addActionListener(action);
		item.setAccelerator(stroke);
		return item;
	}
}
