package com.jens.rhasspy.visualtool;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Robot;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;

import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;

import com.jens.rhasspy.visualtool.lib.JSONObject;

/*
 * Layout types
 * https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
 */

public class GUI
{	
	static JFrame mainFrame = new JFrame(Settings.programName);
	static JLabel connectionStatusTextField = new JLabel();
	static JTextArea mainTextField = new JTextArea();
	static JScrollPane mainTextFieldHolder = new JScrollPane(mainTextField);
	static JPanel optionsFrame = new JPanel();
	static GUI instance = null;
	Font defaultFont = new Font(Font.SANS_SERIF, Font.PLAIN, 20);
	
	final String idleMessage = "Waiting for session...";
	final int idleTime = 2000;
	boolean initialConnectionEstablished = false;
	
	static Dimension defaultContainerDim = new Dimension(700, 250);
	static Dimension zeroDim = new Dimension(0, 0);
	
	ArrayList<String> textEntriesList = new ArrayList<>();

	public static void main(String[] args)
	{
		if(Settings.readSettings())
		{
			getInstance().openWindow();
			
			// Subscribe to MQTT topics
			
			MQTT.getInstance().subscribeToTopic(Settings.topicNameWakewordRecognized);
			MQTT.getInstance().subscribeToTopic(Settings.topicNameDisplayText);
			MQTT.getInstance().subscribeToTopic(Settings.topicNameDisplayOptions);
			MQTT.getInstance().subscribeToTopic(Settings.topicNameIntentsRecognized);
			MQTT.getInstance().subscribeToTopic(Settings.topicNameSayText);
			MQTT.getInstance().subscribeToTopic(Settings.topicNamePlaySoundFinished);
			MQTT.getInstance().subscribeToTopic(Settings.topicNameSessionEnded);
		}
	}
	
	public static GUI getInstance()
	{
		if(instance == null)
			instance = new GUI();
		
		return instance;
	}
	
	void openWindow()
	{
		mainFrame.setLayout(new BoxLayout(mainFrame.getContentPane(), BoxLayout.Y_AXIS));
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainFrame.setSize(700, 320);
		mainFrame.setLocation(50, 50);
		
		try
		{
			Thread.sleep(1000);
		}
		catch (InterruptedException e)
		{
			Miscellaneous.logEvent(Miscellaneous.getStackTraceAsString(e), 1);
		}

		connectionStatusTextField.setText("Connected: " + String.valueOf(MQTT.getInstance().getClient().isConnected()));
		Border border = connectionStatusTextField.getBorder();
		Border margin = new EmptyBorder(10,10,10,10);
		connectionStatusTextField.setBorder(new CompoundBorder(border, margin));
		
		mainTextField.setText(idleMessage);
		mainTextField.setFont(defaultFont);
		if(mainTextField.isEditable())
			mainTextField.setEditable(false);
		Border borderMainTextField = mainTextField.getBorder();
		Border marginMainTextField = new EmptyBorder(10,10,10,10);
		mainTextField.setBorder(new CompoundBorder(borderMainTextField, marginMainTextField));
		
		mainTextFieldHolder.setPreferredSize(defaultContainerDim);
		mainTextFieldHolder.setVisible(true);
		optionsFrame.setPreferredSize(defaultContainerDim);
		optionsFrame.setVisible(false);
		optionsFrame.setLayout(new BoxLayout(optionsFrame, BoxLayout.Y_AXIS));
		
		mainFrame.add(connectionStatusTextField);
		mainFrame.add(mainTextFieldHolder);
		mainFrame.add(optionsFrame);
		
		mainFrame.setVisible(true);
		
		if(Settings.hideWindowWhenIdle && initialConnectionEstablished)
			minimizeDelayed();
	}
	
	public void updateConnectionStatus(boolean connected)
	{
		if(connected)
		{
			if(!initialConnectionEstablished)
				initialConnectionEstablished = true;
			
			connectionStatusTextField.setText("Connected to: " + Settings.mqttClientServerHostname);
			
			if(Settings.hideWindowWhenIdle)
				minimizeDelayed();
		}
		else
		{
			connectionStatusTextField.setText("Not connected");
			getFocus();
		}
	}
	
	public void updateWindow()
	{		
		if(mainTextField.isEditable())
			mainTextField.setEditable(false);
		
		mainTextField.setLineWrap(true);
		mainTextField.setText(Miscellaneous.arrayImplode(Miscellaneous.lineSeparator, textEntriesList));

		JScrollBar vertical = mainTextFieldHolder.getVerticalScrollBar();
		vertical.setValue(vertical.getMaximum());
				
		mainFrame.pack();
		
		mainTextFieldHolder.setVisible(true);
		optionsFrame.setVisible(false);		
		
		mainFrame.repaint();
		
		if(textEntriesList.contains(idleMessage))
		{
			if(Settings.hideWindowWhenIdle)
				minimizeDelayed();
		}
		else
			getFocus();
	}
	
	public void displayOptions(String[] options)
	{
		optionsFrame.removeAll();		

		JLabel infoLabel = new JLabel("Select an option:");
		infoLabel.setFont(defaultFont);
		optionsFrame.add(infoLabel);
		Border labelBorder = optionsFrame.getBorder();
		Border labelMargin = new EmptyBorder(10,10,10,10);
		optionsFrame.setBorder(new CompoundBorder(labelBorder, labelMargin));
		
		for(String option : options)
		{
			JLabel optionTf = new JLabel(option);
			optionTf.setFont(defaultFont);
			Border border = optionTf.getBorder();
			Border margin = new EmptyBorder(10,10,10,10);
			optionTf.setBorder(new CompoundBorder(border, margin));

			optionTf.addMouseListener(new MouseListener()
			{				
				@Override
				public void mouseReleased(MouseEvent e)
				{
				}
				
				@Override
				public void mousePressed(MouseEvent e)
				{
				}
				
				@Override
				public void mouseExited(MouseEvent e)
				{
				}
				
				@Override
				public void mouseEntered(MouseEvent e)
				{
				}
				
				@Override
				public void mouseClicked(MouseEvent e)
				{
					JOptionPane.showMessageDialog(mainFrame, "Option " + option + " selected.");
				}
			});
			optionsFrame.add(optionTf);
		}
		
//		mainTextFieldHolder.setPreferredSize(zeroDim);
//		optionsFrame.setPreferredSize(defaultContainerDim);
		
		mainFrame.pack();
		
		optionsFrame.setVisible(true);
		mainTextFieldHolder.setVisible(false);
		
		mainFrame.repaint();
	}
	
	public void handleNewMessage(Object[] messageArray)
	{
		/*
		 *	Object[] object = new Object[3];
			object[0] = message;
			object[1] = topic;
			object[2] = Calendar.getInstance();
		 */
		
		String payload = new String(((MqttMessage)messageArray[0]).getPayload());
				
		String topic = (String)messageArray[1];
		
		if(MqttTopic.isMatched(Settings.topicNameWakewordRecognized, topic))
		{
			wakeUpScreen();
			
			textEntriesList.clear();
			textEntriesList.add(getMeString() + "Hello! What can I do for you?");
			updateWindow();
		}
		else if(MqttTopic.isMatched(Settings.topicNameIntentsRecognized, topic))
		{
			wakeUpScreen();
			
			JSONObject jo = new JSONObject(payload);
			textEntriesList.clear();
			textEntriesList.add(getYouString() + jo.getString("input"));
			updateWindow();
		}
		else if(MqttTopic.isMatched(Settings.topicNameDisplayText, topic))
		{
			JSONObject jo = new JSONObject(payload);
						
			textEntriesList.add(getMeString() + jo.getString("text"));
			updateWindow();
		}
		else if(MqttTopic.isMatched(Settings.topicNameDisplayOptions, topic))
		{
			JSONObject jo = new JSONObject(payload);
			
			String[] options = jo.getString("text").split(";");
			displayOptions(options);
			
		}
		else if(MqttTopic.isMatched(Settings.topicNameSayText, topic))
		{
			JSONObject jo = new JSONObject(payload);
			textEntriesList.add(getMeString() + jo.getString("text"));
			updateWindow();
		}
		else if(MqttTopic.isMatched(Settings.topicNameSessionEnded, topic))
		{
			textEntriesList.clear();
			textEntriesList.add(idleMessage);
			updateWindow();
		}		
	}
	
	static String getYouString()
	{
		return "You: \t";
	}
	
	static String getMeString()
	{
		return "Me: \t";
	}
	
	static void wakeUpScreen()
	{
		/*
		 * Original plans based on console commands.
		 * 
		 * pi@raspberrypi11:~ $ DISPLAY=:0 xset q
			Keyboard Control:
			  auto repeat:  on    key click percent:  0    LED mask:  00000000
			  XKB indicators:
			    00: Caps Lock:   off    01: Num Lock:    off    02: Scroll Lock: off
			    03: Compose:     off    04: Kana:        off    05: Sleep:       off
			    06: Suspend:     off    07: Mute:        off    08: Misc:        off
			    09: Mail:        off    10: Charging:    off    11: Shift Lock:  off
			    12: Group 2:     off    13: Mouse Keys:  off
			  auto repeat delay:  500    repeat rate:  33
			  auto repeating keys:  00ffffffdffffbbf
			                        fadfffefffedffff
			                        9fffffffffffffff
			                        fff7ffffffffffff
			  bell percent:  50    bell pitch:  400    bell duration:  100
			Pointer Control:
			  acceleration:  20/10    threshold:  10
			Screen Saver:
			  prefer blanking:  yes    allow exposures:  yes
			  timeout:  600    cycle:  600
			Colors:
			  default colormap:  0x20    BlackPixel:  0x0    WhitePixel:  0xffffff
			Font Path:
			  built-ins
			DPMS (Energy Star):
			  Standby: 600    Suspend: 600    Off: 600
			  DPMS is Enabled
			  Monitor is Off
			pi@raspberrypi11:~ $
		 */
		
		Thread t = new Thread(new Runnable()
		{
			@Override
			public void run()
			{
				try
				{
				    /*
				     *  Move the mouse cursor
				     *  
				     *  Using this amount of movements has proven relatively reliable.
				     *  Using less has not always woken up the screen or only woke it
				     *  up, but did not also unblank it, resulting in a well-lit, but
				     *  still black screen.
				     */
					
				    Robot robot = new Robot();
				    for(int xCoord = 500; xCoord < 550; xCoord++)
				    	for(int yCoord = 500; yCoord < 550; yCoord++)
				    		robot.mouseMove(xCoord, yCoord);
				}
				catch(Exception e)
				{
					
				}
			}});
		
		t.start();
	}
	
	void getFocus()
	{
		java.awt.EventQueue.invokeLater(new Runnable()
		{
		    @Override
		    public void run()
		    {
		    	mainFrame.setState(Frame.NORMAL);
		        mainFrame.toFront();
		        mainFrame.repaint();
		    }
		});
	}
	
	void minimizeDelayed()
	{
		try
		{
			Thread.sleep(idleTime);
		}
		catch(Exception e)
		{}
		
		mainFrame.setState(Frame.ICONIFIED);
	}
}