/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: RadixTrie.java
 *
 * Copyright (c) 2010, Static Free Software. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.database.geometry.btree;

import java.io.*;
import java.util.*;

/**
 *  A <a href=http://en.wikipedia.org/wiki/Radix_trie>Radix Trie</a>;
 *  used to store lists of hierarchically-named signals and
 *  efficiently detect the separator character.
 *
 *  A RadixTrie consists of a tree of nodes; each path from the root
 *  to a node with entry!=null constitutes an entry.  The
 *  concatenation of all TreeMap keys along the path is the key and
 *  the entry value is the entry.  So you might think of a RadixTrie
 *  as a Map<String,V> which has excellent space efficiency when the
 *  keys share large prefixes.
 *
 *  An additional feature in this implementation (not normally
 *  included in RadixTries) is the tracking of each character's
 *  "score".  The score of a branch is defined to be the number of
 *  descendents it has with entry!=null.  The score of a node is the
 *  *second largest* score among its branches.  The score of a
 *  character "c" is the sum of the scores of all nodes "n" such that
 *  the last charachter of its key (in its parent's TreeMap) is "c".
 *  The character with the highest score is a very good guess at the
 *  "hierarchy separator"; it is the character which is most likely to
 *  terminate the longest common prefix of a family of strings.
 *
 *  @author Adam Megacz
 */
public class RadixTrie<V> {

    // the root node
    private final Node root = new Node((char)0, null);

    private final int[] scores = new int[65535];

    private class Node {

        private char firstChar;
        private V entry;

        private TreeMap<String,Node> children = null;

        public Node(char firstChar, V value) {
            this.firstChar = firstChar;
            this.entry = value;
        }

        private String getPrev(String key) {
            if (children==null) return null;
            SortedMap<String,Node> prevMap = children.headMap(key);
            return prevMap.size()==0 ? null : prevMap.lastKey();
        }

        public void put(String key, V value) {
            if (key.length()!=0 && children==null) children = new TreeMap<String,Node>();

            // prev is the key right before the "insertion point"
            String prev = getPrev(key);

            if (key.length()==0) {
                this.entry = value;

            } else if (children.containsKey(key)) {
                children.get(key).put("", value);

            } else if (prev!=null && key.startsWith(prev)) {
                // if prev is a prefix of the key we're inserting, we can just walk down that branch
                children.get(prev).put(key.substring(prev.length()), value);
                    
            } else if (prev==null || prev.charAt(0) != key.charAt(0)) {
                // if prev shares not even a single character, we need to make a new branch
                if (children==null) children = new TreeMap<String,Node>();
                children.put(key, new Node(key.charAt(0), value));
                scores[firstChar & 0xffff]++;

            } else {
                // prev shares more than one but less than all characters; need to split a branch
                int i=0;
                for(i=0; i<Math.min(key.length(), prev.length()); i++)
                    if (key.charAt(i)!=prev.charAt(i))
                        break;

                Node oldnode = children.get(prev);
                children.remove(prev);
                scores[firstChar & 0xffff]--;

                Node newnode = new Node(key.charAt(i-1), null);
                children.put(key.substring(0, i), newnode);
                scores[firstChar & 0xffff]++;

                if (newnode.children==null) newnode.children = new TreeMap<String,Node>();
                newnode.children.put(key.substring(i), new Node(key.charAt(key.length()-1), value));
                scores[newnode.firstChar & 0xffff]++;
                newnode.children.put(prev.substring(i), oldnode);
                scores[newnode.firstChar & 0xffff]++;
            }
        }
//        private int size() { return children==null ? 0 : children.size(); }
        public V get(String key) {
            if (key.length()==0) return entry;
            if (children==null) return null;
            if (children.containsKey(key)) return children.get(key).get("");
            SortedMap<String,Node> prevMap = children.headMap(key);
            String prev = prevMap.size()==0 ? null : prevMap.lastKey();
            if (prev!=null && key.startsWith(prev)) return children.get(prev).get(key.substring(prev.length()));
            return null;
        }

    }


    public char guessHierarchySeparator() {
        int best = 0;
        char ret = 0;
        for(int i=0; i<scores.length; i++) {
            if (scores[i] > best) {
                best = scores[i];
                ret = (char)i;
            }
        }
        return ret;
    }

    public V    get(String key) {
        return root.get(key);
    }

    public void put(String key, V value) {
        System.out.println("put \""+key+"\"");
        if (value==null) throw new RuntimeException("deletions not yet implemented");
        root.put(key, value);
    }


}
