// Copyright 1996, Marimba Inc. All Rights Reserved.


// Confidential and Proprietary Information of Marimba, Inc.


// @(#)LinePool.java, 1.7, 12/15/96





package marimba.text;





import java.awt.*;


import java.io.*;





/**


 * A class which implements a line pool, which is a list of lines which


 * represent a the view of a portion of a text widget.


 *


 * @author	Jonathan Payne


 * @version 1.7, 12/15/96


 */


final class LinePool {


    /** Starting position of first line in pool. */


    int pos0;





    /** End position of last line in pool. */


    int pos1;





    /** Number of lines in pool. */


    int lineCount;





    /** The actual lines. */


    Line lines[] = new Line[10];





    /**


     * This is the index of the last line that was found by


     * lineContainingPos.  The theory is that subsequent requests are


     * usually for the next line.


     */


    int lastIndex = -1;





    void clear() {


	lines = new Line[10];


	lineCount = 0;


    }





    Line getLineAt(int index) {


	if (index >= lines.length) {


	    if (index > lines.length)


		throw new ArrayIndexOutOfBoundsException(index + " " + lines.length);


	    Line nl[] = new Line[index + 10];


	    System.arraycopy(lines, 0, nl, 0, lines.length);


	    lines = nl;


	}


	Line line = lines[index];


	if (line == null)


	    lines[index] = line = new Line();


	if (index >= lineCount)


	    lineCount = index + 1;


	return line;


    }





    void finishLines(int lineCount) {


	this.lineCount = lineCount;


	pos0 = lines[0].pos;


	pos1 = lines[lineCount - 1].pos1;


	int limit = lines.length;


	while (lineCount < limit && lines[lineCount] != null)


	    lines[lineCount++] = null;


	lastIndex = -1;


    }





    boolean posVisible(int pos, TextView view) {


	if (pos < pos0)


	    return false;


	if (lineCount > 1) {


	    Line l = lines[lineCount - 1];


	    return (pos < l.pos ||


		    (pos < l.pos1 &&


		     l.y + l.height < view.height - view.insets.bottom));


	}


	return pos < pos1;


    }





    int getPageSize(TextView view) {


	int pos1 = this.pos1;


	if (lineCount > 1) {


	    Line l = lines[lineCount - 1];


	    boolean v = l.y + l.height < view.height - view.insets.bottom;


	    pos1 = !v ? l.pos - 1 : l.posV;


	}


	return pos1 - pos0;


    }





    int lineIndexContainingPos(int pos) {


	if (pos < pos0 || pos > pos1)


	    return -1;





	int i;


	if (lastIndex != -1 && lastIndex < lineCount


	    && lines[lastIndex].pos < pos)


	    i = lastIndex;


	else


	    i = 0;


	int limit = lineCount;





	for (; i < limit; i++) {


	    Line line = lines[i];





	    if (line.pos > pos)


		break;


	    if (line.pos1 > pos)


		return lastIndex = i;


	}


	return -1;


    }





    Line lineContainingPos(int pos) {


	int index = lineIndexContainingPos(pos);


	if (index == -1)


	    return null;


	return lines[index];


    }





    Line findLineAt(int pos) {


	Line l = lineContainingPos(pos);


	return (l == null || l.modified || l.pos != pos) ? null : l;


    }





    Line lineContainingY(int y) {


	int limit = lineCount;


	if (y <= lines[0].y)


	    return lines[0];


	for (int i = limit; --i >= 0; ) {


	    Line line = lines[i];





	    if (line.y < y)


		return line;


	}


	return null;


    }





    /**


     * Updates the line structures so that the line which contains the


     * beginning of the insertion is marked as dirty.  Formatting that


     * begins there will wrap to following lines, if they are also


     * affected.  This also updates each line's "pos" to reflect the


     * insertion.


     */


    void notifyInsert(int pos, int length) {


	if (pos > pos1)


	    return;


	for (int i = lineCount; --i >= 0; ) {


	    Line line = lines[i];





	    if (line.posE < pos)


		break;	// no more lines are affected


	    if (line.pos <= pos)


		line.modified = true;


	    else


		line.pos += length;


	    line.posE += length;


	    line.pos1 += length;


	    line.posV += length;


	}


    }





    /**


     * Updates the line structures so that the line which are between


     * the start and end points of the deletion are marked as dirty.


     * All the lines' "pos" are updated to reflect the deletion.


     */


    void notifyDelete(int pos, int length) {


	if (pos > pos1)


	    return;





	int endpos = pos + length;





	for (int i = lineCount; --i >= 0; ) {


	    Line line = lines[i];





	    if (line.posE <= pos)


		break;	// no more lines are affected


	    if (line.pos >= endpos) {


		line.pos -= length;


		line.pos1 -= length;


		line.posV -= length;


		line.posE -= length;


	    } else {


		if ((line.posE -= length) < line.pos)


		    line.posE = line.pos;


		line.modified = true;


	    }


	}


    }





    public void dirtyRegion(int start, int end) {


	if (end < pos0 || start >= pos1)


	    return;


	int line0 = lineIndexContainingPos(start);


	int limit = lineCount;





	/*


	 * check to make sure the examined portion of the previous


	 * line isn't part of this region


	 */


	if (line0 > 0 && lines[line0 - 1].posE > start)


	    line0 -= 1;


	if (line0 < 0)


	    line0 = 0;


	while (line0 < limit) {


	    Line line = lines[line0++];





	    if (line.pos > end)


		break;


	    line.modified = true;


	}


    }


}


