package myPackage;

import java.util.ArrayList;
import java.util.List;

import ddf.minim.AudioSample;
import ddf.minim.Minim;

import processing.core.PApplet;
import processing.core.PFont;
import rita.*;
import rita.wordnet.RiWordnet;

/*import simplenlg.features.*;
import simplenlg.lexicon.*;*/

public class Memory extends PApplet 
{
	static final boolean FAST = false;
	static final int MAX_LENGTH = 45;
	
	String COMMON_WORDS = ". , 2 one I play pull all a an and is it about above across after against around at before behind below "
			+ "beneath beside veil besides between beyond but by each down during heap except for from in inside into "
			+ "there littler body floater horse like my near reverie of off on out outside over mum since the through throughout till to toward under "
			+ "until impermanent wait stand interesting plus up upon with without according because way addition front regard instead account "; // dch
	
	String VOWELS = "aeiou";


	//PlingStemmer pling = new PlingStemmer();
	//Lexicon lex = new Lexicon();
	
  RiWordnet wordnet;
	RiPosTagger tagger;
	RiStemmer stemmer;
	RiConjugator conjugator;
	RiPluralizer pluralizer;
	RiText text, ritexts[];

	Minim minim;
	String para1, comWords[], syns[];
	int timeStamp,countHolder,timeMemory,timeBackGround,textNum = 1;
	float backGroundColor = 0.0f, theFontColor = 255.0f;
	List[] myArrayOfAL = new ArrayList[121];
	float[] colorValsArray = new float[121];
	boolean backgroundCycleUp = true;
	 
	public void setup() {
		
	size(1050, 800); 
		
	wordnet = new RiWordnet(this);
	pluralizer = new RiPluralizer(this);
    conjugator = new RiConjugator(this);
    tagger = new RiPosTagger(this);
    stemmer = new RiStemmer(this);
	
    //The following is all test code, useful for finding errors, adjusting
    //words to ignore, words with multiple tags, etc.
    /*String tStr = "pictures";
    System.out.println(tStr+" wordnet tag = "+tagger.tagWordForWordNet(tStr));
    System.out.println(tStr+" wordnet tag = "+tagger.tagWordForWordNet(tStr));
    
    if (wordnet.isNoun(tStr)) {
    	System.out.println("Yes is N");
    }
    if (wordnet.isAdjective(tStr)) {
        	System.out.println("Yes is Adj");
    }
    if (wordnet.isAdverb(tStr)) {
        	System.out.println("Yes is Adv");
    }
    if (wordnet.isVerb(tStr)) {
        	System.out.println("Yes is Verb");
    }
    
    //String[] syns = wordnet.getAllHyponyms("church", "n"); 
    String[] syns = wordnet.getAllSynonyms("pictures", "n"); 
    //String[] syns = wordnet.getAllCoordinates("lurk", "v"); 
	if (syns != null) 
	{
		for (int a = 0; a < syns.length; a++) {
		String nStr = syns[a];
		System.out.println(nStr);
		
		}
	}*/
	
    RiText.setDefaultAlignment(LEFT);
    RiText.setDefaultFont("LondonBetween-12.vlw");

    
		//background(0, 0, 0);
		comWords = COMMON_WORDS.split(" ");
		String[] lines = loadStrings("ambulance2.txt");
		para1 = RiTa.join(lines," ");


		// fill array of ArrayLists which will keep track of
		// old Words that have been replaced; then these old
		// words can be "remembered" once the piece goes into
		// a state of trying to remember.
		for (int i = 0; i < myArrayOfAL.length; i++) {
			myArrayOfAL[i] = new ArrayList<String>();
		}
		
		// fill colorarray to keep track of a words current colorVal
		for (int i = 0; i < colorValsArray.length; i++) {
			colorValsArray[i] = 255.0f;
		}

		// clean up para1, removing punctuation so as to just have words
		String ts = para1;
		String tmp = RiText.regexReplace("([,.!?;:])", ts, " $1");
		String[] theWords = tmp.split(" ");

		// add each initial word from the paragraph read from file
		// into the first slot of the arrayList where the replacements
		// will be held. Thus, a replacement word for the first word
		// will go in the second slot of the array, etc., etc.
		for (int j = 0; j < theWords.length; j++) {
			List<String> ta = new ArrayList<String>();
			ta = myArrayOfAL[j];
			ta.add(theWords[j]);
		}

		reformat(); // draw initial paragraph on screen

		new Minim(this).loadFile("bells3.mp3").loop();		
	}

	public void draw() 
	{
		if (millis() - timeBackGround > 100) 
		{
			if (backgroundCycleUp) {
				backGroundColor = backGroundColor + 0.42f;
			} 
			else {
				backGroundColor = backGroundColor - 0.42f;
			}

			if (backGroundColor > 254.0) {
				backgroundCycleUp = false;
			}

			if (backGroundColor < 1.0) {
				backgroundCycleUp = true;
			}

			theFontColor = theFontColor - 1.0f;
			timeBackGround = millis();
		}
		background(backGroundColor);
		changeText();
	}

	//this method reformats the text once a change has been made
	//and displays the new paragraph on screen
	void reformat() {
		RiText.deleteAll(); //get rid of current rita text objs on screen
		String[] riWords = para1.split(" ");
		ritexts = RiText.createWords(this, riWords, 30, 70, MAX_LENGTH, 8);
		RiText[] rts = RiText.getInstances();
		// System.out.println("RTS = " + rts.length);
		for (int i = 0; i < rts.length; i++) {
			//rts[i].fill(random(255)); //, random(230,255), random(230,255) );
			rts[i].fill(colorValsArray[i]);
		}
	}

	//method to join words into a larger string
	/*public String join(Object[] full) {
		String result = "";

		if (full != null) {
			for (int index = 0; index < full.length; index++) {
				if (index == full.length - 1)
					result += full[index];
				else
					result += full[index] + " ";
			}
		}
		return result;
	}*/

	//method that checks to see if any of the words in the 
	//paragraph match common words. If a match is made these
	//words can then be skipped. Essential for retaining
	//legibility of text b/c wordnet will sometimes replace
	//common words with unwanted replacements
	public boolean checkForCommon(String s) {

		for (int i = 0; i < comWords.length; i++) {
			String theTempStr = comWords[i] + "."; // ohmy
			if ((comWords[i]).equals(s) || (theTempStr.equals(s))) {
				return false;
			}
		}
		return true;
	}

	//this is the main location where changes are made to the text
	public void changeText() {

		//once two minutes pass, 120000ms, then the text goes into
		//"remembering" state where it is more likely to remember a 
		//replacement that it has already made then to make a new replacement
		if (millis() - timeMemory > 120000) {
			textNum = (int)random(1, 10); //ran choose textNum which runs case statement below
		} 
		else { 
		  textNum = (int)random(1, 5); //1-5 is before remembering state begins
		}
		
		//this tests for the rate of change of replacements, change for faster/slower rates
		if (FAST || (millis() - timeStamp > 1000)) {
			String testStr = para1;
			String tmp = RiText.regexReplace("([,.!?;:])", testStr, " $1");
			String[] words = tmp.split(" ");
			String newWord = "";
			String tempNewWordHolder = "";
			
			//count is set to random so loop begins randomly in paragraph
			int count = (int) random(1, words.length);
			
			for (int i = 0; i < words.length; count++) 
			{
				i++;
				if (count == words.length) { // wrap at array end
					count = 0;
				}
				if (words[count].length() < 2) // skip punctuation
					continue;
				
				//switch statement which either changes verbs, nouns,
				//adverbs, or adjectives; or remembers an older word
				switch (textNum) {
  				case 1:
  					newWord = checkAdj(words[count]);
  					break;
  				case 2:
  					newWord = checkNoun(words[count]);
  					break;
  				case 3:
  					newWord = checkAdv(words[count]);
  					break;
  				case 4:
  					if (count<2) {
  						break;
  					}
  					newWord = checkVerb(words[count], words[count-1]);
  					break;
  				default:
  					newWord = remember(count);
  					count = countHolder;
  					break;
  		  }
         
			/*	if ((newWord != null) && (newWord.length()>0)) {
				if (!wordnet.exists(newWord)){
					System.out.println("OMG "+newWord);
						newWord = null;
						
				}
				}*/
				
				//newWord = "mum"; //can test words to make sure they don't get through
				if ((((newWord != null) && (newWord.length()>0)) && (checkForCommon(newWord)))) {
					//do this because I change the newWord string below if there
					//is an "a" or "an" -- so tempNewWordHolder is needed to add
					//the original newWord to the array of oldWords below
					tempNewWordHolder = newWord; 
					String theWordToChange = words[count];
					String tempString = words[count - 1]; //to check "a" and "an" agreement
					//System.out.println("The Old W = "+theWordToChange+" The New W = "+newWord);
					RiText[] rts = RiText.getInstances();
					for (int t = 0; t < rts.length; t++) { 
					String riString = rts[t].getText();
					if ((theWordToChange.equalsIgnoreCase(riString)) || 
				    		((theWordToChange+".").equalsIgnoreCase(riString)) || 
				    		((theWordToChange+",").equalsIgnoreCase(riString))) {
						//change the color of text if needed
						changeTextColor(textNum, t);
				    }
					}
					
					//this should be rewritten as its own method
					//it is the part which checks "a" and "an"
					//and then makes appropriate changes in the text
					String regex = "([aeiou])";
					String regex2 = "([^aeiou])";
					char chr = newWord.charAt(0);
					if ((tempString.equalsIgnoreCase("a"))
							&& (String.valueOf(chr).matches(regex))) 
					{
						theWordToChange = words[count - 1] + " " + words[count];
						if (Character.isUpperCase(tempString.charAt(0))) {
							newWord = "An " + newWord;
						} else {
							newWord = "an " + newWord;
						}
					} 
					else 
					{
						if ((tempString.equalsIgnoreCase("an"))
								&& (String.valueOf(chr).matches(regex2))) {
							theWordToChange = words[count - 1] + " "
									+ words[count];
							if (Character.isUpperCase(tempString.charAt(0))) {
								newWord = "A " + newWord;
							} else {
								newWord = "a " + newWord;
							}
						}
					} //end the "a" and "an" agreement part
					
					//here is where the replacement is actually made in the paragraph
					para1 = testStr.replaceFirst("\\b" + theWordToChange + "\\b", newWord);
					
					//following 5 lines needed to add the new word to the
					//arrays of words that keep track of the history of replacements
					List<String> tempArray = new ArrayList<String>();
					
					// THIS WAS THROWING AN EXCEPTION -- DCH
					tempArray = myArrayOfAL[Math.min(count, myArrayOfAL.length-1)]; // bounds-check
					
					if (textNum <=4) {
					  tempArray.add(tempNewWordHolder);
					}
					
					reformat(); //reformat the screen
					break;
				}
			}
			timeStamp = millis(); //reset timer to make changes
		}
	}

	//this method changes the text's color after a replacement; if a new replacement
	//is made the text gets darker; if an old word is remembered, the
	//text gets lighter
	private void changeTextColor(int textNum2, int t) {
		if (textNum2 <=4) {
			if ((colorValsArray[t]) <= 0) {
				colorValsArray[t] = 0;
			} else {
            colorValsArray[t] = colorValsArray[t]-50;
			}
		} else {
			if ((colorValsArray[t]) >= 255) {
				colorValsArray[t] = 255;
			} else {
            colorValsArray[t] = colorValsArray[t]+50;
			}
		}
		
	}

	//method where the manipulation of the Array of Arraylists
	//"myArrayofAL" takes place. Checks size of random Arraylist and
	//if it contains more than one word (the original) it picks 
	//"remembers" the previous replacement and re-inserts the
	//word back into the text
	private String remember(int c) {
		
		List<String> tArray = new ArrayList<String>();
		
		for (int t = 0; t < myArrayOfAL.length; t++) 
		{
			if (c == myArrayOfAL.length) // wrap at array end
				c = 0;
			
			tArray = myArrayOfAL[c];
			
			String theOldWord = null;
			String tempPrintWord = null;
			
			if ((myArrayOfAL[c].size() < 2) && (myArrayOfAL[c].size() > 0)) {
				c++; // update counter and continue if an array of size 1 is found
				continue;
			}
			
			//if there are exactly two words; this is probably not needed now
			//since the next part could handle it if re-written
			if (myArrayOfAL[c].size() == 2) {
				theOldWord = (String)myArrayOfAL[c].get(0);
				myArrayOfAL[c].remove(1);
			}
			
		    if (myArrayOfAL[c].size() > 2) {

		     	/*System.out.println("START Size = "+myArrayOfAL[c].size()+" Count = "+c);
				for (int z = 0; z < myArrayOfAL[c].size(); z++) {
					tempPrintWord = (String)myArrayOfAL[c].get(z);
					System.out.println(z+" = "+tempPrintWord);
				}
				System.out.println("END");*/
		    	
				//need to subtract by 2 in order to get the item before the 
				//current one which is at the end of the array at myArrayOfAL[c].size()
			    theOldWord = (String)myArrayOfAL[c].get(myArrayOfAL[c].size()-2);
			    //remove the last item in the array
			    myArrayOfAL[c].remove(myArrayOfAL[c].size()-1);
		}
		    
		  //check the word against common words I want to ignore
			if (checkForCommon(theOldWord)) {
				countHolder = c;
				return theOldWord;
			}
			c++; //update counter if a word is not returned
		}
			return null;
		}

	//here are the methods for retrieving words from wordNet and making
	//appropriate changes to their structure if needed. NOTE there is a bug
	//in RiTaWN right now so I had to change the tagger of adverb to "a"
	//(when it should be "r") and the adjective tag to "r" (when it should be "a")
	private String checkAdv(String testStr) {

    if (wordnet.isAdverb(testStr))
    {
      if (tagger.tagWordForWordNet(testStr) != null)
      {
        if ((checkForCommon(testStr)) && (tagger.tagWordForWordNet(testStr).equalsIgnoreCase("a")))
        {
          //System.out.println("adverb = "+testStr+" "+tagger.tagWordForWordNet(testStr));
          String[] derivedAdj = wordnet.getAllDerivedTerms(testStr, "r"); // ,pos);
          if (derivedAdj != null)
          {
            String tempAdjStr = derivedAdj[(int) random(0, derivedAdj.length)];
            String[] newAdjSyns = wordnet.getAllSynonyms(tempAdjStr, "a");
            if (newAdjSyns != null)
            {
              String newStr = newAdjSyns[(int) random(0, newAdjSyns.length)];
              newStr = changeToAdverb(newStr);

              if (wordnet.exists(newStr))
              {
                char chr = testStr.charAt(0);
                if (Character.isUpperCase(chr))
                {
                  newStr = changeFirstLetterToUpperCase(newStr);
                }
                return newStr;
              }
            }
          }
        }
      }
    }
    return null;
	}

	private String changeToAdverb(String nStr) {

		if (nStr.endsWith("y")) {
			String cutStr = nStr.substring(0, nStr.length() - 1);
			nStr = cutStr + "ily";
			return nStr;
		} else {
			if (nStr.endsWith("ic")) {
				nStr = nStr + "ally";
				return nStr;
			} else {
				nStr = nStr + "ly";
				return nStr;
			}
		}
	}

	private String checkVerb(String testStr, String testStrBefore) {

		if (testStr.equalsIgnoreCase("am")) {
			return "was";
		} else if (testStr.equalsIgnoreCase("was")) {
			return "am";
		}
		
		if (wordnet.isVerb(testStr)) {
			if (checkForCommon(testStr)
					&& (tagger.tagWordForWordNet(testStr).equalsIgnoreCase("v"))) {
				String[] syns = wordnet.getAllCoordinates(testStr, "v"); // ,pos);
				if (syns != null) {
					String newStr = syns[(int) random(0, syns.length)];
					//System.out.println("old word = " + testStr + " new word = " + newStr + ".");

					if (!wordnet.exists(newStr)) {
						return null;
					}

					if (testStr.endsWith("ing")) {
					  newStr = conjugator.getPresentParticiple(newStr);
						char chr = testStr.charAt(0);
						if (Character.isUpperCase(chr)) {
							newStr = changeFirstLetterToUpperCase(newStr);
						}
						//System.out.println("old word = " + testStr + " new word = " + newStr);
						return newStr;
					}
					
					if (testStrBefore.startsWith("I")) {
						newStr = conjugator.conjugate(RiTa.SINGULAR, RiTa.FIRST_PERSON, RiTa.PRESENT_TENSE, newStr);
						char chr = testStr.charAt(0);
						if (Character.isUpperCase(chr)) {
							newStr = changeFirstLetterToUpperCase(newStr);
						}
						return newStr;
					}

				}
			}
		}
		return null;

	}

	private String checkNoun(String testStr) {

		if (wordnet.isNoun(testStr)) 
		{
			if (tagger.tagWordForWordNet(testStr) != null) 
			{
				if ((checkForCommon(testStr))
						&& (tagger.tagWordForWordNet(testStr).equalsIgnoreCase("n"))) {

					String[] syns = wordnet.getAllSynonyms(testStr, "n"); 
					if (syns != null) 
					{
						String newStr = syns[(int) random(0, syns.length)];
						if ((newStr.equals("repeat")) || (newStr.endsWith("ful"))){
							//System.out.println("ow = " + testStr + " nw = "+newStr);
							return null;
						}

						if (pluralizer.isPlural(testStr)) {
							newStr = pluralizer.pluralize(newStr);
							if (!wordnet.exists(newStr)) {
								return null;
							}
						}

						if (newStr.endsWith("ing")) {
							return null;
						}

						char chr = testStr.charAt(0);
						if (Character.isUpperCase(chr)) {
							newStr = changeFirstLetterToUpperCase(newStr);
						}
						return newStr;

					}
				}
			}
		}
		return null;
	}

	private String checkAdj(String testStr) {

		if (wordnet.isAdjective(testStr)) {
			if (tagger.tagWordForWordNet(testStr) != null) {
				if ((checkForCommon(testStr)) && (tagger.tagWordForWordNet(testStr).equalsIgnoreCase("r"))) {
					//System.out.println("adjective = "+testStr+" "+tagger.tagWordForWordNet(testStr));
					String[] syns = wordnet.getAllSynonyms(testStr, "a"); // ,pos);
					if (syns != null) {
						String newStr = syns[(int) random(0, syns.length)];

						char chr = testStr.charAt(0);
						if (Character.isUpperCase(chr)) {
							newStr = changeFirstLetterToUpperCase(newStr);
						}

						return newStr;
					}

				}
			}
		}
		return null;
	}

	//needed to maintain grammar after periods, etc.
	private String changeFirstLetterToUpperCase(String nStr) {
		char chr2 = nStr.charAt(0);
		String newLetter = String.valueOf(chr2);
		newLetter = newLetter.toUpperCase();
		String cutStr = nStr.substring(1, nStr.length());
		nStr = newLetter + cutStr;
		return nStr;
	}
	
	//quit on "q" press
	public void keyPressed() {
		  if (key == 'q') {
			  System.exit(0);
		  }
		}
	
	public static void main(String[] args) {
		PApplet.main(new String[] { "--present", Memory.class.getName() });
	}
}
