package commons.collections.trie;


/*
 * Author: kst
 */
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;



public class Node implements INode, Serializable
{

   public char    CHAR;

   private Node   parent   = null;

   Node[]         subTrees = new Node[0]; // transitions

   public String  output   = "";

   public boolean isFinal  = false;      // true if node marks word end

   static Class   type;                  // singelton, needed for technical

   static int     depth    = 0;          //nur anzeige

   static int     maxdepth = 0;          //nur anzeige

   static int     maxwidth = 0;

   static long    lexsize  = 0;

   static long    rawsize  = 0;

   static long    states   = 0;


   //root constructor
   public Node ()
   {
      //type of array stored for better performance:
      type = ((Object) subTrees).getClass().getComponentType();
   }


   //child constructor
   private Node (Node parent)
   {
      this();
      this.parent = parent;
   }


   public char getChar ()
   {
      return CHAR;
   }


   public INode[] getSubtrees ()
   {
      return (INode[]) subTrees;
   }


   /**
    * adding substring of words
    */
   public void addCurrentSuffix (int offset, String word, int max, String replacement)
   {
      if (maxdepth < depth)
      {
         maxdepth = depth;
      }

      //iif word end reached: this node is a final state node.
      if (offset == max)
      {
         if (isFinal == false)
         {
            output = replacement;
            isFinal = true;
         }
      }
      else
      //continue traversing the tree...
      {
         char c = word.charAt(offset);

         //debugging: print word, commonprefix, currentsuffix, lastchild:
         //         System.out.print(new StateHelper(this,
         // word.substring(offset)).toString());
         //         Node lastChild = (Node)(new StateChildHelper (this).getLastChild());
         //         System.out.println (" lastChild: [" + lastChild.CHAR + "]");

         Node lastState = this;
         Node nextNode = (Node) findMatchingNode(c);

         if (nextNode == null) //reaching the new path
         {
            //'this' is the CommonPrefix now, it is becoming parent of the new
            // path:
            nextNode = addNode(new Node(lastState));
            nextNode.CHAR = c;
         }

         depth++;
         nextNode.addCurrentSuffix(offset + 1, word, max, replacement);
         depth--;
      }
   }


   /**
    * matching an substring of a word
    */
   public String matchSubWord (int offset, String subWord, int max)
   {
      //if word end reached: word matches if this is a final node.
      if (offset == max)
      {
         return isFinal ? this.output : null;
      }
      else
      //continue traversing the tree...
      {
         char c = subWord.charAt(offset);

         Node entryPoint = (Node) findMatchingNode(c);

         if (entryPoint == null) //not matching
         {
            return null;
         }
         return entryPoint.matchSubWord(offset + 1, subWord, max);
      }
   }


   public INode findMatchingNode (char CHAR)
   {
      if (maxwidth < subTrees.length)
      {
         maxwidth = subTrees.length;
      }

      for (int i = 0; i < subTrees.length; i++)
      {
         Node trie = (Node) subTrees[i];
         if (trie.CHAR == CHAR)
         {
            return (INode) trie;
         }
      }

      return (INode) null;
   }


   public Node addNode (Node newNode)
   {
      int x = subTrees.length;

      Object target = Array.newInstance(type, x + 1);
      System.arraycopy(subTrees, 0, target, 0, x);

      subTrees = (Node[]) target;

      subTrees[x] = newNode;

      states++;

      return newNode;
   }


   public boolean hasChildren ()
   {
      return (subTrees != null && subTrees.length > 0);
   }


   public INode getRoot ()
   {
      return (parent == null) ? (INode) this : parent.getRoot();
   }


   public String hash ()
   {
      return null;
   }


   public INode getParent ()
   {
      return parent;
   }

   public String toString ()
   {
      StringBuffer s = new StringBuffer();
      if (isFinal)
      {
         s.append(output);
      }
      else
      {
         s.append(CHAR);
//         s.append(countChildren());
      }
      return s.toString();
   }


   private void writeObject (ObjectOutputStream out) throws IOException
   {
      out.writeChar(CHAR);
      out.writeBoolean(isFinal);
      out.writeInt(subTrees.length);
      out.writeObject(output);

      for (int i = 0; i < subTrees.length; i++)
      {
         Node n = (Node) subTrees[i];
         n.writeObject(out);
      }
   }


   private void readObject (ObjectInputStream in) throws IOException,
         ClassNotFoundException
   {
      CHAR = in.readChar();
      isFinal = in.readBoolean();
      int x = in.readInt();
      output = (String)in.readObject();
      
      subTrees = new Node[x];

      for (int i = 0; i < x; i++)
      {
         Node n = new Node();
         n.readObject(in);
         n.parent = this;
         subTrees[i] = n;
      }
   }

}