package basics.collections.index;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import basics.collections.index.HashedCharacterTree.IHashNode;
import basics.testing.Test;
import basics.utl.SUtl;

/**
 * Provides an index an a list of features.
 * @author ks
 *
 * @param <D>
 */
public class IndexList<T extends Indexable>
{
List<T>                       indexedList = new ArrayList<T>();
protected HashedCharacterTree pkIndex     = new HashedCharacterTree();

public int size()
{
   return indexedList.size();
}

public void applyFunctor(IndexableFunctor<T> functor)
{
   for(int i = 0; i < indexedList.size(); i++)
      indexedList.set(i, functor.apply(indexedList.get(i)));
}

/**
 * Check trie if key exists.
 * @param key key to search
 * @return true if found
 */
public boolean contains(String key)
{
   return pkIndex.retrieve(key) > -1;
}

/**
 * Check trie if key exists, and return the index position stored with the node.
 * @param key key to search
 * @return -1 if not found, or a index position for retrieving the associated datarow.
 */
public synchronized int indexOf(String key)
{
   return pkIndex.retrieve(key);
}

public synchronized void remove(String key)
{
   int x = pkIndex.retrieve(key);
   if(x > -1)
   {
      pkIndex.remove(key);
      indexedList.remove(x);
   }
}

/**
 * Add a T to index and to list. But if the T turns out to be in the index,
 * do not add it to the list! Return the correct index position of the T anyway.
 * @param indexable to be added if not in the index already.
 * @return position of the element.
 */
public synchronized int add(T indexable)
{
   int freepos = indexedList.size();
   int x = pkIndex.enshure(indexable._key, freepos);
   //   if(x != freepos)
   //      throw new Failure("Duplicate key: " + indexable._key);
   if(indexable._index != x)
      indexable._index = x;
   indexedList.add(indexable);
   return x;
}

public T get(String key)
{
   int x = indexOf(key);
   return get(x);
}

public T get(int x)
{
   return x > -1 && x < indexedList.size() ? indexedList.get(x) : null;
}

public Iterator<T> iterator()
{
   return indexedList.iterator();
}

public synchronized Iterator<String> keyIterator()
{
   final Iterator<T> iter = iterator();
   return new Iterator<String>()
   {
      public boolean hasNext()
      {
         return iter.hasNext();
      }

      public String next()
      {
         return iter.next()._key;
      }

      public void remove()
      {
      }
   };
}

public List<T> copyList()
{
   List<T> newlist = new ArrayList<T>();
   for(T xd : indexedList)
      newlist.add(xd);
   return newlist;
}

public List<Match<T>> retrieveFuzzy(String key, double likeness)
{
   Collector resultsCollector = new Collector();
   pkIndex.retrieveFuzzy(resultsCollector, (IHashNode)pkIndex.getRoot(), key, likeness);
   Map<Integer, Match<Integer>> rawmap = resultsCollector.resultset;
   List<Match<T>> results = new ArrayList<Match<T>>();
   for(Iterator<Integer> iter = rawmap.keySet().iterator(); iter.hasNext();)
   {
      int i = iter.next();
      Match<Integer> m = rawmap.get(i);
      T t = get(m.content);
      if(likeness <= m.quality)
      {
         Match<T> mw = new Match<T>(t, m.quality);
         results.add(mw);
      }
   }
   Collections.sort(results, new Comparator<Match<T>>()
   {
      public int compare(Match<T> mw1, Match<T> mw2)
      {
         Double v1 = mw1.quality;
         Double v2 = mw2.quality;
         return v2.compareTo(v1);
      }
   });
   return results;
}

public static void main(String [] args)
{
   IndexList.unittest();
}

public static void unittest()
{
   Indexable fd = null;
   IndexList<MultiLang> f = new IndexList<MultiLang>();
   f.add(new MultiLang("hallo", SUtl.split("de")));
   f.add(new MultiLang("halli", SUtl.split("de")));
   f.add(new MultiLang("galli", SUtl.split("de")));
   f.add(new MultiLang("gallie", SUtl.split("de")));
   f.add(new MultiLang("gallien", SUtl.split("de")));
   f.add(new MultiLang("halllo", SUtl.split("en")));
   f.add(new MultiLang("haus", SUtl.split("de")));
   f.add(new MultiLang("raus", SUtl.split("de")));
   f.add(new MultiLang("house", SUtl.split("en")));
   fd = f.get("hallo");
   Test.assertNotNull(fd, "missing hallo");
   Test.printout("hallo: % % %", fd._index, fd._key, fd.toString());
   fd = f.get("haus");
   Test.assertNotNull(fd, "missing haus");
   Test.printout("haus: % % %", fd._index, fd._key, fd.toString());
   fd = f.get("house");
   Test.assertNotNull(fd, "missing house");
   Test.printout("house: % % %", fd._index, fd._key, fd.toString());
   f.applyFunctor(new IndexableFunctor<MultiLang>()
   {
      public MultiLang apply(MultiLang fd)
      {
         fd.add("gr");
         return fd;
      }
   });
   fd = f.get("hallo");
   Test.assertNotNull(fd, "missing hallo");
   Test.printout("hallo: % % %", fd._index, fd._key, fd.toString());
   fd = f.get("haus");
   Test.assertNotNull(fd, "missing haus");
   Test.printout("haus: % % %", fd._index, fd._key, fd.toString());
   fd = f.get("house");
   Test.assertNotNull(fd, "missing house");
   Test.printout("house: % % %", fd._index, fd._key, fd.toString());
   String term = "Aallo";
   double likeness = 0.5;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
   term = "maus";
   likeness = 0.6;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("1 FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
   term = "aus";
   likeness = 0.5;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("2 FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
   term = "fraus";
   likeness = 0.5;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("3 FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
   term = "fraks";
   likeness = 0.5;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("4 FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
   term = "haKs";
   likeness = 0.5;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("5 FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
   term = "mauser";
   likeness = 0.2;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("6 FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
   term = "mause";
   likeness = 0.6;
   for(Match<MultiLang> mml : f.retrieveFuzzy(term, likeness))
      Test.printout("7 FUZZY get(% -> %): % %", term, likeness, mml.content, mml.quality);
}
}
