package de.stoermer.xxx.client;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;

import de.stoermer.xxx.domain.Geschlecht;
import de.stoermer.xxx.domain.InkonsistenterAufgabenZustand;
import de.stoermer.xxx.domain.InkonsistenterSpielerZustand;
import de.stoermer.xxx.domain.Rolle;
import de.stoermer.xxx.domain.Session;
import de.stoermer.xxx.domain.Spieler;
import de.stoermer.xxx.domain.Toy;
import de.stoermer.xxx.domain.Werkzeug;
import de.stoermer.xxx.domain.xml.XmlAufgabenProvider;

public class StartupPanel extends JPanel {

	private static final long serialVersionUID = 1L;

	private JTabbedPane tabbedPane;
	
	private JScrollPane playerPanel;
	private JScrollPane toyPanel;
	private JPanel einstellungenPanel;
	
	private JList<SpielerBean> spielerList;
	
	private JTextField nameField;
	
	private JCheckBox rolleBestrafter;
	private JCheckBox rolleBestrafer;
	private JCheckBox rolleBeglueckter;
	private JCheckBox rolleBegluecker;
	
	private JCheckBox mund;
	private JCheckBox vagina;
	private JCheckBox penis;
	private JCheckBox anus;
	private JCheckBox umschnalldildo;

	private XmlAufgabenProvider provider;
	private Map<String, Toy> selectedToys = new HashMap<>();

	private MainFrame mainFrame;
	
	private int valueSperren = 10;
	private int valueStrafen = 20;
	
	private int gameLenght = 10;
	
	public StartupPanel(MainFrame mainFrame, XmlAufgabenProvider provider) {	
		super(new BorderLayout());
		this.mainFrame = mainFrame;
		this.provider = provider;
	}
	
	private List<SpielerBean> spieler;
	private SpielerBean selectedSpieler;
	
	public void init() {
		SavedState state = loadState();
		if (state == null || state.getSpieler() == null || state.getSpieler().size() < 1) {		
			this.spieler = new ArrayList<>();
			
			SpielerBean spieler1 = new SpielerBean();
			spieler1.setName("Spieler 1");
			spieler1.setRollen(new ArrayList<>(Arrays.asList(Rolle.values())));
			spieler1.setWerkzeuge(new ArrayList<>(Arrays.asList(Werkzeug.values())));
			spieler.add(spieler1);
			
			this.selectedSpieler = spieler1;
			
			SpielerBean spieler2 = new SpielerBean();
			spieler2.setName("Spieler 2");
			spieler2.setRollen(new ArrayList<>(Arrays.asList(Rolle.values())));
			spieler2.setWerkzeuge(new ArrayList<>(Arrays.asList(Werkzeug.values())));
			spieler.add(spieler2);
		} else {
			spieler = state.getSpieler();
			this.selectedSpieler = spieler.get(0);
		}
		
		tabbedPane = new JTabbedPane();
		
		tabbedPane.addTab("Spieler", getPlayerPanel());
		tabbedPane.addTab("Toys", getToyPanel());
		tabbedPane.addTab("Sonstige Einstellungen", getEinstellungenPanel());
		
		this.add(tabbedPane, BorderLayout.CENTER);
		
		provider.getToys().forEach(toy -> selectedToys.put(toy.getId(), toy));
		
		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		
		JButton start = new JButton("Start");
		start.addActionListener((event) -> {
			try {				
				Session session = new Session(
						spieler.stream().map(bean -> new Spieler(bean.getName(), Geschlecht.DIVERS, bean.getRollen(), bean.getWerkzeuge())).toList(), 
						valueSperren, valueStrafen, selectedToys.entrySet().stream().map(entry -> entry.getValue()).toList(), gameLenght, provider);
				safeState();
				mainFrame.startGame(session);
			} catch (InkonsistenterSpielerZustand | InkonsistenterAufgabenZustand exception) {
				exception.printStackTrace();
				JOptionPane.showMessageDialog(mainFrame, exception.getMessage(), "Fehler",
				        JOptionPane.ERROR_MESSAGE);
			}
		});
		buttonPanel.add(start);
		this.add(buttonPanel, BorderLayout.SOUTH);
		refreshSpieler();
	}

	private Component getEinstellungenPanel() {
		if (einstellungenPanel == null) {
			einstellungenPanel = new JPanel(new BorderLayout());
			JPanel panel = new JPanel(new GridLayout(6, 1, 5, 5));
			
			panel.add(new JLabel("Anteil (Weg)Sperrungen"));
			JSlider sperrenSlider = new JSlider(0, 100, 10);
			sperrenSlider.setMinorTickSpacing(25);
			sperrenSlider.setPaintTicks(true);
			sperrenSlider.setPaintLabels(true);
			sperrenSlider.setMajorTickSpacing(50);	
			panel.add(sperrenSlider);
			
			panel.add(new JLabel("Anteil Strafen"));
			JSlider strafenSlieder = new JSlider(0, 100, 20);
			strafenSlieder.setMinorTickSpacing(25);
			strafenSlieder.setPaintTicks(true);
			strafenSlieder.setPaintLabels(true);
			strafenSlieder.setMajorTickSpacing(50);	
			panel.add(strafenSlieder);
			
			panel.add(new JLabel("Anteil \"normale\" Aufgaben"));
			JSlider aufgabenSlider = new JSlider(0, 100, 60);
			aufgabenSlider.setEnabled(false);
			aufgabenSlider.setMinorTickSpacing(25);
			aufgabenSlider.setPaintTicks(true);
			aufgabenSlider.setPaintLabels(true);
			aufgabenSlider.setMajorTickSpacing(50);	
			panel.add(aufgabenSlider);
			
			panel.add(new JLabel("Spieldauer"));
			JSlider lenghtSlieder = new JSlider(5,15);
			lenghtSlieder.setPaintTicks(true);
			lenghtSlieder.setPaintLabels(true);
			lenghtSlieder.setMajorTickSpacing(10);	
			Hashtable<Integer, JLabel> table = new Hashtable<Integer, JLabel>();
		    table.put (5, new JLabel("kurz"));
		    table.put (15, new JLabel("lang"));		    
		    lenghtSlieder.setLabelTable(table);
			panel.add(lenghtSlieder);
			
			sperrenSlider.addChangeListener((e) -> {
				JSlider source = (JSlider) e.getSource();
				if (!source.getValueIsAdjusting()) {
					valueSperren = source.getValue();
					if (valueSperren + valueStrafen > 100) {
						valueStrafen = 100 - valueSperren;
					}
					strafenSlieder.setValue(valueStrafen);
					aufgabenSlider.setValue(100 - valueSperren - valueStrafen);
				}
			});
			strafenSlieder.addChangeListener((e) ->{
				JSlider source = (JSlider) e.getSource();
				if (!source.getValueIsAdjusting()) {
					valueStrafen = source.getValue();
					if (valueSperren + valueStrafen > 100) {
						valueSperren = 100 - valueStrafen;
					}
					sperrenSlider.setValue(valueSperren);
					aufgabenSlider.setValue(100 - valueSperren - valueStrafen);
				}
			});
			lenghtSlieder.addChangeListener((e) -> {
				JSlider source = (JSlider) e.getSource();
				if (!source.getValueIsAdjusting()) {
					gameLenght = source.getValue();
				}
			});
			
			einstellungenPanel.add(panel, BorderLayout.NORTH);
		}
		return einstellungenPanel;
	}

	private void safeState() {
		File dir = new File(System.getProperty("user.home") + "/xxx/");
		dir.mkdirs();
		try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(dir.getAbsolutePath() + "/safe.dat"))) {
			SavedState state = new SavedState();
			state.setSpieler(spieler);
			oos.writeObject(state);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private SavedState loadState() {
		File file = new File(System.getProperty("user.home") + "/xxx/safe.dat");
		if (file.exists()) {
			try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
				return (SavedState) ois.readObject();
			} catch (IOException | ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	private JScrollPane getPlayerPanel() {
		if (playerPanel == null) {

			JPanel contentPane = new JPanel(new BorderLayout(10,10));

			JPanel addRemovePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
			
			Button add = new Button("+");
			add.addActionListener((e)  -> {
				if (spieler.size() < 8) {					
					SpielerBean neuerSpieler = new SpielerBean();
					neuerSpieler.setName("Neuer Spieler");
					neuerSpieler.setRollen(new ArrayList<>(Arrays.asList(Rolle.values())));
					neuerSpieler.setWerkzeuge(new ArrayList<>(Arrays.asList(Werkzeug.values())));
					spieler.add(neuerSpieler);
					selectedSpieler = neuerSpieler;
					refreshSpieler();
					repaint();
					SwingUtilities.invokeLater(() -> spielerList.setSelectedValue(selectedSpieler, true));
				}
			});
			addRemovePanel.add(add);
			
			Button rmv = new Button("-");
			rmv.addActionListener((e) -> {
				if (selectedSpieler != null && spieler.size() > 2) {
					spieler.remove(selectedSpieler);
					selectedSpieler = spieler.get(0);
					spielerList.setSelectedValue(selectedSpieler, true);
					refreshSpieler();
					repaint();
				}
			});
			addRemovePanel.add(rmv);
			
			contentPane.add(addRemovePanel, BorderLayout.NORTH);
			
			
			spielerList = new JList<>();
			spielerList.setPreferredSize(new Dimension(200,200));
			spielerList.setModel(new ListModel<SpielerBean>() {
				private List<ListDataListener> l = new ArrayList<>();
				@Override
				public void removeListDataListener(ListDataListener l) {
					this.l.remove(l);
				}
				@Override
				public int getSize() {
					return spieler.size();
				}
				
				@Override
				public SpielerBean getElementAt(int index) {
					return spieler.get(index);
				}
				
				@Override
				public void addListDataListener(ListDataListener l) {
					this.l.add(l);
				}
			});
			spielerList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			spielerList.addListSelectionListener(new ListSelectionListener() {
				@Override
				public void valueChanged(ListSelectionEvent e) {
					if (!e.getValueIsAdjusting()) {
						selectedSpieler = spielerList.getSelectedValue();
						refreshSpieler();
						repaint();
					}
				}
			});
			contentPane.add(spielerList, BorderLayout.WEST);
			
			JPanel selectionPanel = new JPanel(new GridLayout(15, 1));
			
			selectionPanel.add(new JLabel("Name"));
			
			nameField = new JTextField();
			nameField.setText(selectedSpieler.getName());
			selectionPanel.add(nameField);
			nameField.getDocument().addDocumentListener(new DocumentListener() {
				@Override
				public void removeUpdate(DocumentEvent e) {
					try {
						updateName(e.getDocument().getText(0, e.getDocument().getLength()));
					} catch (BadLocationException e1) {
						e1.printStackTrace();
					}
				}
				
				@Override
				public void insertUpdate(DocumentEvent e) {
					try {
						updateName(e.getDocument().getText(0, e.getDocument().getLength()));
					} catch (BadLocationException e1) {
						e1.printStackTrace();
					}
				}
				
				@Override
				public void changedUpdate(DocumentEvent e) {
					try {
						updateName(e.getDocument().getText(0, e.getDocument().getLength()));
					} catch (BadLocationException e1) {
						e1.printStackTrace();
					}
				}
			});
			
			selectionPanel.add(new JLabel());
			selectionPanel.add(new JLabel("Rollen:"));
			
			rolleBestrafer = new JCheckBox(Rolle.BESTRAFENDE_PERSON.anzeigeText(), true);
			rolleBestrafer.setToolTipText(Rolle.BESTRAFENDE_PERSON.beschreibungsText());
			rolleBestrafer.addActionListener(event -> checkRolle(Rolle.BESTRAFENDE_PERSON, rolleBestrafer.isSelected()));
			selectionPanel.add(rolleBestrafer);
			
			rolleBestrafter = new JCheckBox(Rolle.BESTRAFTE_PERSON.anzeigeText(), true);
			rolleBestrafter.setToolTipText(Rolle.BESTRAFTE_PERSON.beschreibungsText());
			rolleBestrafter.addActionListener(event -> checkRolle(Rolle.BESTRAFTE_PERSON, rolleBestrafter.isSelected()));
			selectionPanel.add(rolleBestrafter);
			
			rolleBegluecker = new JCheckBox(Rolle.BEGLUECKENDE_PERSON.anzeigeText(), true);
			rolleBegluecker.setToolTipText(Rolle.BEGLUECKENDE_PERSON.beschreibungsText());
			rolleBegluecker.addActionListener(event -> checkRolle(Rolle.BEGLUECKENDE_PERSON, rolleBegluecker.isSelected()));
			selectionPanel.add(rolleBegluecker);
			
			rolleBeglueckter = new JCheckBox(Rolle.BEGLUECKTE_PERSON.anzeigeText(), true);
			rolleBeglueckter.setToolTipText(Rolle.BEGLUECKTE_PERSON.beschreibungsText());
			rolleBeglueckter.addActionListener(event -> checkRolle(Rolle.BEGLUECKTE_PERSON, rolleBeglueckter.isSelected()));
			selectionPanel.add(rolleBeglueckter);
			
			selectionPanel.add(new JLabel());
			selectionPanel.add(new JLabel("\"Werkzeuge\":"));
			
			mund = new JCheckBox(Werkzeug.MUND.anzeigeText(), true);
			mund.setToolTipText(Werkzeug.MUND.beschreibungsText());
			mund.addActionListener(evelt -> checkWerkzeug(Werkzeug.MUND, mund.isSelected()));
			selectionPanel.add(mund);
			
			vagina = new JCheckBox(Werkzeug.VAGINA.anzeigeText(), true);
			vagina.setToolTipText(Werkzeug.VAGINA.beschreibungsText());
			vagina.addActionListener(evelt -> checkWerkzeug(Werkzeug.VAGINA, vagina.isSelected()));
			selectionPanel.add(vagina);
			
			penis = new JCheckBox(Werkzeug.PENIS.anzeigeText(), true);
			penis.setToolTipText(Werkzeug.PENIS.beschreibungsText());
			penis.addActionListener(evelt -> checkWerkzeug(Werkzeug.PENIS, penis.isSelected()));
			selectionPanel.add(penis);
			
			anus = new JCheckBox(Werkzeug.ANUS.anzeigeText(), true);
			anus.setToolTipText(Werkzeug.ANUS.beschreibungsText());
			anus.addActionListener(evelt -> checkWerkzeug(Werkzeug.ANUS, anus.isSelected()));
			selectionPanel.add(anus);
			
			umschnalldildo = new JCheckBox(Werkzeug.UMSCHNALLDILDO.anzeigeText(), true);
			umschnalldildo.setToolTipText(Werkzeug.UMSCHNALLDILDO.beschreibungsText());
			umschnalldildo.addActionListener(evelt -> checkWerkzeug(Werkzeug.UMSCHNALLDILDO, umschnalldildo.isSelected()));
			selectionPanel.add(umschnalldildo);
			
			contentPane.add(selectionPanel , BorderLayout.CENTER);

			playerPanel = new JScrollPane(contentPane);
		}
		return playerPanel;
	}
	
	private void updateName(String string) {
		selectedSpieler.setName(string);
		repaint();
	}

	private void refreshSpieler() {
		nameField.setText(selectedSpieler.getName());
		
		rolleBestrafer.setSelected(selectedSpieler.getRollen().contains(Rolle.BESTRAFENDE_PERSON));
		rolleBestrafter.setSelected(selectedSpieler.getRollen().contains(Rolle.BESTRAFTE_PERSON));
		rolleBegluecker.setSelected(selectedSpieler.getRollen().contains(Rolle.BEGLUECKENDE_PERSON));
		rolleBeglueckter.setSelected(selectedSpieler.getRollen().contains(Rolle.BEGLUECKTE_PERSON));
		
		mund.setSelected(selectedSpieler.getWerkzeuge().contains(Werkzeug.MUND));
		vagina.setSelected(selectedSpieler.getWerkzeuge().contains(Werkzeug.VAGINA));
		penis.setSelected(selectedSpieler.getWerkzeuge().contains(Werkzeug.PENIS));
		anus.setSelected(selectedSpieler.getWerkzeuge().contains(Werkzeug.ANUS));
		umschnalldildo.setSelected(selectedSpieler.getWerkzeuge().contains(Werkzeug.UMSCHNALLDILDO));
	}
	
	private void checkRolle(Rolle rolle, boolean selected) {
		if (selected) {
			if (!selectedSpieler.getRollen().contains(rolle)) {
				selectedSpieler.getRollen().add(rolle);
			}
		} else {
			if (selectedSpieler.getRollen().contains(rolle)) {
				selectedSpieler.getRollen().remove(rolle);
			}
		}
	}
	
	private void checkWerkzeug(Werkzeug werkzeug, boolean selected) {
		if (selected) {
			if (!selectedSpieler.getWerkzeuge().contains(werkzeug)) {
				selectedSpieler.getWerkzeuge().add(werkzeug);
			}
		} else {
			if (selectedSpieler.getWerkzeuge().contains(werkzeug)) {
				selectedSpieler.getWerkzeuge().remove(werkzeug);
			}
		}
	}
	
	private JScrollPane getToyPanel() {
		if (toyPanel == null) {

			List<Toy> toys = provider.getToys();
			toys.sort((toy1, toy2) -> toy1.getBeschreibung().compareTo(toy2.getBeschreibung()));
			JPanel contentPane = new JPanel(new GridLayout(toys.size() / 2, 2));
			
			for (Toy toy : toys) {
				final String id = toy.getId();
				JCheckBox checkbox = new JCheckBox(toy.getBeschreibung(), true);
				checkbox.setToolTipText(toy.getBeschreibung());
				checkbox.addActionListener(event -> {
					if (checkbox.isSelected()) {
						Toy currentToy = toys.stream().filter(lToy -> lToy.getId().equals(id)).findFirst().get();
						selectedToys.put(id, currentToy);
					} else {
						selectedToys.remove(id);
					}
				});
				contentPane.add(checkbox);
			}
			toyPanel = new JScrollPane(contentPane);
		}
		return toyPanel;
	}
	
	@Override
	public void repaint() {
		super.repaint();
		if (spielerList != null) {			
			spielerList.updateUI();
		}
	}
}
