package basics.collections.index;

import java.util.HashSet;
import java.util.Set;
import basics.testing.Test;
import basics.utl.SUtl;

/**
 * This constructs an index as a forest, each node is realized as a hash map where the key stores only one sign.
 * @author ks
 */
public class XHashedCharacterTree extends HashedCharacterTreeBase
{
class XHashNode extends HashNode<Set<Integer>>
{
/**
 * Root constructor
 */
XHashNode()
{
   super();
}

/**
 * Element constructor
 * @param c
 * @param i
 */
XHashNode(int i)
{
   newPosition(i);
}

public void newPosition(int i)
{
   if(value == null)
      value = new HashSet<Integer>(2, 1);
   value.add(i);
}

public boolean noPositions()
{
   return(value == null || value.size() == 0);
}

public String toString()
{
   return SUtl.sprint("(%)", value == null ? "NoPos" : SUtl.join(value));
}

boolean isMatch()
{
   return value != null;
}
}

private XHashNode root = new XHashNode();

HashNode<?> getRoot()
{
   return root;
}

Set<Integer> enshure(String term, int newpos)
{
   return enshure(newpos, root, term, 0, term.length());
}

Set<Integer> enshure(int newpos, XHashNode node, String T, int i, int x)
{
   int ipp = i + 1;
   String c = T.substring(i, ipp);
   XHashNode nextnode = null;
   if(node.next == null)
      node.createNext();
   else
      nextnode = (XHashNode)node.next.get(c);
   if(nextnode == null)
   {
      if(ipp == x)
      {
         nextnode = new XHashNode(newpos);
         node.next.put(c, nextnode);
         return nextnode.value;
      }
      else
      {
         nextnode = new XHashNode();
         node.next.put(c, nextnode);
         return enshure(newpos, nextnode, T, ipp, x);
      }
   }
   else
   {
      if(ipp == x)
      {
         nextnode.newPosition(newpos);
         return nextnode.value;
      }
      else
      {
         return enshure(newpos, nextnode, T, ipp, x);
      }
   }
}

/**
 * Retrieve index number of a term. The index number applies to an external list position.
 * @param term
 * @return 0...n if found, or -1 if not found.
 */
Set<Integer> retrieve(String term)
{
   return retrieve(root, term, 0, term.length());
}

private Set<Integer> retrieve(XHashNode node, String key, int i, int x)
{
   if(i == x)
      return node.value;
   int ipp = i + 1;
   if(node.next == null)
      return null;
   XHashNode nextset = (XHashNode)node.next.get(key.substring(i, ipp));
   if(nextset == null)
      return null;
   return retrieve(nextset, key, ipp, x);
}

/**
 * Remove a term, this only sets a flag.
 * @param key to be removed from the index.
 */
void remove(String key, int setpos)
{
   remove(root, key, 0, key.length(), setpos);
}

private void remove(XHashNode node, String key, int i, int x, int setpos)
{
   if(i == x)
      node.value.remove(setpos);
   else
   {
      int ipp = i + 1;
      if(node.next != null)
      {
         XHashNode nextset = (XHashNode)node.next.get(key.substring(i, ipp));
         if(nextset != null)
            remove(nextset, key, ipp, x, setpos);
      }
   }
}

void retrieveFuzzy(Collector results, HashNode<?> _node, String term, int i, int size, int limit,
   int limit0, int r)
{
   XHashNode node = (XHashNode)_node;
   int k;
   XHashNode nextnode = null;
   if(!deadNode(node))
   {
      if(matching(term, i, size, limit, node, r))
         results.insertMatches(node.value, matchRatio(term, limit0, limit, size, r));
      if(limit > 0)
      {
         if((nextnode = (XHashNode)nextNode(term, i, size, node)) != null)
            retrieveFuzzy(results, nextnode, term, i + 1, size, limit, limit0, r + 1);
         else
            for(int tolerance = 0; tolerance < 2 * limit; tolerance++)
               if((k = 1 + i + flipflop(tolerance)) >= 0)
                  if(retrieveFuzzySet(results, node, term, k, size, limit - 1, limit0, r))
                     ;
         //                     break;
      }
   }
}

public static void unittest()
{
   XHashedCharacterTree ht = new XHashedCharacterTree();
   String a = "abcd";
   String b = "xyz";
   String c = "hallo";
   String d = "hall";
   ht.enshure(a, 0);
   ht.enshure(b, 0);
   ht.enshure(b, 1);
   ht.enshure(c, 0);
   ht.enshure(c, 1);
   ht.enshure(c, 2);
   Test.assertTrue(ht.retrieve(a).size() == 1, a + " has 1 elements not " + ht.retrieve(a).size());
   Test.assertTrue(ht.retrieve(b).size() == 2, b + " has 2 elements not " + ht.retrieve(b).size());
   Test.assertTrue(ht.retrieve(c).size() == 3, c + " has 3 elements not " + ht.retrieve(c).size());
   ht.remove(c, 2);
   Test.assertTrue(ht.retrieve(c).size() == 2, c + " has 2 elements after del not "
      + ht.retrieve(c).size());
   ht.enshure(c, 3);
   Test.assertTrue(ht.retrieve(c).size() == 3, c + " has 3 elements not " + ht.retrieve(c).size());
}
}
