/*******************************************************************************
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package org.worldgrower.gui.conversation;

import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.KeyStroke;

import org.worldgrower.ManagedOperation;
import org.worldgrower.ManagedOperationListener;
import org.worldgrower.WorldObject;
import org.worldgrower.conversation.ConversationCategory;
import org.worldgrower.conversation.ConversationFormatter;
import org.worldgrower.conversation.Conversations;
import org.worldgrower.conversation.Question;
import org.worldgrower.conversation.Response;
import org.worldgrower.gui.AbstractDialog;
import org.worldgrower.gui.ActionContainingArgs;
import org.worldgrower.gui.ImageIds;
import org.worldgrower.gui.ImageInfoReader;
import org.worldgrower.gui.ImageSubstituter;
import org.worldgrower.gui.chooseworldobject.ChooseWorldObjectDialog;
import org.worldgrower.gui.music.SoundIdReader;
import org.worldgrower.gui.util.DialogUtils;
import org.worldgrower.gui.util.JButtonFactory;
import org.worldgrower.gui.util.JLabelFactory;
import org.worldgrower.gui.util.JProgressBarFactory;
import org.worldgrower.gui.util.MenuFactory;

public class AskQuestionDialog extends AbstractDialog implements ManagedOperationListener {

	private final Answerer answerer;
	private final JButton askQuestion;
	private final JLabel label;
	private final JProgressBar relationshipProgresBar;
	private final SoundIdReader soundIdReader;
	private final ImageInfoReader imageInfoReader;
	private final ConversationFormatter conversationFormatter;
	
	private class ExecuteQuestionAction extends AbstractAction implements ActionContainingArgs {
		
		private final Question question;
		private int subjectId = -1;
		
		public ExecuteQuestionAction(Question question) {
			this.question = question;
		}

		@Override
		public void actionPerformed(ActionEvent actionEvent) {
			int actualSubjectId;
			if (subjectId != -1) {
				actualSubjectId = subjectId;
			} else {
				actualSubjectId = question.getSubjectId();
			}
			
			int[] args = new int[] { question.getId(), actualSubjectId, question.getHistoryItemId(), question.getAdditionalValue(), question.getAdditionalValue2() };
			answerer.askQuestion(args);
			askQuestion.setEnabled(answerer.canAskQuestion());
		}

		@Override
		public void setArgs(int[] args) {
			subjectId = args[0];
		}
	}
	
	private class ChooseSubjectAction implements ActionListener {
		private final ExecuteQuestionAction executeQuestionAction;
		private final Question question;
		
		public ChooseSubjectAction(ExecuteQuestionAction executeQuestionAction, Question question) {
			super();
			this.executeQuestionAction = executeQuestionAction;
			this.question = question;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			ChooseWorldObjectDialog dialog = answerer.createChooseWorldObjectsDialog(executeQuestionAction, question, AskQuestionDialog.this);
			dialog.showMe();
		}
	}

	private final class CloseDialogAction implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			AskQuestionDialog.this.dispose();
		}
	}
	
	public AskQuestionDialog(Answerer answerer, Conversations conversations, ImageIds imageIdPerformer, ImageIds imageIdTarget, String performerName, String targetName, Map<Integer, ImageIds> subjectImageIds, ImageInfoReader imageInfoReader, SoundIdReader soundIdReader, JFrame parentFrame) {
		super(650, 300, imageInfoReader);
		this.answerer = answerer;
		this.soundIdReader = soundIdReader;
		this.imageInfoReader = imageInfoReader;
		ImageSubstituter imageSubstituter = new ImageSubstituter(imageInfoReader);
		this.conversationFormatter = new ConversationFormatterImpl(imageSubstituter);
		
		KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
        rootPane.registerKeyboardAction(new CloseDialogAction(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
			
		JPanel buttonPane = new JPanel();
		buttonPane.setBounds(0, 218, 632, 40);
		buttonPane.setOpaque(false);
		buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
		addComponent(buttonPane);

		JButton cancelButton = JButtonFactory.createButton("Cancel", imageInfoReader, soundIdReader);
		cancelButton.setActionCommand("Cancel");
		buttonPane.add(cancelButton);
		
		JButton okButton = JButtonFactory.createButton("OK", imageInfoReader, soundIdReader);
		okButton.setActionCommand("OK");
		buttonPane.add(okButton);
		getRootPane().setDefaultButton(okButton);

		JLabel performerLabel = JLabelFactory.createJLabel(imageInfoReader.getImage(imageIdPerformer, null));
		performerLabel.setToolTipText(performerName);
		performerLabel.setBounds(6, 17, 32, 48);
		addComponent(performerLabel);
		
		askQuestion = JButtonFactory.createButton("Ask Question", imageInfoReader, soundIdReader);
		askQuestion.setBounds(44, 27, 580, 22);
		askQuestion.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				JPopupMenu popupMenu = createQuestions(imageInfoReader, subjectImageIds, conversations, answerer);
				popupMenu.show(askQuestion, askQuestion.getX(), askQuestion.getY());
				
			}
			
		});
		addComponent(askQuestion);
		
		JLabel targetLabel = JLabelFactory.createJLabel(imageInfoReader.getImage(imageIdTarget, null));
		targetLabel.setToolTipText(targetName);
		targetLabel.setBounds(6, 90, 32, 48);
		addComponent(targetLabel);
		
		label = JLabelFactory.createJLabel(" ");
		label.setBounds(44, 50, 495, 200);
		addComponent(label);
		
		JLabel relationshipLabel = JLabelFactory.createJLabel("Relationship:");
		relationshipLabel.setToolTipText("Relationship with " + targetName);
		relationshipLabel.setBounds(6, 220, 100, 30);
		addComponent(relationshipLabel);
		
		relationshipProgresBar = JProgressBarFactory.createHorizontalJProgressBar(-1000, 1000, imageInfoReader);
		relationshipProgresBar.setBounds(110, 220, 300, 30);
		relationshipProgresBar.setValue(answerer.getRelationshipValue());
		relationshipProgresBar.setToolTipText("Relationship with " + targetName);
		addComponent(relationshipProgresBar);
		
		okButton.addActionListener(new CloseDialogAction());
		cancelButton.addActionListener(new CloseDialogAction());
		
		this.addWindowListener(new WindowAdapter()
	    {
	      public void windowClosed(WindowEvent e)
	      {
	        answerer.doneTalking();
	      }
	    });
		
		pressAskQuestionButtonOnVisible();
		DialogUtils.createDialogBackPanel(this, parentFrame.getContentPane());
	}

	private void pressAskQuestionButtonOnVisible() {
		ComponentListener listener = new ComponentAdapter() {
			
			// this method is called after componentShown and performs the click
			// otherwise the component may get moved/resized after clicking
			@Override
			public void componentResized(ComponentEvent e) {
				askQuestion.doClick();
			}
		};
		this.addComponentListener(listener);
	}
	
	private JPopupMenu createQuestions(ImageInfoReader imageInfoReader, Map<Integer, ImageIds> subjectImageIds, Conversations conversations, Answerer answerer) {
		Map<ConversationCategory, List<Question>> questionsMap = answerer.getQuestionPhrases();
		JPopupMenu popupMenu = MenuFactory.createJPopupMenu(imageInfoReader);
		for(Entry<ConversationCategory, List<Question>> entry : getQuestions(questionsMap)) {
			JMenu menu = MenuFactory.createJMenu(entry.getKey().getDescription(), entry.getKey().getImageId(), imageInfoReader, soundIdReader);
			popupMenu.add(menu);
			
			List<Question> questions = entry.getValue();
			if (questions.size() > 20 && questions.get(0).getSubjectId() != -1) {
				Map<Integer, JMenu> subMenus = new HashMap<>();
				
				fillSubMenu(answerer, entry, menu, subMenus);
				addQuestionsToSubMenu(imageInfoReader, subjectImageIds, entry, menu, subMenus);
				
 			} else {
 				if (questions.size() < 20) {
 					addQuestionsToMenu(imageInfoReader, subjectImageIds, answerer, menu, questions);
 				} else {
 					List<List<Question>> questionParts = splitList(questions, 20);
 					for(List<Question> questionPart : questionParts) {
 						JMenu menuPart = MenuFactory.createJMenu(getDescriptionForQuestions(questionPart), imageInfoReader, soundIdReader);
 						addQuestionsToMenu(imageInfoReader, subjectImageIds, answerer, menuPart, questionPart);
 						menu.add(menuPart);
 					}
 				}
			}
		}
		
		return popupMenu;
	}
	
	private void setMenuIcon(JMenuItem menuItem, ImageIds imageIds) {
		Image image = imageInfoReader.getImage(imageIds, null);
		menuItem.setIcon(new ImageIcon(image));
	}
	
	private String getDescriptionForQuestions(List<Question> questions) {
		String firstQuestion = questions.get(0).getQuestionPhrase(conversationFormatter);
		String lastQuestion = questions.get(questions.size()-1).getQuestionPhrase(conversationFormatter);
		int indexOfFirstDifferingCharacter = indexOfFirstDifferingCharacter(firstQuestion, lastQuestion);
		if (indexOfFirstDifferingCharacter != -1) {
			int firstQuestionEndIndex = Math.min(indexOfFirstDifferingCharacter+15, firstQuestion.length());
			int lastQuestionEndIndex = Math.min(indexOfFirstDifferingCharacter+15, lastQuestion.length());
			return "..."
					+ firstQuestion.substring(indexOfFirstDifferingCharacter, firstQuestionEndIndex) 
					+ " ... " 
					+ lastQuestion.substring(indexOfFirstDifferingCharacter, lastQuestionEndIndex)
					+ "...";
		} else {
			return firstQuestion + " ... " + lastQuestion;
		}
	}
	
	private int indexOfFirstDifferingCharacter(String s1, String s2) {
		int length = Math.min(s1.length(), s2.length());
		for(int i=0; i<length; i++) {
			if (s1.charAt(i) != s2.charAt(i)) {
				return i;
			}
		}
		return -1;
	}

	static <T> List<List<T>> splitList(List<T> list, final int listSize) {
	    List<List<T>> parts = new ArrayList<List<T>>();
	    final int totalListSize = list.size();
	    for (int i = 0; i < totalListSize; i += listSize) {
	        parts.add(new ArrayList<T>(list.subList(i, Math.min(totalListSize, i + listSize))));
	    }
	    return parts;
	}

	private void addQuestionsToMenu(ImageInfoReader imageInfoReader, Map<Integer, ImageIds> subjectImageIds, Answerer answerer, JMenu menu, List<Question> questions) {
		for(Question question : questions) {
			List<WorldObject> possibleSubjects = answerer.getPossibleSubjects(question);
			if (possibleSubjects != null && possibleSubjects.size() > 0) {
				JMenuItem questionMenuItem = createSubjectListMenuItem(imageInfoReader, subjectImageIds, question);
				menu.add(questionMenuItem);
			} else if (possibleSubjects == null) {
				JMenuItem questionMenuItem = createAskQuestionMenuItem(imageInfoReader, subjectImageIds, question);
				menu.add(questionMenuItem);
			}
		}
	}

	private JMenuItem createAskQuestionMenuItem(ImageInfoReader imageInfoReader, Map<Integer, ImageIds> subjectImageIds, Question question) {
		JMenuItem questionMenuItem = createQuestionMenuItem(imageInfoReader, subjectImageIds, question);
		questionMenuItem.addActionListener(new ExecuteQuestionAction(question));
		return questionMenuItem;
	}

	private JMenuItem createSubjectListMenuItem(ImageInfoReader imageInfoReader, Map<Integer, ImageIds> subjectImageIds, Question question) {
		JMenuItem questionMenuItem = createQuestionMenuItem(imageInfoReader, subjectImageIds, question);
		questionMenuItem.addActionListener(new ChooseSubjectAction(new ExecuteQuestionAction(question), question));
		questionMenuItem.setToolTipText("click to see list of possible subjects");
		return questionMenuItem;
	}

	private void addQuestionsToSubMenu(ImageInfoReader imageInfoReader, Map<Integer, ImageIds> subjectImageIds, Entry<ConversationCategory, List<Question>> entry, JMenu menu, Map<Integer, JMenu> subMenus) {
		for(Question question : entry.getValue()) {
			JMenuItem questionMenuItem = createAskQuestionMenuItem(imageInfoReader, subjectImageIds, question);
			int subjectId = question.getSubjectId();
			if (subjectId != -1) {
				subMenus.get(subjectId).add(questionMenuItem);
			} else {
				menu.add(questionMenuItem);
			}
		}
	}

	private void fillSubMenu(Answerer answerer, Entry<ConversationCategory, List<Question>> entry, JMenu menu, Map<Integer, JMenu> subMenus) {
		for(Question question : entry.getValue()) {
			int subjectId = question.getSubjectId();
			if (subjectId != -1) {
				if (!subMenus.containsKey(subjectId)) {
					String name = answerer.getDescription(subjectId);
					JMenu subMenu = MenuFactory.createJMenu(name, imageInfoReader, soundIdReader);
					subMenus.put(subjectId, subMenu);
					menu.add(subMenu);
				}
			}
		}
	}

	private JMenuItem createQuestionMenuItem(ImageInfoReader imageInfoReader, Map<Integer, ImageIds> subjectImageIds, Question question) {
		String questionPhrase = "<html>" + question.getQuestionPhrase(conversationFormatter) + "</html>";
		JMenuItem questionMenuItem = MenuFactory.createJMenuItem(questionPhrase, soundIdReader);
		int subjectId = question.getSubjectId();
		ImageIds subjectImageId = subjectImageIds.get(subjectId);
		if (subjectImageId != null) {
			questionMenuItem.setIcon(new ImageIcon(imageInfoReader.getImage(subjectImageId, null)));
		}
		return questionMenuItem;
	}

	private List<Entry<ConversationCategory, List<Question>>> getQuestions(Map<ConversationCategory, List<Question>> questionsMap) {
		List<Entry<ConversationCategory, List<Question>>> questions = new ArrayList<>();
		questions.addAll(questionsMap.entrySet());
		
		Collections.sort(questions, new Comparator<Entry<ConversationCategory, List<Question>>>() {
			@Override
			public int compare(Entry<ConversationCategory, List<Question>> o1, Entry<ConversationCategory, List<Question>> o2) {
				return o1.getKey().getDescription().compareTo(o2.getKey().getDescription());
			}
		});
		
		return questions;
	}

	public void showMe() {
		setVisible(true);
	}

	@Override
	public void actionPerformed(ManagedOperation managedOperation, WorldObject performer, WorldObject target, int[] args, Object value) {
		if (answerer.filterMessage(performer)) {
			Response response = (Response) value;
			String responsePhrase = response.getResponsePhrase(conversationFormatter);
			label.setText("<html>" + responsePhrase + "</html>");
			relationshipProgresBar.setValue(answerer.getRelationshipValue());
		}
	}
}