/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dowser.model;

import dict.IDictionary;
import dict.WNDictionary;
import dowser.model.Relation.Participant;
import edu.smu.tspell.wordnet.SynsetType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JOptionPane;

/**
 *
 * @author Chetan Pawgi
 */
public final class StaticModelValidator {

	public static interface IModelChangeListener {

		public boolean updateClassName(DomainModel model, dowser.model.Class cClass, String newName);

		public boolean updateAttributeName(DomainModel model, Attribute attribute, String newName);

		public boolean updateOperationName(DomainModel model, Operation operation, String newName);
	}
	private static List<IModelChangeListener> m_Listeners = new ArrayList<IModelChangeListener>();
	private static IDictionary dictionary = WNDictionary.getInstance();

	public static void addListener(IModelChangeListener listener) {
		if (!m_Listeners.contains(listener)) {
			m_Listeners.add(listener);
		}
	}

	protected static boolean notifyListeners(DomainModel model, dowser.model.Class cClass, String newName) {
		boolean bUpdate = false;
		for (IModelChangeListener listener : m_Listeners) {
			bUpdate |= listener.updateClassName(model, cClass, newName);
		}
		return bUpdate;
	}

	protected static boolean notifyListeners(DomainModel model, Attribute attrib, String newName) {
		boolean bUpdate = false;
		for (IModelChangeListener listener : m_Listeners) {
			bUpdate |= listener.updateAttributeName(model, attrib, newName);
		}
		return bUpdate;
	}

	protected static boolean notifyListeners(DomainModel model, Operation operation, String newName) {
		boolean bUpdate = false;
		for (IModelChangeListener listener : m_Listeners) {
			bUpdate |= listener.updateOperationName(model, operation, newName);
		}
		return bUpdate;
	}

	private static boolean isValidWord(String word) {
		word = word.replace("[_]", " ");
		Collection<String> arrSyns = dictionary.getSynonyms(word);
		return !(arrSyns == null || arrSyns.size() == 0);
	}

	private static boolean isValidNoun(String word) {
		word = word.replace("[_]", " ");
		Collection<String> arrSyns = dictionary.getSynonyms(word, SynsetType.NOUN);
		return !(arrSyns == null || arrSyns.size() == 0);
	}

	private static void checkExact(Collection<String> arrSyns, Set<String> setSuggestions, DomainModel model) {
		boolean exact = false;
		for (String s : arrSyns) {
			if (model.containsClass(s)) {
				setSuggestions.add(s);
				exact = true;
				break;
			}
		}
		if (!exact) {
			for (String s : arrSyns) {
				setSuggestions.add(s);
			}
		}
	}

	public static boolean ValidateModel(DomainModel model) {
		return ValidateModel(model, null);
	}

	public static boolean ValidateModel(DomainModel model, DomainModel ontoModel) {
		Collection<dowser.model.Class> lstClasses = new ArrayList<dowser.model.Class>(model.getClasses());
		boolean bUpdate = false;
		for (dowser.model.Class c : lstClasses) {
			if (isValidNoun(c.getName())) {
				continue;
			}
			String name = c.getName();
			while (!isValidNoun(name)) {
				if (c.getName().contains("_")) {
					String[] aWords = c.getName().split("[_]");
					boolean bFound = false;
					String strModel = "this";
					for (String w : aWords) {
						if (ontoModel != null && ontoModel.containsClass(w)) {
							strModel = "ontology";
						}
						if (model.containsClass(w)) {
							dowser.model.Class otherClass = model.getClass(w);
							if (c.getSuperClasses().contains(otherClass) || c.getSubClasses().contains(otherClass)) {
								bFound = true;
								break;
							}
							for (Relation r : c.getRelations()) {
								for (Participant p : r.getParticipants()) {
									if (p.getParticipatingClass().equals(otherClass)) {
										bFound = true;
										break;
									}
								}
								if (bFound) {
									break;
								}
							}
							if (bFound) {
								break;
							}
							JOptionPane.showMessageDialog(null, c.getName() + " doesn't seem to be a valid word.\nHowever " + w + " is a class in " + strModel + " model.\nYou might want to consider specifying some relationship between " + c.getName() + " and " + w);
							bFound = true;
							break;
						}
					}
					if (bFound) {
						break;
					}
				}
				int result = JOptionPane.showConfirmDialog(null, name + " doesn't seem to be a valid word !" + "\nDo you want to specify a different name ?", "Unkown Class Name", JOptionPane.YES_NO_OPTION);
				if (result == JOptionPane.YES_OPTION) {
					String msgWithSuggestions = "Enter a different name : ";
					Set<String> setSuggestions = new HashSet<String>();
					if (!c.getSubClasses().isEmpty()) {
						for (dowser.model.Class cSubClass : c.getSubClasses()) {
							Collection<String> arrSyns = dictionary.getHypernyms(cSubClass.getName());
							checkExact(arrSyns, setSuggestions, model);
						}
					}
					if (!c.getSuperClasses().isEmpty()) {
						for (dowser.model.Class cSuperClass : c.getSuperClasses()) {
							Collection<String> arrSyns = dictionary.getHyponyms(cSuperClass.getName());
							checkExact(arrSyns, setSuggestions, model);
						}
					}
					if (c.getRelationCount() > 0) {
						for (Relation r : c.getRelations()) {
							if (r.getName().startsWith("#aggregation") && r.getParticipantCount() == 2) {
								for (Participant p : r.getParticipants()) {
									int otherIndex = 1 - r.getParticipants().indexOf(p);
									if (p.getParticipatingClass().equals(c) && p.getRole().equals("sub-component")) {
										Collection<String> arrSyns = dictionary.getMeronyms(r.getParticipant(otherIndex).getParticipatingClass().getName());
										checkExact(arrSyns, setSuggestions, model);
									} else if (p.getParticipatingClass().equals(c) && p.getRole().equals("composite")) {
										Collection<String> arrSyns = dictionary.getHolonyms(r.getParticipant(otherIndex).getParticipatingClass().getName());
										checkExact(arrSyns, setSuggestions, model);
									}
								}
								break;
							}
						}
					}
					if (!setSuggestions.isEmpty()) {
						int iNewLineCount = 0;
						StringBuffer sBufSuggestions = new StringBuffer();
						for (String s : setSuggestions) {
							sBufSuggestions.append(s.toLowerCase().replaceAll("[ ]", "_") + ",");
							iNewLineCount = (iNewLineCount + 1) % 8;
							if (iNewLineCount == 0) {
								sBufSuggestions.append("\n");
							}
						}
						String strSuggestions = sBufSuggestions.toString().trim();
						strSuggestions = strSuggestions.substring(0, strSuggestions.length() - 1);
						msgWithSuggestions = "Suggestions : \n" + strSuggestions + "\n\nOR\n\n" + msgWithSuggestions;
					}
					String newName = JOptionPane.showInputDialog(msgWithSuggestions, c.getName());
					if (newName != null && !newName.trim().isEmpty() && !c.getName().equals(newName)) {
						name = newName.toLowerCase();
					} else {
						break;
					}
				} else {
					break;
				}
			}
			bUpdate |= notifyListeners(model, c, name);
		}

		lstClasses = new ArrayList<dowser.model.Class>(model.getClasses());
		for (dowser.model.Class c : lstClasses) {
			Collection<Attribute> attribs = c.getAttributes();
			for (Attribute a : attribs) {
				if (isValidWord(a.getName())) {
					continue;
				}
				String name = a.getName();
				while (!isValidWord(name)) {
					int result = JOptionPane.showConfirmDialog(null, name + " doesn't seem to be a valid word !" + "\nDo you want to specify a different name ?", "Unkown Attribute Name", JOptionPane.YES_NO_OPTION);
					if (result == JOptionPane.YES_OPTION) {
						String msg = "Enter a different name : ";
						String newName = JOptionPane.showInputDialog(msg, a.getName());
						if (newName != null && !newName.trim().isEmpty() && !a.getName().equals(newName)) {
							name = newName.toLowerCase();
						} else {
							break;
						}
					} else {
						break;
					}
				}
				bUpdate |= notifyListeners(model, a, name);
			}
		}

		lstClasses = new ArrayList<dowser.model.Class>(model.getClasses());
		for (dowser.model.Class c : lstClasses) {
			Collection<Operation> oprns = c.getOperations();
			for (Operation o : oprns) {
				if (isValidWord(o.getName())) {
					continue;
				}
				String name = o.getName();
				while (!isValidWord(name)) {
					int result = JOptionPane.showConfirmDialog(null, name + " doesn't seem to be a valid word !" + "\nDo you want to specify a different name ?", "Unkown Attribute Name", JOptionPane.YES_NO_OPTION);
					if (result == JOptionPane.YES_OPTION) {
						String msg = "Enter a different name : ";
						String newName = JOptionPane.showInputDialog(msg, o.getName());
						if (newName != null && !newName.trim().isEmpty() && !o.getName().equals(newName)) {
							name = newName.toLowerCase();
						} else {
							break;
						}
					} else {
						break;
					}
				}
				bUpdate |= notifyListeners(model, o, name);
			}
		}
		return bUpdate;
	}

	private StaticModelValidator() {
	}
}
