package tools;

import java.io.File;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.io.DirectoryWalker;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import authoritymanager.client.DataSerial;
import authoritymanager.client.DataPair;
import authoritymanager.client.SearchQuery;
import authoritymanager.client.Utilities;

/*
 * @author      (classes and interfaces only, required)
 * @version     (classes and interfaces only, required. See footnote 1)
 * @param       (methods and constructors only)
 * @return      (methods only)
 * @exception   (@throws is a synonym added in Javadoc 1.2)
 * @see         
 * @since       
 * @serial      (or @serialField or @serialData)
 * @deprecated  (see How and When To Deprecate APIs)
 */
/**
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * @author Dejan Kolundzija
 * @version 1.0
 */
public class Authority implements Serializable {
	public static String PATH		=	new File("").getAbsolutePath() + File.separatorChar + "data" + File.separatorChar  ;
	public static final String DEFAULT_NAME = "DEFAULT_NAME";
	public static final int DEFAULT_ID = 0;

	public static Random generator = new Random(System.currentTimeMillis());

	private String authorityType ;
	public String authorityName;
	public int authorityID;
	public HashMap<String, ElementData> data;
	public Configuration configuration;
	public Configuration confManifestation;
	public PrintWriter writer;
	public String sortingField;
	private int numFiles, numProcessed ;
	private static Map<String, Authority> typeToAuthority = new HashMap<String, Authority>() ;
	private boolean isLoaded, isChanged ;
	
	public Authority() {
		this.authorityName = DEFAULT_NAME;
		this.authorityID = DEFAULT_ID;
		this.data = new HashMap<String, ElementData>();
		this.configuration = null;
		this.authorityType = null ;
		
		setLoaded(false) ;
		setChanged(false) ;
	}

	public Authority(PrintWriter writer) {
		this();
		this.writer = writer;
	}

	public Authority(Configuration configuration) {
		this() ;
		this.configuration = configuration;
	}

	public Authority(String authorityType, String configurationURL) {
		this(configurationURL) ;
		this.setType(authorityType) ;
	}
	public static ElementData getByID(String itemID) {
		Collection<Authority> authorities = typeToAuthority.values() ;
		for (Iterator<Authority> itAuthority = authorities.iterator() ; itAuthority.hasNext() ; ) {
			Authority authority = itAuthority.next() ;
			ElementData result = authority.data.get(itemID) ;
			if (result != null) {
				return result ;
			}
		}
		return null ;
	}
	public Authority(HashMap<String, ElementData> data,
			Configuration configuration) {
		this.data = data;
		this.configuration = configuration;
	}
	public String getType() {
		return authorityType ;
	}
	public void setType(String authorityType) {
		this.authorityType = authorityType ;
		Authority.typeToAuthority.put(authorityType, this) ;		
	}
	public Authority(String configurationURL) {
		this();
		long curTime;

		curTime = System.currentTimeMillis();
		this.loadConfiguration(configurationURL) ;
		// BUG: this breaks linux
//		this.configuration.setURL("data" + File.separatorChar + configuration.getURL()) ;
		this.loadData() ;
		
	}
	
	public boolean isLoaded() {
		return this.isLoaded ;
	}
	
	public boolean isChanged() {
		return this.isChanged ;
	}
	
	public void setLoaded(boolean isLoaded) {
		this.isLoaded = isLoaded ;
	}
	
	public void setChanged(boolean isChanged) {
		this.isChanged = isChanged ;
	}
	
	public static Collection<Authority> getAuthorities() {
		return typeToAuthority.values() ;
	}
	
	public void loadData() {
		long initTime = System.currentTimeMillis();
		String URL = configuration.getURL() ;
		System.out.println("LOADING " + URL);	
		numFiles = countFiles(new File(URL)) ;
		numProcessed = 0 ;
		processURL(new File(URL));
		setLoaded(true) ;
		System.out.println("Total time reading " + 1.000
				* (System.currentTimeMillis() - initTime) + "ms");
	}
	
	private void loadConfiguration(String configurationURL) {
		configuration = new Configuration(configurationURL);
		configuration.compile();
	}
	
	public static Authority fromDump(String dumpDir, String confURL) {
		Authority authority = new Authority() ;
		authority.loadConfiguration(confURL) ;
		authority.configuration.setURL(dumpDir) ;
		authority.loadData() ;
		return authority ;
	}
	
	public static Authority byType(String type) {
		return typeToAuthority.get(type) ;
	}
	
	public void setConfiguration(String configurationURL) {
		this.loadConfiguration(configurationURL) ;
	}
	
	private int countFiles(File file) {
		int count = 0 ;
		if (file.isFile()) {
			count ++;
		}
		if (file.isDirectory()) {
			File[] fileChildren = file.listFiles();
			for (int iFile = 0; iFile < fileChildren.length; iFile++) {
				count += countFiles(fileChildren[iFile]);
			}
		}
		return count ;
	}
	private void processURL(File file) {
		if (file.isFile()) {
			createFromFile(file);
		} else if (file.isDirectory()) {
			File[] fileChildren = file.listFiles();
			for (int iFile = 0; iFile < fileChildren.length; iFile++) {
				processURL(fileChildren[iFile]);
			}
		} else {
			throw new IllegalArgumentException("is not a file, is not a dir... wtf? does it exist? " + file.exists());
		}
	}

	
	public boolean addElement(ElementData element) {
		return this.data.put((String) element.getField("efgIdentifier").iterator().next(), element) != null;
	}

	private char dec2hex(int value) {
		if (value < 10)
			return (char) ('0' + value);
		else
			return (char) ('A' + (value - 10));
	}

	public String genID(int length) {
		String id = "";
		for (int i = 0; i < length; i++) {
			id += dec2hex(generator.nextInt() % 16);
		}
		return id;
	}

	public ElementData getElement(long id) {
		return this.data.get("" + id);
	}

	public Object[] getAllData() {
		return data.values().toArray();
	}

	public static String readField(Element element, String tagName) {
		try {
			NodeList nodeList = element.getElementsByTagName(tagName);
			Element tagElement = (Element) nodeList.item(0);
			NodeList textList = tagElement.getChildNodes();
			return ((Node) textList.item(0)).getNodeValue().trim();
		} catch (Exception e) {
			return null;
		}
	}

	public void dump(String dumpDir) {
		Collection<ElementData> values = data.values() ;
		String dumpFile = "d_" + authorityType.replace(" ", "") ;
		int countXML = 0, xmlPerFile = 10, countFile = 0;
		try {
			PrintWriter curFile = new PrintWriter(new File(dumpDir + dumpFile + "_" + countFile + ".xml")) ;
			for (Iterator<ElementData> itValue = values.iterator() ; itValue.hasNext() ; ) {
				ElementData curValue = itValue.next() ;
				curFile.println(curValue.toXML()) ;
				countXML ++ ;
				if (countXML >= xmlPerFile) {
					countXML = 0 ;
					curFile.flush() ;
					curFile.close() ;
					countFile ++ ;
					curFile = new PrintWriter(new File(dumpDir + dumpFile + countFile + ".xml")) ;
				}
			}
			curFile.flush() ;
			curFile.close() ;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	public void visit(Node node) {
		NamedNodeMap map = node.getAttributes();
		System.out.println("Nodename: " + node.getNodeName() + ", nodetype: "
				+ node.getNodeType() + ", nodevalue: " + node.getNodeValue());
		System.out.print("Attributes: [");

		if (map != null)
			for (int iAttr = 0; iAttr < map.getLength(); iAttr++) {
				Node attr = map.item(iAttr);
				visit(attr);
			}
		System.out.println("]");
		NodeList children = node.getChildNodes();
		System.out.print("Children: [");
		for (int iChild = 0; iChild < children.getLength(); iChild++) {
			Node child = children.item(iChild);
			visit(child);
		}
		System.out.println("]");
	}

	public void createFromFile2(File file) {
		try {
			DocumentBuilderFactory domFactory = DocumentBuilderFactory
					.newInstance();
			domFactory.setNamespaceAware(false);
			DocumentBuilder builder = domFactory.newDocumentBuilder();
			Document doc = builder.parse(file);
			doc.getDocumentElement().normalize();

			Element element = doc.getDocumentElement();
			visit(element);
		} catch (Exception e) {
			System.out.println("Exception: " + e);
		}
	}

	public void createFromFile(File file) {
		try {
			//System.out.println("ELABORATING: " + file.getAbsolutePath()) ;
			NamespaceContext context = new NamespaceContext() {
				@Override
				public String getNamespaceURI(String prefix) {
					return "";
				}

				public String getPrefix(String arg0) {
					return null;
				}

				public Iterator getPrefixes(String arg0) {
					return null;
				}
			};
			DocumentBuilderFactory domFactory = DocumentBuilderFactory
					.newInstance();
			domFactory.setNamespaceAware(true);
			// domFactory.setNamespaceAware(true) ;
			DocumentBuilder builder = domFactory.newDocumentBuilder();
			Document doc = builder.parse(file);
			doc.getDocumentElement().normalize();
			/*
			 * //NodeList elements =
			 * doc.getElementsByTagName(configuration.getRootPath()) ; XPath
			 * xpath = XPathFactory.newInstance().newXPath(); //XPathExpression
			 * expr = xpath.compile(configuration.getRootPath());
			 * xpath.setNamespaceContext(context) ; XPathExpression expr =
			 * xpath.compile("//dateStamp") ; NodeList elements = (NodeList)
			 * expr.evaluate(doc, XPathConstants.NODESET) ;
			 */
			/*
			Element root = doc.getDocumentElement();
			NodeList records = root.getElementsByTagName("efg:efgEntity");
			System.out.println("records.size(): " + records.getLength());
			XPath xpath = XPathFactory.newInstance().newXPath(); 
			XPathExpression expr = xpath.compile("record/efg:efgEntity");			 
			NodeList records2 = (NodeList) expr.evaluate(doc, XPathConstants.NODESET) ;
			System.out.println("records2.size(): " + records2.getLength());
			*/
			StringTokenizer st = new StringTokenizer(configuration.getRootPath(), "/") ;
			Element element = doc.getDocumentElement() ;
			NodeList elements = null ;
			
			while (st.hasMoreTokens()) {
				String path = st.nextToken().trim();
				elements = element.getElementsByTagName(path) ;
				element = (Element) elements.item(0) ;
			}
			
			int total = elements.getLength(), count = 0;
			int percent = 0;
			// System.out.println("__________") ;
			for (int iElement = 0; iElement < elements.getLength(); iElement++) {
				Node curNode = (Node) elements.item(iElement);
				if (curNode.getNodeType() == Node.ELEMENT_NODE) {
					Element curElement = (Element) curNode;
					ElementData record = new ElementData(this);
					readData(curElement, record, configuration.getRoot());

					addElement(record);	
					record.fileName = file.getAbsolutePath() ;
					//System.out.println("FILE: " + record.fileName) ;
					//System.out.println(record.toXML()) ;
				}
				count++;
				if ((percent + 1) * 10 < 100.0 * count / total + 5.0) {
					percent++;
					System.out.print("*");
				}
			}
		} catch (Exception e) {
			System.out.println("Exception: " + e);
		}
	}

	public void createFromFile(String fileName) {
		createFromFile(new File(fileName));
	}

	public void writeToFile(String fileName) {
		try {
			PrintWriter out = new PrintWriter(fileName);
			Collection<ElementData> collection = data.values();
			for (Iterator<ElementData> it = collection.iterator(); it.hasNext();) {
				out.println(it.next());
				out.println("$");
			}
			out.flush();
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}
	
	private void readData(Element element, ElementData record, ConfigTree tree) {
		String name = tree.name;
		if (name != null) {
			if (satisfy(element, tree.conditions)) {
				if (tree.isComplex()) {
					ElementData recordComplex = new ElementData(this) ;
					record.addData(name, recordComplex) ;
					record = recordComplex ;
				}
				else {
					record.addData(name, getValue(element, tree.path));
				}
			}
		}
		Collection<ConfigTree> children = tree.children.values();
		for (java.util.Iterator<ConfigTree> it = children.iterator(); it
				.hasNext();) {
			ConfigTree child = it.next();

			if (isAttribute(child)) {
				readData(element, record, child);
			} else {
				if (child.path.equals("/") || child.path.length() == 0) {
					readData(element, record, child) ;
				}
				else {
					NodeList nextElements = element.getElementsByTagName(child.path);
					for (int i = 0; i < nextElements.getLength(); i++) {
						Element nextElement = (Element) nextElements.item(i);
						if (nextElement.getParentNode() == element && satisfy(nextElement, child.conditions)) {
							readData(nextElement, record, child);
						}
					}
				}
			}
		}
	}

	private boolean satisfy(Element nextElement, Collection<Condition> conditions) {
		for (Iterator<Condition> it = conditions.iterator(); it.hasNext();) {
			Condition curCondition = (Condition) it.next();
			String fieldName = curCondition.fieldName.substring(1);
			String attr = nextElement.getAttribute(fieldName);
			if (!attr.equals(curCondition.fieldValue)) {
				return false;
			}
		}
		return true;
	}

	private boolean isAttribute(ConfigTree child) {
		return child.path.length() > 0 && child.path.charAt(0) == '@';
	}

	private String getValue(Element element, String path) {
		try {
			if (path.length() > 0 && path.charAt(0) == '@') {
				return element.getAttribute(path.substring(1));
			} else {
				return element.getChildNodes().item(0).getNodeValue();
			}
		} catch (Exception e) {
			return "";
		}
	}

	public void createFromFileXPATH(String fileName) {
		try {
			configuration = new Configuration();
			configuration
					.addField("uid", "ExtIDs/extid/text()", "integer", 0.0);
			configuration.addField("title", "IDTitel/text()", "string", 1.9,
					CompareUtilities.JARO_DISTANCE_REFERENCE
							| CompareUtilities.TITLE_DISTANCE_REFERENCE);
			configuration.addField("sortval", "IDTitel/@sortval", "string",
					3.5, CompareUtilities.EDIT_DISTANCE_REFERENCE);
			configuration.addField("year", "ProdJahr/text()", "string", 3.8,
					CompareUtilities.YEAR_DISTANCE_REFERENCE);
			configuration.addField("region",
					"Ursprungsland/Region/RegionCode/text()", "string", 0.7,
					CompareUtilities.JARO_DISTANCE_REFERENCE);

			confManifestation = new Configuration();
			confManifestation.addField("workRelation", "WerkRelation",
					"string", 0.6, CompareUtilities.JARO_DISTANCE_REFERENCE);
			confManifestation.addField("gaugeFormat", "Traegerformat",
					"string", 0.2, CompareUtilities.JARO_DISTANCE_REFERENCE);
			confManifestation.addField("aspectRatioFormat", "Bildformat",
					"string", 0.4, CompareUtilities.JARO_DISTANCE_REFERENCE);
			confManifestation.addField("sound", "Ton", "string", 1.5,
					CompareUtilities.JARO_DISTANCE_REFERENCE);
			confManifestation.addField("colour", "Farbe", "string", 2.5,
					CompareUtilities.JARO_DISTANCE_REFERENCE);
			confManifestation.addField("carrierType", "CarrierType??",
					"string", 0.7, CompareUtilities.JARO_DISTANCE_REFERENCE);
			confManifestation.addField("duration", "Dauer", "string", 8.6,
					CompareUtilities.JARO_DISTANCE_REFERENCE);
			confManifestation.addField("dimension", "Laenge", "string", 3.0,
					CompareUtilities.JARO_DISTANCE_REFERENCE);

			PrintWriter out = new PrintWriter("test.txt");

			DocumentBuilderFactory domFactory = DocumentBuilderFactory
					.newInstance();
			domFactory.setNamespaceAware(false);
			DocumentBuilder builder = domFactory.newDocumentBuilder();

			long costInit = 0, costCompile = 0, costEvaluate = 0, curTime;

			try {
				curTime = System.currentTimeMillis();
				Document doc = builder.parse(new File(fileName));
				doc.getDocumentElement().normalize();
				costInit += System.currentTimeMillis() - curTime;
				curTime = System.currentTimeMillis();
				XPath xpath = XPathFactory.newInstance().newXPath();

				XPathExpression expr = xpath.compile("/dif_export/Filmwerk");
				NodeList movies = (NodeList) expr.evaluate(doc,
						XPathConstants.NODESET);

				Collection<String> colNames = configuration.getField().keySet();
				XPathExpression[] exprFields = new XPathExpression[colNames
						.size()];
				int iField = 0;
				for (java.util.Iterator<String> itNames = colNames.iterator(); itNames
						.hasNext();) {
					String fieldName = itNames.next();
					Field field = configuration.getField().get(fieldName);
					exprFields[iField++] = xpath.compile(field.path);
				}
				costCompile += System.currentTimeMillis() - curTime;
				curTime = System.currentTimeMillis();
				int countEvaluate = 0;
				for (int iMovies = 0; iMovies < movies.getLength(); iMovies++) {
					Node curMovieNode = (Node) movies.item(iMovies);
					ElementData record = new ElementData(this);
					iField = 0;
					for (java.util.Iterator<String> itNames = colNames
							.iterator(); itNames.hasNext();) {
						String fieldName = itNames.next();
						countEvaluate++;
						String value = (String) exprFields[iField++].evaluate(
								curMovieNode, XPathConstants.STRING);
						record.addData(fieldName, value);
					}
					System.out.println("Adding: " + record.toXML()) ;
					addElement(record);
				}
				costEvaluate += System.currentTimeMillis() - curTime;
				curTime = System.currentTimeMillis();

				System.out.println("COST: " + costInit + ", " + costCompile
						+ ", " + costEvaluate + ", " + countEvaluate);
			} catch (Exception e) {
				System.out.println("Exception: " + e);
			}
		} catch (Exception e) {
			System.out.println("Exception: " + e);
		}
	}

	public Vector<DataPair> findDuplicates() {
		Object[] records = ((Collection<ElementData>) data.values()).toArray();
		Collection<Field> fields = configuration.getField().values();

		Vector<DataPair> duplicates = new Vector<DataPair>();
		HashMap<String, Boolean> control = new HashMap<String, Boolean>();
		int total = records.length;
		System.out.println("Number of records: " + records.length);
		Stack<String> path = new Stack<String>() ;
		
		try {
			PrintWriter out = new PrintWriter("distances.txt");
			for (int iRecord1 = 0 ; iRecord1 < records.length ; iRecord1 ++) {
				ElementData record1 = (ElementData) records [iRecord1] ;
				System.out.println(iRecord1 + "/" + records.length) ;
				for (int iRecord2 = iRecord1 + 1 ; iRecord2 < records.length ; iRecord2 ++) {
					ElementData record2 = (ElementData) records [iRecord2] ;
					double distance = record1.similarity(record2) ;
					if (distance > configuration.getThreshold()) {
						System.out.println("found " + distance) ;
						out.println("DISTANCE: " + distance) ;
						out.println("$") ;
						out.println("RECORD 1: ") ;
						out.println(record1) ;
						out.println("$") ;
						out.println("RECORD 2: ") ;
						out.println(record2) ;
						out.println("$") ;				
					}
					
				}
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		for (Field field : fields) {
			if (field.isSortable()) {
				int count = 0, percent = 0;
				sortingField = field.name;
				System.out.println("Sorting: " + sortingField);
				Arrays.sort(records);

				System.out.println("Finding duplicates...");
				System.out.println("__________");
				for (int iRecord = configuration.getDelta(); iRecord < records.length; iRecord++) {
					// if (iRecord % 100 == 0)
					// System.out.println("iRecord = " + iRecord + "/" +
					// records.length) ;
					ElementData curRecord = (ElementData) records[iRecord];
					for (int iPrevRecord = iRecord - configuration.getDelta(); iPrevRecord < iRecord; iPrevRecord++) {
						ElementData prevRecord = (ElementData) records[iPrevRecord];
						// System.out.println("Distance: " +
						// curRecord.distance(prevRecord)) ;

						double curDis = curRecord.similarity(prevRecord);
						if (curDis > configuration.getThreshold()) {
							if (!control.containsKey(prevRecord.id
									+ curRecord.id)) {
								control.put(prevRecord.id + curRecord.id,
										Boolean.TRUE);
								control.put(curRecord.id + prevRecord.id,
										Boolean.TRUE);
								DataPair pair = new DataPair(curDis, curRecord.data,
										prevRecord.data);
								duplicates.add(pair);
							}

							// if (duplicates.size() % 10 == 0)
							// System.out.println("pairs: " + duplicates.size())
							// ;
						}
					}
					count++;
					if ((percent + 1) * 10 < 100.0 * count / total + 5.0) {
						percent++;
						System.out.print("*");
					}
				}
				System.out.println();
			}
		}
		
		return duplicates;
	}

	/*
	public Vector<DataSerial> search(String searchField, String searchValue) {
		System.out.println("Searching for: " + searchValue + " in "
				+ searchField + " ...");
		Collection<ElementData> records = data.values();
		Vector<DataSerial> results = new Vector<DataSerial>();
		for (Iterator<ElementData> it = records.iterator(); it.hasNext();) {
			ElementData curData = it.next();
			if (((String) curData.data.get(searchField)).contains(searchValue)) {
				results.add(curData.toDataSerial());
			}
		}
		return results;
	}
	*/
	public Vector<DataSerial> search(String query) {
		System.out.println("Searching for: " + query + " ...");
		Collection<ElementData> records = data.values();
		Vector<DataSerial> results = new Vector<DataSerial>(1);
		// tring [] fields = query.split("$") ;
		StringTokenizer sq = new StringTokenizer(query, "" + Utilities.DELIMITER);
		String[] fields = new String[sq.countTokens()];
		for (int iField = 0; sq.hasMoreTokens(); iField++) {
			fields[iField] = sq.nextToken();
		}
		Vector<String> fieldName = new Vector<String>(1);
		Vector<String> relationship = new Vector<String>(1);
		Vector<String> fieldValue = new Vector<String>(1);

		for (int iField = 0; iField < fields.length; iField++) {
			String[] field = fields[iField].split(":");
			// String [] field = st.nextToken().split(":") ;
			String name = field[0].trim();
			if (!name.contains("relationship")) {
				String value = field[1].trim();
				fieldName.add(name);
				fieldValue.add(value.toUpperCase());
				boolean found = false;
				for (int iRel = 0; iRel < fields.length && !found; iRel++) {
					String[] rel = fields[iRel].split(":");
					String nameRel = rel[0].trim();
					if (nameRel.contains("relationship")) {
						String[] st = nameRel.split("-");
						if (st[1].trim().equals(name)) {
							found = true;
							relationship.add(rel[1].trim());
						}
					}
				}
				if (!found) {
					relationship.add(null);
				}
			}
		}
		// int size = records.size(), cur = 0 ;
		for (Iterator<ElementData> it = records.iterator(); it.hasNext();) {
			ElementData curData = it.next();
			// System.out.println((cur ++) + "/" + size) ;
			boolean found = false;
			for (Iterator<String> itField = fieldName.iterator(), itRel = relationship
					.iterator(), itValue = fieldValue.iterator(); itField
					.hasNext()
					&& !found;) {
				String valuePath = itField.next();
				String rel = itRel.next();
				String value = itValue.next();
				Object valueCol = curData.getValue(valuePath);
				if (valueCol == null) {
					continue;
				}
				Collection colValues = null ;
				if (valueCol instanceof String) {
					colValues = new Vector(1) ;
					colValues.add((String) valueCol) ;
				}
				if (valueCol instanceof Collection) {
					colValues = (Collection) valueCol ;
				}
				for (Iterator<String> itValueCol = colValues.iterator() ; itValueCol.hasNext() && !found ; ) {
					String dataValue = ((String) itValueCol.next()).toUpperCase() ;
					if (rel == null) {
						if (dataValue.equals(value)) {
							found = true;
						}
					} else {
						if (rel.equals("begins with")) {
							if (!dataValue.startsWith(value)) {
								found = true;
							}
						}
						if (rel.equals("contains")) {
							if (!dataValue.contains(value)) {
								found = true;
							}
						}
					}
				}
			}
			if (found) {
		//		System.out.println(curData.toDataSerial()) ;
				results.add(curData.toDataSerial());
			}
		}
		return results;
	}
	
	public Vector<DataSerial> search(SearchQuery query) {
		System.out.println("Searching for: " + query + " ...");
		Collection<ElementData> records = data.values();
		Vector<DataSerial> results = new Vector<DataSerial>(1);
		long time = 0, timeSatisfy = 0, timeToSerial = 0 ;
		for (Iterator<ElementData> it = records.iterator(); it.hasNext();) {
			ElementData curData = it.next();
			long cur1 = System.currentTimeMillis() ;
			if (curData.satisfy(query)) {
				long cur2 = System.currentTimeMillis() ;
				DataSerial ds = curData.toDataSerial(query.getResultFields()) ;
				results.add(ds);
				timeToSerial += System.currentTimeMillis() - cur2 ;
				//DataSerial ds = new DataSerial() ;
				//ds.put("title", "some title") ;
				//results.add(ds) ;
			}
			timeSatisfy += System.currentTimeMillis() - cur1 ;
		}
		System.out.println("timeSatify = " + (timeSatisfy - timeToSerial) + ", toSerial = " + timeToSerial) ;
		
		return results;
	}

	public DataSerial getItem(String itemID) {
		ElementData item = data.get(itemID) ;
		if (item != null) {
			return item.toDataSerial() ;
		}
		return null;
	}
}

/*
 * configuration = new Configuration("eac:eac") ; configuration.addField("id",
 * "@uid", "string", 0.0) ; configuration.addField("uid", "ExtIDs/extid/",
 * "integer", 0.0) ; configuration.addField("title", "IDTitel", "string", 1.9,
 * CompareUtilities.JARO_DISTANCE_REFERENCE |
 * CompareUtilities.TITLE_DISTANCE_REFERENCE) ;
 * configuration.addField("sortval", "IDTitel/@sortval", "string", 3.5,
 * CompareUtilities.EDIT_DISTANCE_REFERENCE) ; configuration.addField("year",
 * "ProdJahr/", "string", 3.8, CompareUtilities.YEAR_DISTANCE_REFERENCE) ;
 * configuration.addField("region", "Ursprungsland/Region/RegionCode/",
 * "string", 0.7, CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * configuration.compile() ;
 */
/*
 * configuration = new Configuration("Filmwerk") ; configuration.addField("id",
 * "@uid", "string", 0.0) ; configuration.addField("uid", "ExtIDs/extid/",
 * "integer", 0.0) ; configuration.addField("title", "IDTitel", "string", 1.9,
 * CompareUtilities.JARO_DISTANCE_REFERENCE |
 * CompareUtilities.TITLE_DISTANCE_REFERENCE) ;
 * configuration.addField("sortval", "IDTitel/@sortval", "string", 3.5,
 * CompareUtilities.EDIT_DISTANCE_REFERENCE) ; configuration.addField("year",
 * "ProdJahr/", "string", 3.8, CompareUtilities.YEAR_DISTANCE_REFERENCE) ;
 * configuration.addField("region", "Ursprungsland/Region/RegionCode/",
 * "string", 0.7, CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * configuration.compile() ;
 * 
 * confManifestation = new Configuration("FilmManifestation") ;
 * confManifestation.addField("workRelation", "WerkRelation", "string", 0.6,
 * CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * confManifestation.addField("gaugeFormat", "Traegerformat", "string", 0.2,
 * CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * confManifestation.addField("aspectRatioFormat", "Bildformat", "string", 0.4,
 * CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * confManifestation.addField("sound", "Ton", "string", 1.5,
 * CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * confManifestation.addField("colour", "Farbe", "string", 2.5,
 * CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * confManifestation.addField("duration", "Dauer", "string", 8.6,
 * CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 * confManifestation.addField("dimension", "Laenge", "string", 3.0,
 * CompareUtilities.JARO_DISTANCE_REFERENCE) ;
 */
