package org.incava.java;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import org.incava.log.Log;


/**
 * Parses Javadoc into a list of points, which represent the locations of
 * description and tagged comments in a Javadoc comment block.
 */
public class JavadocParser
{
    /**
     * The current position.
     */
    private int pos;

    /**
     * The length of the Javadoc being parsed.
     */
    private int len;

    /**
     * The Javadoc being parsed.
     */
    private String text;

    /**
     * Parses the Javadoc in the text. Assumes a start line of 1 and a start
     * column of 1.
     */
    public List parse(String text)
    {
        return parse(text, 1, 1);
    }

    public List parse(String text, int startLine, int startColumn)
    {
        // Log.log("text: " + text);
        
        this.text = text;
        len = text.length();
        ArrayList ary = new ArrayList();
        pos = 0;

        while (pos < len && Character.isWhitespace(text.charAt(pos))) {
            ++pos;
        }
        
        if (pos + 3 < len && text.startsWith("/**")) {
            // Log.log("got comment start");
            pos += 3;

            while (pos < len && (Character.isWhitespace(text.charAt(pos)) || text.charAt(pos) == '*')) {
                ++pos;
            }

            --len;
            while (len >= 0) {
                // Log.log("char[" + len + "]: '" + text.charAt(len) + "'");
                if (Character.isWhitespace(text.charAt(len)) || text.charAt(len) == '*') {
                    // Log.log("star or WS; (text: '" + text.charAt(len) + "')");
                    --len;
                }
                else if (len > 0 && text.charAt(len) == '/' && text.charAt(len - 1) == '*') {
                    // Log.log("at end of comment");
                    len -= 2;
                }
                else {
                    break;
                }
            }
            ++len;

            // Log.log("pos: " + pos + "; len: " + len);
            
            if (pos < len) {
                // the description
                if (text.charAt(pos) == '@') {
                    // Log.log("got tag start -- no description");
                    // null means no description
                    ary.add(null);
                }
                else {
                    // Log.log("at description start: " + pos);

                    Point desc = new Point(pos, -1);

                    read(desc);
                
                    // Log.log("at end, pos: " + pos + "; desc pos   : " + desc);

                    ary.add(desc);
                }

                // now, the tagged comments:
                while (pos < len && text.charAt(pos) == '@') {
                    // Log.log("tag starting.");

                    Point tag = new Point(pos, -1);

                    ++pos;
                    read(tag);

                    // Log.log("tag pos   : " + tag);

                    ary.add(tag);
                }
            }
            
            // Log.log("returning: " + ary);

            return ary;
        }
        else {
            // Log.log("no Javadoc comment in this string.");
            return null;
        }
    }

    /**
     * Reads to the next Javadoc field, or to the end of the comment.
     */
    protected void read(Point pt) 
    {
        // Log.log("pos: " + pos + ", len: " + len + ", text: " + text + ", pt: " + pt);
        
        pt.y = pos;
        while (pos < len && (text.charAt(pos) != '@' || (pos >= 0 && text.charAt(pos - 1) == '{'))) {
            // // Log.log("pos: " + pos);
            pt.y = pos;

            ++pos;
                        
            // read to end of line, or end of text.
            // Mac: \r, Unix: \n, DOS: \r\n:
            if (text.charAt(pos) == '\r') {
                if (pos + 1 < len && text.charAt(pos + 1) == '\n') {
                    ++pos;
                }
            }
            else if (text.charAt(pos) != '\n') {
                continue;
            }

            // Log.log("looking for next position");

            // now, we're at the start of a new line:
            while (pos < len && (Character.isWhitespace(text.charAt(pos)) || text.charAt(pos) == '*')) {
                ++pos;
                // Log.log("advanced pos: " + pos);
            }
        }

        ++pt.y;
        // Log.log("at end -- pos : " + pos  + ", len: " + len + ", char: '" + text.charAt(pos) + "', text: " + text + ", pt: " + pt);
    }

}
