package Practicals.Ex4;

import java.io.*;
import java.util.*;
import Practicals.Ex3.FileWordStream;
import Practicals.Ex3.Word;
import Practicals.Ex3.WordStream;

public class FileConcordanceEx extends FileConcordance implements ConcordanceEx
{
	private TreeMap m_wordPathsMap = new TreeMap();	// String -> HashSet<String>

	public void addWords(WordStream ws)
	{
		while(ws.hasNext())
		{
			Word w = ws.next();

			// Maintain a list of the words in each file to make it more efficient to remove words from a specific
			// file later on if desired.
			HashSet hs = null;
			if(m_pathWordsMap.containsKey(w.path))
			{
				hs = (HashSet)m_pathWordsMap.get(w.path);
			}
			else
			{
				hs = new HashSet();
			}
			hs.add(w.text);
			m_pathWordsMap.put(w.path, hs);

			// NEW CODE: Maintain a list of the files which contain each word to make it more efficient to perform complex
			// queries later on.
			hs = null;
			if(m_wordPathsMap.containsKey(w.text))
			{
				hs = (HashSet)m_wordPathsMap.get(w.text);
			}
			else
			{
				hs = new HashSet();
			}
			hs.add(w.path);
			m_wordPathsMap.put(w.text, hs);

			// Add the word location to the map.
			LinkedList lst = null;
			if(m_wordLocationMap.containsKey(w.text))
			{
				lst = (LinkedList)m_wordLocationMap.get(w.text);
			}
			else
			{
				lst = new LinkedList();
			}
			lst.addLast(w);
			m_wordLocationMap.put(w.text, lst);
		}
	}

	public Set filesMentioning(String expression)
	{
		StringTokenizer tokenizer = new StringTokenizer(expression);
		Stack stack = new Stack();

		while(tokenizer.hasMoreTokens())
		{
			String token = tokenizer.nextToken();

			// Note that this isn't unary NOT, it's set subtraction
			if(token.equals("AND") || token.equals("NOT") || token.equals("OR"))
			{
				assert(stack.size() >= 2);

				HashSet rightSet = (HashSet)stack.pop();
				HashSet leftSet = (HashSet)stack.pop();

				if(token.equals("AND"))
				{
					leftSet.retainAll(rightSet);	// intersection of the two sets
					stack.push(leftSet);
				}
				else if(token.equals("NOT"))
				{
					leftSet.removeAll(rightSet);	// difference of the two sets
					stack.push(leftSet);
				}
				else	// token.equals("OR") == true
				{
					leftSet.addAll(rightSet);	// union of the two sets
					stack.push(leftSet);
				}
			}
			else
			{
				stack.push(((HashSet)m_wordPathsMap.get(token)).clone());
			}
		}

		return (Set)stack.pop();
	}

	public void remove(String path)
	{
		Iterator it = m_pathWordsMap.keySet().iterator();
		while(it.hasNext())
		{
			String word = (String)it.next();
			HashSet hs = (HashSet)m_wordPathsMap.get(word);
			hs.remove(path);
		}

		super.remove(path);
	}

	public static void main(String[] args) throws Exception
	{
		// CONCORDANCE SHELL
		FileConcordance conc = new FileConcordanceEx();
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

		for(;;)
		{
			System.out.print("Concordance Shell - Menu\n\n1. build <file list>\n2. extend <file list>\n" +
					 "3. remove <file list>\n4. save <filename>\n5. load <filename>\n6. print\n" +
					 "7. query <word>\n8. complexquery <expression>\n9. clear\n10. quit\n\n");

			System.out.print("> ");
			String command = reader.readLine();
			System.out.println();

			if(command.equals("quit")) break;

			if(command.length() >= 5 && command.substring(0,5).equals("build"))
			{
				conc = new FileConcordanceEx();
				StringTokenizer st = new StringTokenizer(command.substring(6));
				while(st.hasMoreTokens()) conc.addWords(new FileWordStream(st.nextToken()));
			}
			else if(command.length() >= 6 && command.substring(0,6).equals("extend"))
			{
				StringTokenizer st = new StringTokenizer(command.substring(7));
				while(st.hasMoreTokens()) conc.addWords(new FileWordStream(st.nextToken()));
			}
			else if(command.length() >= 6 && command.substring(0,6).equals("remove"))
			{
				StringTokenizer st = new StringTokenizer(command.substring(7));
				while(st.hasMoreTokens()) conc.remove(st.nextToken());
			}
			else if(command.length() >= 4 && command.substring(0,4).equals("save"))
			{
				conc.save(command.substring(5));
			}
			else if(command.length() >= 4 && command.substring(0,4).equals("load"))
			{
				conc = FileConcordance.load(command.substring(5));
			}
			else if(command.length() >= 5 && command.substring(0,5).equals("print"))
			{
				Iterator words = conc.words();
				while(words.hasNext())
				{
					String path = null;
					String word = (String)words.next();
					int n = 0;
					System.out.print(word);
					WordStream occurrences = conc.occurrences(word);
					while(occurrences.hasNext())
					{
						Word occurrence = occurrences.next();
						if(occurrence.path != path)
						{
							path = occurrence.path;
							System.out.print("\n\t");
							System.out.print(path);
							n = 0;
						}
						++n;
						if(n % 10 == 0) System.out.print("\n\t");
						else System.out.print(" ");
						System.out.print(occurrence.lineNum);
					}
					System.out.println();
				}
				System.out.println();
			}
			else if(command.length() >= 5 && command.substring(0,5).equals("query"))
			{
				String path = null;
				String word = command.substring(6);
				int n = 0;
				System.out.print(word);
				WordStream occurrences = conc.occurrences(word);
				while(occurrences.hasNext())
				{
					Word occurrence = occurrences.next();
					if(occurrence.path != path)
					{
						path = occurrence.path;
						System.out.print("\n\t");
						System.out.print(path);
						n = 0;
					}
					++n;
					if(n % 10 == 0) System.out.print("\n\t");
					else System.out.print(" ");
					System.out.print(occurrence.lineNum);
				}
				System.out.print("\n\n");
			}
			else if(command.length() >= 12 && command.substring(0,12).equals("complexquery"))
			{
				Set s = ((FileConcordanceEx)conc).filesMentioning(command.substring(13));
				Iterator it = s.iterator();
				while(it.hasNext())
				{
					System.out.println(it.next());
				}
				System.out.println();
			}
			else if(command.length() >= 5 && command.substring(0,5).equals("clear"))
			{
				conc = new FileConcordanceEx();
			}
			else
			{
				System.out.print("Invalid command!\n\n");
			}
		}
	}
}