package cnp.ew.richtext;

import java.util.*;
import cnp.ew.util.*;

public class CpRunList
{
    Vector runs = new Vector();

    public void setRuns(Vector newRuns)
    {
        runs = newRuns;
    }

    public CpRunList getCopy()
    {
        Vector copy = new Vector();
        for (int i = 0; i < runs.size(); i++) {
            CpRun run = elementAt(i);
            copy.addElement(run.getCopy());
        }
        CpRunList newRunList = new CpRunList();
        newRunList.setRuns(copy);
        return newRunList;
    }

    public String toString()
    {
        StringBuffer buf = new StringBuffer();
    	buf.append("{");
    	for (int i = 0; i < size(); i++) {
    	    CpRun run = elementAt(i);
    	 //   System.out.println("Appending run " + i);
    	    buf.append(run.toString());
    	    if (i + 1 < size()) {
    		    buf.append(" ");
    	    }
    	}
        buf.append("}");
        return buf.toString();
    }

    int lastOffset()
    {
        CpRun lastRun = elementAt(size() - 1);
        return lastRun.offset + lastRun.length - 1;
    }

    int split(int offset)
    {
//        System.out.println("Splitting offset: " + offset);
        if (offset > lastOffset()) {
            return runs.size();
        }

        CpTextLocation indexAndOffset = indexAndOffsetForOffset(offset);
        int startOffset = indexAndOffset.offset();
        int runIndex = indexAndOffset.index();


        if (startOffset != offset) {
            int lengthOfNewRun = startOffset + elementAt(runIndex).length - offset;
            elementAt(runIndex).length -= lengthOfNewRun;
            runs.insertElementAt(new CpRun(offset, lengthOfNewRun, ((CpCopyable)elementAt(runIndex).object).getCopy()), runIndex + 1);
//            System.out.println("  Splitting offset " + offset + ", got runs " + elementAt(runIndex).toString() + " and " + elementAt(runIndex + 1).toString());
            return runIndex + 1;
        }
        return runIndex;
    }

    public void insertElementAt(CpRun run, int index)
    {
        runs.insertElementAt(run, index);
    }

    void deleteIndexes(int fromIndex, int toIndex)
    {
     //   System.out.println("  Deleting indexes from " + fromIndex + " to " + toIndex);
        for (int i = fromIndex; i <= toIndex; i++) {
            removeElementAt(fromIndex);
        }
    }


    // In these next three methods, the "to" is non-inclusive.  so from = 2 to to = 4 would effect 2 characters.
    // This jibes with the selection start and end in textpane

    public void delete(int from, int to)
    {
       // System.out.println("Run list delete: ");
        int splitFrom = split(from);
        int splitTo = split(to);
        for (int i = splitTo; i < runs.size(); i++) {
            elementAt(i).offset -= (to - from);
        }
      //  System.out.println("In del: " + from + " to " + to + ", dusting indexes = " + splitFrom + " to " + (splitTo - 1));
      //  System.out.println(" of formats " + this);
        deleteIndexes(splitFrom, splitTo - 1);
    }

    public void addFormat(int from, int to, CpCharFormat format)
    {
        int splitFrom = split(from);
        int splitTo = split(to);
        deleteIndexes(splitFrom, splitTo - 1);
        runs.insertElementAt(new CpRun(from, to - from, format), splitFrom);
    }

    public void mergeFormat(int from, int to, CpCharFormat format)
    {
        int splitFrom = split(from);
        int splitTo = split(to);
        for (int i = splitFrom; i <= splitTo - 1; i++) {
            CpRun run = elementAt(i);
            run.object = ((CpCharFormat)run.object).merge(format);
        }
    }


/*    public CpTextLocation indexAndOffsetForOffset(int offset)
    {
        // Lets try a linear lookup.  Later, we can store run offsets and use a binary lookup

        int i = 0;
        CpRun run = null;
        for (i = 0; i < runs.size(); i++) {
            run = elementAt(i);
            if (offset < run.offset) {
                return new CpTextLocation(i, run.offset);
            }
        }
        if (run == null) {
            return new CpTextLocation(0, 0);
        } else {
            return new CpTextLocation(i, run.offset + run.length);
        }
    }

  */  public int newOffsetForIndex(int index)
    {
        if (index >= runs.size()) {
            return lastOffset() + 1;
        }
        return elementAt(index).offset;

    }
/*
    public int lengthOfIndex(int index)
    {
        return elementAt(index).length;
    }
    */
    public CpTextLocation indexAndOffsetForOffset(int offset)
    {
        // Lets try a linear lookup.  Later, we can store run offsets and use a binary lookup

        int curOffset = 0;
        int index = 0;
        for (int i = 0; i < runs.size(); i++) {
            CpRun run = (CpRun)runs.elementAt(i);
            curOffset += run.length;
            if (offset < curOffset) {
                return new CpTextLocation(i, curOffset - run.length);
            }
            index = i;
        }
        return new CpTextLocation(index, curOffset);
    }

    public int offsetForIndex(int index)
    {
        int offset = 0;
        for (int i = 0; i < index; i++) {
            CpRun run = (CpRun)runs.elementAt(i);
            offset += run.length;
        }
        if (offset != newOffsetForIndex(index)) {
          //  System.out.println("Offset for Index = " + offset + " newOffsetForIndex = " + newOffsetForIndex(index));
        }
        return offset;
    }

    public int lengthOfIndex(int index)
    {
        return ((CpRun)runs.elementAt(index)).length;
    }

    public CpRun runForOffset(int offset)
    {
        return elementAt(indexForOffset(offset));
    }

    public CpRun elementAt(int i)
    {
        return (CpRun)runs.elementAt(i);
    }

    public CpRun getRun(int i)
    {
        // HEY!  USE THIS INSTEAD OF ELEMENTAT, more descriptive
        return (CpRun)runs.elementAt(i);
    }

    public int size()
    {
        return runs.size();
    }

    public void addElement(CpRun newRun)
    {
        runs.addElement(newRun);
    }

    public void removeElementAt(int i)
    {
        runs.removeElementAt(i);
    }

    public void setElementAt(CpRun o, int i)
    {
        runs.setElementAt(o, i);
    }

    public Object objectAt(int i)
    {
        return elementAt(i).object;
    }

    public int offsetOfRunContainingOffset(int offset)
    {
        return indexAndOffsetForOffset(offset).offset();
    }

    public int indexForOffset(int offset)
    {
        return indexAndOffsetForOffset(offset).index();
    }
}
