/**
 * Class that represents Strings that can be efficiently spliced together
 * using structure sharing.
 *   @author Dave Reed
 *   @version 10/10/18
 */
public class SpliceString {
    private Node<String> front;
    private Node<String> back;
    private int numChars;
    
    /** 
     * Constructs an empty SpliceString object.
     */
    public SpliceString() {
        this.front = null;
        this.back = null;
        this.numChars = 0;
    }
    
    /**
     * Constructs a SpliceString object that stores a particular string value.
     *   @param str the value to be stored
     */
    public SpliceString(String str) {
        this.front = new Node<String>(str, null);
        this.back = this.front;
        this.numChars = str.length();
    }
    
    /**
     * Determines the length (number of characters) in this SpliceString.
     *   @return the number of characters in the represented string value
     */
    public int length() {
        return this.numChars;
    }
    
    /**
     * Determines if this SpliceString represents the empty string.
     *   @return true if the empty string is stored, else false
     */
    public boolean empty() {
        return this.length() == 0;
    }
    
    /**
     * Splices a string value to the end of this SpliceString object.
     *   @param str the string value to be spliced
     */
    public void splice(String str) {
        this.splice(new SpliceString(str));
    }
    
    /**
     * Splices another SpliceString object to the end of this one.
     *   @param sstr the SpliceString object to be spliced
     */
    public void splice(SpliceString sstr) {
        if (sstr.length() > 0) {
            if (this.empty())
                this.front = sstr.front;
            else {
                this.back.setNext(sstr.front);
            }
            this.back = sstr.back;
            this.numChars += sstr.length();
        }
    }
    
    /**
     * Returns the underlying string value being represented.
     *   @return the string value
     */    
    public String toString() {
        String str = "";
        Node<String> stepper = front;
        while (stepper != null) {
            str += stepper.getData();
            stepper = stepper.getNext();
        }
        return str;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    
    /** Inner class for representing a node in the SpliceString. */
    public class Node<E> {
        private E data;
        private Node<E> next;

        public Node(E data, Node<E> next) {
            this.data = data;
            this.next = next;
        }

        public E getData() {
            return this.data;
        }

        public Node<E> getNext() {
            return this.next;
        }

        public void setData(E newData) {
            this.data = newData;
        }

        public void setNext(Node<E> newNext) {
            this.next = newNext;
        }
    }
}
