package org.cocolab.inpro.dialogmanagement.composer;

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

/**
 * AVM Composer - Reads AVPairs.  Attempts composition of
 * new AVMs and subsequent unification of existing ones.
 * @author okko
 */
public class AVMComposer {

	private List<AVM> avmPrototypes = new ArrayList<AVM>();
	private List<AVM> avmList = new ArrayList<AVM>();
	
	/**
	 * Creates AVMComposer with a list of prototypes of
	 * different typed AVMs.
	 * New AVPairs will first be put into an UntypedAVM,
	 * for which unification will be attempted on each
	 * of these.  Non-null ones will be kept.
	 */
	AVMComposer() {
		this.setPrototypeAVMs();
	}
	
	public static void main(String[] args) {
		System.out.println("Starting AVM Composer.");
		AVMComposer composer = new AVMComposer();		

		// Below is a demonstration of what should happen when tags come in.

		ArrayList<AVPair> avps = new ArrayList<AVPair>();
		
		avps.add(new AVPair("color", "grün"));
		avps.add(new AVPair("name", "kreuz"));
//		avps.add(new AVPair("ord", "1"));
		avps.add(new AVPair("orient", "top"));
//		avps.add(new AVPair("rel", "next_to"));
//		avps.add(new AVPair("rel", "above"));

		for (AVPair avp : avps) {
			composer.unifyNewAVPair(avp);
			composer.printAVMs();
		}

	}

	/**
	 * Method to call when a new AVPair becomes known.
	 * Creates an UntypedAVM.  Attempt unification with known
	 * prototypes.  For all unified AVMs re-attempts unification
	 * with prototypes (for higher-order AVMs).  Lastly attempts
	 * unification with existing AVMs.
	 * @param avp
	 */
	private void unifyNewAVPair(AVPair avp) {
		System.out.println("Adding tag AVPair '" + avp.toString() + "'.");
		this.setPrototypeAVMs();
		UntypedAVM untyped = new UntypedAVM(avp);
		ArrayList<AVM> firstOrder= new ArrayList<AVM>();
		for (AVM proto : avmPrototypes) {
			AVM unified = proto.unify(untyped);
			if (unified != null) {
				if (!firstOrder.contains(unified)) {
					firstOrder.add(unified);
				}
			}			
		}

		ArrayList<AVM> secondOrder = new ArrayList<AVM>();
		for (AVM proto : avmPrototypes) {
			for (AVM unified : firstOrder) {
				AVM reUnified = proto.unify(unified);
				if (reUnified != null) {
					if (!secondOrder.contains(reUnified)) {
						secondOrder.add(reUnified);
					}
				}
			}
		}

		for (AVM avm : secondOrder) {
			if (!firstOrder.contains(avm)) {
				firstOrder.add(avm);
			}
		}		

		for (AVM a : firstOrder) {
			if (!avmList.contains(a)) {
				avmList.add(a);
			}
		}

		this.setPrototypeAVMs();
		ArrayList<AVM> thirdOrder = new ArrayList<AVM>();
		for (AVM proto : avmPrototypes) {
			for (AVM avm1 : avmList) {
				AVM reUnified = proto.unify(avm1);
				if (reUnified != null) {
					for (AVM avm2 : avmList) {
						AVM reReUnified = reUnified.unify(avm2);
						if (reReUnified != null) {
							thirdOrder.add(reReUnified);
						}
					}						
				}
			}
		}
		for (AVM a : thirdOrder) {
			if (!avmList.contains(a)) {
				avmList.add(a);
			}
		}

	}

	/**
	 * Sets prototype AVMs to be used for matching
	 * against UntypedAVMs (e.g. from new AVPairs).
	 */
	private void setPrototypeAVMs() {
		avmPrototypes.clear();
		avmPrototypes.add(new TileAVM());
		avmPrototypes.add(new FieldAVM());
		avmPrototypes.add(new LocationAVM());
		avmPrototypes.add(new RelativeLocationAVM());
		avmPrototypes.add(new RowAVM());
		avmPrototypes.add(new ColumnAVM());
	}

	/**
	 * Prints out all known AVMs.
	 */
	private void printAVMs() {
		System.out.println("AVMs:");
		for (AVM a : this.avmList) {
			System.out.println(a.toString());
		}
	}
}
@@ -63,22 +63,87 @@
 ava.
-util.ArrayList
+io.BufferedReader;%0Aimport java.io.IOException;%0Aimport java.io.InputStreamReader
 ;%0Aim
@@ -161,14 +161,16 @@
 til.
-HashSe
+ArrayLis
 t;%0Ai
@@ -796,18 +796,39 @@
 %5D args) 
-%7B%0A
+throws IOException %7B%0A/*
 %09%09System
@@ -1375,24 +1375,476 @@
 AVMs();%0A%09%09%7D%0A
+*/%0A%09%09interactiveTest();%0A%09%7D%0A%09%0A%09static void interactiveTest() throws IOException %7B%0A%09%09BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));%0A%09%09String line = stdin.readLine();%0A%09%09AVMComposer composer = new AVMComposer();%0A%09%09while (!line.equals(%22exit%22)) %7B%0A%09%09%09if (line.equals(%22new%22))  %0A%09%09%09%09composer = new AVMComposer();%0A%09%09%09else %7B%0A%09%09%09%09composer.unifyNewAVPair(new AVPair(line));%0A%09%09%09%7D%0A%09%09%09composer.printAVMs();%0A%09%09%09line = stdin.readLine();%0A%09%09%7D
 %0A%09%7D%0A%0A%09/**%0A%09 

add an interactive test mode to AVMComposer