package cnp.ew.richtext;

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

import cnp.ew.util.*;
import cnp.ew.displayer.*;

public class CpParagraph extends CpDefaultObservable
{
    public static final int LEFT = CpAlignable.ALIGN_LEFT;
    public static final int RIGHT = CpAlignable.ALIGN_RIGHT;
    public static final int CENTER = CpAlignable.ALIGN_CENTER;
    public static final int FULL = CpAlignable.ALIGN_JUSTIFIED;

    static CpCharFormat normalFormat = new CpCharFormat();
    static CpCharFormat keywordFormat = CpCharFormat.getBold();
    static CpCharFormat commentFormat = new CpCharFormat().getItalic();
    static CpCharFormat literalFormat = new CpCharFormat();

    static String startCommentString = "" + '/' + '*';
    static String endCommentString = "" + '*' + '/';
    static String lineCommentString = "" + '/' + '/';
    static String quoteString = "" + '"';

    static Hashtable keywords;

    static {
        normalFormat.setFontName("Courier");
        keywordFormat.setFontName("Courier");
        commentFormat.setFontName("Courier");
        literalFormat.setFontName("Courier");
        commentFormat.setColor(new Color(0, 0, 128));
        literalFormat.setColor(new Color(0, 128, 0));
        keywords = new Hashtable();
        keywords.put("abstract", "");
        keywords.put("int", "");
        keywords.put("import", "");
        keywords.put("final", "");
        keywords.put("boolean", "");
        keywords.put("interface", "");
        keywords.put("break", "");
        keywords.put("long", "");
        keywords.put("byte", "");
        keywords.put("native", "");
        keywords.put("case", "");
        keywords.put("new", "");
        keywords.put("null", "");
        keywords.put("catch", "");
        keywords.put("char", "");
        keywords.put("class", "");
        keywords.put("package", "");
        keywords.put("const", "");
        keywords.put("private", "");
        keywords.put("continue", "");
        keywords.put("protected", "");
        keywords.put("default", "");
        keywords.put("public", "");
        keywords.put("do", "");
        keywords.put("double", "");
        keywords.put("return", "");
        keywords.put("else", "");
        keywords.put("short", "");
        keywords.put("extends", "");
        keywords.put("static", "");
        keywords.put("void", "");
        keywords.put("false", "");
        keywords.put("true", "");
        keywords.put("while", "");
        keywords.put("for", "");
        keywords.put("String", "");
        keywords.put("if", "");
        keywords.put("else", "");
        keywords.put("switch", "");
        keywords.put("try", "");
    }

    static Color bulletColor = Color.black;
    static int bulletExtent = 4;

    int horizontalAlignment;
    int leftMargin;
    int rightMargin;
    int firstIndent;

    CpTextModel text;
    CpRunList formats;
    CpRunList lines;

    int height;

    // These probably shouldn't be in paragraph (copied from textpane)
    int pageLeft;
    int pageRight;

    boolean bulletMode = false;

    boolean isMultiLineJava = false; // Set if this is a continuation of a multi line java construct (comment, string)

    public CpParagraph()
    {
        super();
    }

    public CpParagraph(String text)
    {
        super();
        setText(text);
    }

    public CpParagraph getCopy()
    {
        CpParagraph copy = new CpParagraph();
        copy.setHorizontalAlignment(getHorizontalAlignment());
        copy.setMargins(leftMargin, rightMargin, firstIndent);
        copy.setTextModel(text.getCopy());
        if (lines != null) { // has not been wrapped yet
            copy.setLines(lines.getCopy());
        }
        copy.setFormats(formats.getCopy());
        copy.setExtents(height, pageLeft, pageRight);
        copy.setBulletMode(bulletMode);
        return copy;
    }

    public void setBulletMode(boolean newMode)
    {
        bulletMode = newMode;
    }

    public boolean getBulletMode()
    {
        return bulletMode;
    }

    /********************** JAVA PARSING ***************************/

    public boolean getIsMultiLineJava()
    {
        return isMultiLineJava;
    }

    void addNormal(CpRunList formats, CpRun token, int start, CpCharFormat format)
    {
        if (token.offset - start > 0) {
            formats.addElement(new CpRun(start, token.offset - start, format));
        }
    }

    int lookFor(CpRunList formats, Vector tokens, CpRun token, int curToken, CpCharFormat format, String lookFor)
    {
        CpRun nextToken = null;
        String s;
        do {
            if (curToken == tokens.size() - 1) {
                break;
            }
            curToken++;
            nextToken = (CpRun)tokens.elementAt(curToken);
            s = (String)nextToken.object;
        } while (!s.equals(lookFor));
        if (nextToken != null) {
            formats.addElement(new CpRun(token.offset, nextToken.offset + nextToken.length - token.offset, format));
        } else {
            // Stab in the dark...
            formats.addElement(new CpRun(token.offset, token.length, format));
        }
        return curToken;
    }

    void parseJava(boolean isMultiLine)
    {
    //    printDataStructures();
        // isMultiLine is true if the PREVIOUS paragraph ended in the middle of a multiline token (comment, string)

        isMultiLineJava = false;
        formats = new CpRunList();
        int curOffset = 0;
        int startOfNormal = 0;

        Vector tokens = new Vector();
        do {
            curOffset = text.getToken(curOffset, tokens);
        } while (curOffset < text.length());

        if (tokens.isEmpty()) { // A blank line
            if (isMultiLine) {
                formats.addElement(new CpRun(0, text.length(), commentFormat));
                isMultiLineJava = true;
            } else {
                formats.addElement(new CpRun(0, text.length(), normalFormat));
            }
            return;
        }

       // System.out.println("Got tokens:..." );
      //  for (int i = 0; i < tokens.size(); i++) {
      //      CpRun token = (CpRun)tokens.elementAt(i);
      //      System.out.print("." + (String)token.object + ".(" + token.offset + "," + token.length + ")");
      //  }

        int curToken = 0;
        if (isMultiLine) {
            CpRun token = (CpRun)tokens.elementAt(curToken);
            addNormal(formats, token, startOfNormal, normalFormat);
            curToken = lookFor(formats, tokens, token, curToken, commentFormat, endCommentString);
//            System.out.println("Cur token = " + curToken + " tokens.size = " + tokens.size());
  //          System.out.println(" stringn = " + (String)((CpRun)tokens.elementAt(curToken)).object);
            if (curToken == tokens.size() - 1 && !((String)((CpRun)tokens.elementAt(curToken)).object).equals(endCommentString)) {
                // Ted just commented out 10/2
                formats.addElement(new CpRun(startOfNormal, text.length() - startOfNormal, normalFormat));
                isMultiLineJava = true;
                return;
            }
        }

        isMultiLine = false;
        while (curToken < tokens.size()) {
            CpRun token = (CpRun)tokens.elementAt(curToken);
            String s = (String)token.object;
            if (keywords.containsKey(s)) {
//            if (s.equals("public") || s.equals("int") || s.equals("String") || s.equals("package") || s.equals("Object") || s.equals("interface")) {
             //   System.out.println("Got a Keyword");
                addNormal(formats, token, startOfNormal, normalFormat);
                formats.addElement(new CpRun(token.offset, token.length, keywordFormat));
                startOfNormal = token.offset + token.length;
            } else if (s.equals(quoteString)) {
                addNormal(formats, token, startOfNormal, normalFormat);
                curToken = lookFor(formats, tokens, token, curToken, literalFormat, quoteString);
                token = (CpRun)tokens.elementAt(curToken);
                startOfNormal = token.offset + token.length;
                if (curToken == tokens.size() - 1) {   // Assume strings are not multi-line
                    break;
                }
            } else if (s.equals(lineCommentString)) {
                addNormal(formats, token, startOfNormal, normalFormat);
                curToken = lookFor(formats, tokens, token, curToken, commentFormat, lineCommentString);
                token = (CpRun)tokens.elementAt(curToken);
                startOfNormal = token.offset + token.length;
            } else if (s.equals(startCommentString)) {
                addNormal(formats, token, startOfNormal, normalFormat);
                curToken = lookFor(formats, tokens, token, curToken, commentFormat, endCommentString);
                token = (CpRun)tokens.elementAt(curToken);
                startOfNormal = token.offset + token.length;
                if (curToken == tokens.size() - 1 && !((String)((CpRun)tokens.elementAt(curToken)).object).equals(endCommentString)) {
                    isMultiLineJava = true;
                    break;
                }
            }

            curToken++;
        }
        formats.addElement(new CpRun(startOfNormal, text.length() - startOfNormal, normalFormat));
    //    System.out.println("Is Multiline = " + isMultiLine);
    }

    public void setExtents(int newHeight, int newPageLeft, int newPageRight)
    {
        height = newHeight;
        pageLeft = newPageLeft;
        pageRight = newPageRight;
    }

    public void setFormats(CpRunList newFormats)
    {
        // THIS IS ODD.  for testing only
        formats = newFormats;
    }

    public CpRunList getFormats()
    {
        return formats;
    }

    public void setLines(CpRunList newLines)
    {
        lines = newLines;
    }

    public CpRunList getLines()
    {
        return lines;
    }

    public void setTextModel(CpTextModel newText)
    {
        text = newText;
    }

    public void setText(String newText)
    {
        text = new CpTextModel(newText + " ");
        formats = new CpRunList();
        formats.addElement(new CpRun(0, text.length(), new CpCharFormat()));
    }

    public char[] getChars()
    {
        return text.getChars();
    }

    public String getString()
    {
        return text.getString();
    }

    public int getHorizontalAlignment()
    {
        return horizontalAlignment;
    }

    public void setHorizontalAlignment(int newHorizontalAlignment)
    {
        horizontalAlignment = newHorizontalAlignment;
    }

    public int getLeftMargin()
    {
        return leftMargin;
    }

    public int getRightMargin()
    {
        return rightMargin;
    }

    public int getFirstIndent()
    {
        return firstIndent;
    }

    public void setMargins(int newLeft, int newRight, int newFirst)
    {
        leftMargin = newLeft;
        rightMargin = newRight;
        firstIndent = newFirst;
    }

    public void computeHeight()
    {
        height = 0;
        for (int i = 0; i < lines.size(); i++) {
        //    System.out.println("Height for line " + i + " = " + getLine(i).getHeight());
            height += getLine(i).getHeight();
        }
    }

    public int getHeight()
    {
        return height;
    }

    public int length()
    {
        return text.length();
    }

    public int numLines()
    {
        return lines.size();
    }

    public int lineNumForOffset(int offset)
    {
        return lines.indexForOffset(offset);
    }

    public CpLine lineForOffset(int offset)
    {
        return getLine(lineNumForOffset(offset));
    }

    public CpLine getLine(int lineNum)
    {
        return (CpLine)((CpRun)lines.elementAt(lineNum)).object;
    }

   public CpCharFormat getFormat(int formatNum)
    {
        return (CpCharFormat)((CpRun)formats.elementAt(formatNum)).object;
    }

    public CpCharFormat formatForOffset(int offset)
    {
        // ted added - 1 10/6.

        CpCharFormat f = getFormat(formats.indexForOffset(offset - 1));
     //   if (f instanceof CpImageFormat) {
     //       f = getFormat(formats.indexForOffset(offset));
     //   }
        return f;
    }

    public int find(String s, int offset)
    {
        return text.getString().indexOf(s, offset);
    }

    public void printDataStructures()
    {
       System.out.println("  text:  " + text);
       System.out.print("  lines: " + lines);
       System.out.print("  formats: " + formats);
    }

    /************************** WRAPPING *****************************/

    public void rewrap(int newPageLeft, int newPageRight)
    {
        pageLeft = newPageLeft;
        pageRight = newPageRight;

      //  System.out.println("about to REWRAP");
    //    printDataStructures();
        int wrapWidth = wrapWidthFor(0);

        lines = new CpRunList();
        int curOffset = 0;
        height = 0;
        while (curOffset < text.length()) {
            CpRun lineRun = getNextLine(curOffset, wrapWidth);
            lines.addElement(lineRun);
            height += ((CpLine)lineRun.object).getHeight();
            curOffset += lineRun.length;
            wrapWidth = wrapWidthFor(1);
        }
    }

    int wrapWidthFor(int lineNum)
    {
        if (lineNum == 0) {
            return (pageRight - rightMargin) - (pageLeft + leftMargin) - firstIndent;
        } else {
            return (pageRight - rightMargin) - (pageLeft + leftMargin);
        }
    }

    private CpRun getNextLine(int startOffset, int wrapWidth)
    {
        int lastBreakOffset = 0;
        int widthAtLastBreak = 0;
        int heightAtLastBreak = 0;
        int descentAtLastBreak = 0;

        int formatNum = formats.indexForOffset(startOffset);
        int lastOffsetInFormat = formats.offsetForIndex(formatNum) + formats.lengthOfIndex(formatNum) - 1;
        int[] charWidths = getFormat(formatNum).getWidths();

        int curWidth = 0;
        int curHeight = getFormat(formatNum).getHeight();
        int curDescent = getFormat(formatNum).getDescent();
        int curOffset = startOffset;
        while (curWidth < wrapWidth) {
            if (curOffset >= text.length()) {
                lastBreakOffset = curOffset - 1;
                widthAtLastBreak = curWidth;
                heightAtLastBreak = curHeight;
                descentAtLastBreak = curDescent;
                break;
            }
            if (curOffset > lastOffsetInFormat) {  // Get the next format
               // System.out.println("text len = " + text.length() + " curOffset = " + curOffset + " formatNum = " + formatNum + " lastOffset = " + lastOffsetInFormat);
                formatNum++;
                charWidths = getFormat(formatNum).getWidths();
                curHeight = Math.max(getFormat(formatNum).getHeight(), curHeight);
                curDescent = Math.max(getFormat(formatNum).getDescent(), curDescent);
                lastOffsetInFormat = curOffset + formats.lengthOfIndex(formatNum) - 1;
            }

            curWidth += charWidths[text.charAt(curOffset)];

            if (text.charAt(curOffset) == ' ' || (curWidth >= wrapWidth && lastBreakOffset == 0)) {
                lastBreakOffset = curOffset;
                widthAtLastBreak = curWidth;
                heightAtLastBreak = curHeight;
                descentAtLastBreak = curDescent;
            }
            curOffset++;
        }
        CpLine line = new CpLine();
        line.setWidth(widthAtLastBreak);
        line.setHeight(heightAtLastBreak);
        line.setDescent(descentAtLastBreak);
        return new CpRun(startOffset, lastBreakOffset - startOffset + 1, line);
    }

    public Point getWordAtOffset(int offset)
    {
        return text.getWordAtOffset(offset);
    }

    // insert a char and return the lines to invalidate x = from, y = to
    public Point insertChar(int offset, char c)
    {
        boolean computeHeight = false;

        text.insert(offset, c);
        // TEd added -1 10/6
        int curFormatNum = formats.indexForOffset(offset - 1);
        /*
        if (offset == 0) {
            System.out.println("INSERTING CHar: " + c);
            System.out.println("Cur formatNum = " + curFormatNum);
            System.out.println("getFormat = " + getFormat(curFormatNum));
        }
        */

        // Big BIG hack to make sure characters don't get an image format
        if ((int)c != 0) { // if this isn't an image (which gets char 0)
            if (getFormat(curFormatNum) instanceof CpImageFormat || getFormat(curFormatNum) instanceof CpLcFormat) {
                curFormatNum++;
                if (offset == 0 || getFormat(curFormatNum) instanceof CpImageFormat || getFormat(curFormatNum) instanceof CpLcFormat) {
                    int tempFormatNum = curFormatNum;
                    while (getFormat(tempFormatNum) instanceof CpImageFormat || getFormat(tempFormatNum) instanceof CpLcFormat) {
                        tempFormatNum++;
                    }
                    CpCharFormat charFormat = (CpCharFormat)getFormat(tempFormatNum).getCopy();
                    //System.out.println("OK.  Got the format (" + tempFormatNum + "), inserting at offset");
                    //System.out.println("formats: " + formats.toString());
                    if (offset == 0) {
                        curFormatNum--;
                    }
                    formats.insertElementAt(new CpRun(offset, 0, charFormat), curFormatNum);
                   // System.out.println("after formats: " + formats.toString());
                }
            }
        }

        formats.getRun(curFormatNum).length++;
        for (int i = curFormatNum + 1; i < formats.size(); i++) {
            formats.getRun(i).offset++;
        }

        if (lines == null) {
            return null;
        }
        int curLineNum = lines.indexForOffset(offset);
        int damageStart = lines.offsetForIndex(curLineNum);
        lines.getRun(curLineNum).length++;
        for (int i = curLineNum + 1; i < lines.size(); i++) {
            lines.getRun(i).offset++;
        }

        while (true) {
            CpRun oldLine = lines.getRun(curLineNum);
            CpRun newLine = getNextLine(lines.offsetForIndex(curLineNum), wrapWidthFor(curLineNum));

            lines.setElementAt(newLine, curLineNum);
            if (((CpLine)oldLine.object).getHeight() != ((CpLine)newLine.object).getHeight()) {
                computeHeight = true;
            }
            if (oldLine.length == newLine.length) {
                if (computeHeight) {
                    computeHeight();
                }
                return new Point(damageStart, newLine.offset + newLine.length - 1);
            }
         //   System.out.println("Line " + curLineNum + " wrapped ");
            curLineNum++;
            if (curLineNum >= lines.size()) {
          //      System.out.println("Added a line");
                lines.addElement(new CpRun(0, 0, new CpLine()));
            }
            CpRun nextLine = lines.getRun(curLineNum);
            nextLine.offset = newLine.offset + newLine.length;
            nextLine.length += oldLine.length - newLine.length;
        }
    }

    public void deleteFrom(int offset)
    {
        text.delete(offset, text.length() - offset);

        int linesToDelete = lines.indexForOffset(offset);
        for (int i = 0; i < linesToDelete; i++) {
            lines.removeElementAt(0);
        }
        int formatsToDelete = formats.indexForOffset(offset);
        for (int i = 0; i < formatsToDelete; i++) {
            getFormat(i).aboutToDelete();
            formats.removeElementAt(0);
        }
        computeHeight();
    }

    public void insertParagraph(int offset, CpParagraph para)
    {
        CpParagraph endPara = splitParagraph(offset);
        appendParagraph(para);
        appendParagraph(endPara);
    }

    public void appendParagraph(CpParagraph para)
    {
     //   System.out.println("In append, adding: " + para.getChars() + " at " + text.length());
        delete(length() - 1, length());  // Get rid of the ending space " "
        int len = length();
        text.insert(len, para.getChars());
        CpRunList paraFormats = para.getFormats();
        for (int i = 0; i < paraFormats.size(); i++) {
            CpRun format = paraFormats.getRun(i);
            format.offset += len;
            formats.addElement(format);
        }
        //printDataStructures();
        rewrap(pageLeft, pageRight);
        computeHeight();
    }

    public CpParagraph splitParagraph(int offset)
    {
        CpParagraph newPara;

        newPara = getCopy();
        delete(offset, length() - 1);
        newPara.delete(0, offset);
        if (newPara.length() == 1) {
            CpRunList newFormats = new CpRunList();
            newFormats.addElement(new CpRun(0, 1, formatForOffset(length() - 1)));
            newPara.setFormats(newFormats);
        }
        return newPara;
    }

    public char charAt(int offset)
    {
        return text.charAt(offset);
    }

    public int getNumCharsInLineWithOffset(int offset)
    {
        return lines.getRun(lines.indexForOffset(offset)).length;
    }

    public void delete(int from, int to)
    {
        if (from >= to) {
            return;
        }
     //   System.out.println("DELETING from " + from + " to " + to);
    //    System.out.println("  Formats: " + formats);

        // Ted added for inserting lcs 10/6
        int start = formats.indexForOffset(from);
        int end = formats.indexForOffset(to - 1);
        for (int i = start; i <= end; i++) {
            getFormat(i).aboutToDelete();
        }
        // End Ted add

        formats.delete(from, to);
    //    System.out.println("  Formats: " + formats);
    //    System.out.println("Text was len " + text.length());
        text.delete(from, to - from);
   //     System.out.println("Deleted text from " + from + " len " + (to - from + 1) + " now len = " + text.length());
      //  System.out.println("  Text: " + text);
        rewrap(pageLeft, pageRight);
   //    System.out.println("Lines: " + lines);
        computeHeight();
    }

    // can be called by someone creating a paragraph programmatically (like in an example)
    public void setFormat(int from, int length, CpCharFormat format)
    {
        formats.addFormat(from, from + length, format);
    }

    public void addOrMergeFormat(boolean merge, int from, int to, CpCharFormat format)
    {
        if (merge) {
            formats.mergeFormat(from, to, format);
        } else {
            formats.addFormat(from, to, format);
        }
        rewrap(pageLeft, pageRight);
        computeHeight();
    }

    public int xForOffset(int offset)
    {
        int lineOffset = lines.offsetOfRunContainingOffset(offset);

        int formatNum = formats.indexForOffset(lineOffset);

        int[] charWidths = getFormat(formatNum).getWidths();
        int lastOffsetInFormat = formats.offsetForIndex(formatNum) + formats.lengthOfIndex(formatNum) - 1;

        int x = 0;
        int curOffset = lineOffset;
        while (curOffset < offset) {
            if (curOffset > lastOffsetInFormat) {  // Get the next format
                formatNum++;
                charWidths = getFormat(formatNum).getWidths();
                lastOffsetInFormat = curOffset + formats.lengthOfIndex(formatNum) - 1;
            }
            x += charWidths[text.charAt(curOffset)];
            curOffset++;
        }
        return x;
    }

    public int offsetForX(int lineNum, int x)
    {
        int lineOffset = lines.offsetForIndex(lineNum);

        int formatNum = formats.indexForOffset(lineOffset);

        int[] charWidths = getFormat(formatNum).getWidths();
        int lastOffsetInFormat = formats.offsetForIndex(formatNum) + formats.lengthOfIndex(formatNum) - 1;

        int curX = 0;
        int curOffset = lineOffset;
        while (curOffset < text.length() && curX + charWidths[text.charAt(curOffset)] / 2 < x) {
            curX += charWidths[text.charAt(curOffset)];
            curOffset++;
            if (curOffset > lastOffsetInFormat && curOffset < text.length()) {  // Get the next format
                formatNum++;
                charWidths = getFormat(formatNum).getWidths();
                lastOffsetInFormat = curOffset + formats.lengthOfIndex(formatNum) - 1;
            }
        }
        return Math.min(curOffset, text.length() - 1);
    }

    int xForLineNum(int lineNum)
    {
        int left = pageLeft + getLeftMargin();
        int right = pageRight - getRightMargin();

        if (lineNum == 0) {
            left += getFirstIndent();
        }
        CpLine line = getLine(lineNum);
        switch (getHorizontalAlignment()) {
            case RIGHT : { return right - line.getWidth(); }
            case CENTER : { return (right - left - line.getWidth()) / 2 + left; }
            default: return left;
        }
    }

    public int yForLineNum(int lineNum)
    {
        int curY = 0;
        for (int i = 0; i < lineNum; i++) {
            curY += getLine(i).getHeight();
        }
        return curY;
    }

    public int lineNumForY(int y)
    {
        int curY = 0;
        for (int i = 0; i < lines.size(); i++) {
            curY += getLine(i).getHeight();
            if (y < curY) {
                return i;
            }
        }
        return lines.size() - 1;
    }

    public int paintFrom(Graphics g, int y, Rectangle clipRect)
    {
        for (int lineNum = 0;  lineNum < lines.size(); lineNum++) {
            CpLine line = getLine(lineNum);
            if (y + line.getHeight() <= clipRect.y) { // Line is above cliprect
                y += line.getHeight();
             //   System.out.println("Not drawing line " + lineNum);
                continue;
            }
            int curX = xForLineNum(lineNum);
            if (lineNum == 0 && bulletMode) {
                int bulletY = y + (line.getHeight() - 4) / 2;
                g.setColor(bulletColor);
                g.fillRect(curX - bulletExtent - 10, bulletY, bulletExtent, bulletExtent);
            }
         //   System.out.println("Drawing line " + lineNum + " at " + curX + ", " + y);
            int lineStartOffset = lines.offsetForIndex(lineNum);
            int startFormat = formats.indexForOffset(lineStartOffset);
            int endFormat = formats.indexForOffset(lineStartOffset + lines.lengthOfIndex(lineNum) - 1);
            int curOffset = lineStartOffset;
            for (int formatNum = startFormat; formatNum <= endFormat; formatNum++) {
                CpCharFormat format = (CpCharFormat)formats.objectAt(formatNum);
                int charsToDraw = Math.min(lines.lengthOfIndex(lineNum) - (curOffset - lines.offsetForIndex(lineNum)), formats.lengthOfIndex(formatNum) - (curOffset - formats.offsetForIndex(formatNum)));

                curX += format.paint(g, curOffset, charsToDraw, line, text, curX, y);


                curOffset += charsToDraw;
            }
            y += line.getHeight();
          //  System.out.println("Drew line " + lineNum + ", now y = " + y + " clip val = " + (clipRect.y + clipRect.height));
            if (y > clipRect.y + clipRect.height) {  // line is below cliprect
                return y;
            }
        }
        return y;
    }


    /*********************  FILE SAVE/LOAD ***************************/

    public CpParagraph(RandomAccessFile file) throws IOException
    {
        super();
        horizontalAlignment = file.readInt();
        leftMargin = file.readInt();
        rightMargin = file.readInt();
        firstIndent = file.readInt();
      //  System.out.println("Got just " + horizontalAlignment + " left " + leftMargin + " right " + rightMargin + " first " + firstIndent);
        String s = file.readUTF();
     //   System.out.println("Setting text to: " + s);
     //   System.out.println("Text len = " + s.length());
        text = new CpTextModel(s);
        int numFormats = file.readInt();
        formats = new CpRunList();
        for (int i = 0; i < numFormats; i++) {
            CpRun run;
            formats.addElement(run = new CpRun(file.readInt(), file.readInt(), new CpCharFormat(file)));
          //  System.out.println("Added format " + run);
        }
    }

    public void writeTo(RandomAccessFile file) throws IOException
    {
        file.writeInt(horizontalAlignment);
        file.writeInt(leftMargin);
        file.writeInt(rightMargin);
        file.writeInt(firstIndent);
        file.writeUTF(text.getString());
        file.writeInt(formats.size());
        for (int i = 0; i < formats.size(); i++) {
            file.writeInt(formats.getRun(i).offset);
            file.writeInt(formats.getRun(i).length);
            getFormat(i).writeTo(file);
        }
    }

}
