/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.kahadb.util;

import java.util.ArrayList;

/**
 * Provides a list of LinkedNode objects. 
 * 
 * @author chirino
 */
public class LinkedNodeList<T extends LinkedNode<T>> {

    T head;
    int size;

    public LinkedNodeList() {
    }

    public boolean isEmpty() {
        return head == null;
    }

    public void addLast(T node) {
        node.linkToTail(this);
    }

    public void addFirst(T node) {
        node.linkToHead(this);
    }

    public T getHead() {
        return head;
    }

    public T getTail() {
        return head != null ? head.prev : null;
    }
    
    public void clear() {
        while (head != null) {
            head.unlink();
        }
    }

    public void addLast(LinkedNodeList<T> list) {
        if (list.isEmpty()) {
            return;
        }
        if (head == null) {
            head = list.head;
            reparent(list);
        } else {
            getTail().linkAfter(list);
        }
    }

    public void addFirst(LinkedNodeList<T> list) {
        if (list.isEmpty()) {
            return;
        }
        if (head == null) {
            reparent(list);
            head = list.head;
            list.head = null;
        } else {
            getHead().linkBefore(list);
        }
    }

    public T reparent(LinkedNodeList<T> list) {
        size += list.size;
        T n = list.head;
        do {
            n.list = this;
            n = n.next;
        } while (n != list.head);
        list.head = null;
        list.size = 0;
        return n;
    }

    /**
     * Move the head to the tail and returns the new head node.
     * 
     * @return
     */
    public T rotate() {
    	if( head ==null )
    		return null;
        return head = head.getNextCircular();
    }

    /**
     * Move the head to the tail and returns the new head node.
     * 
     * @return
     */
    public void rotateTo(T head) {
    	assert head!=null: "Cannot rotate to a null head";
    	assert head.list == this : "Cannot rotate to a node not linked to this list";
        this.head = head;
    }

    public int size() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        boolean first=true;
        T cur = getHead();
        while( cur!=null ) {
            if( !first ) {
                sb.append(", ");
            }
            sb.append(cur);
            first=false;
            cur = cur.getNext();
        }
        sb.append("]");
        return sb.toString();
    }
    
    /**
     * Copies the nodes of the LinkedNodeList to an ArrayList.
     * @return
     */
    public ArrayList<T> toArrayList() {
    	ArrayList<T> rc = new ArrayList<T>(size);
    	T cur = head;
    	while( cur!=null ) {
    		rc.add(cur);
    		cur = cur.getNext();
    	}
    	return rc;
    }
}
