/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.ast;

import java.util.Arrays;

/**
 * Calculates from an absolute offset in the source file the line/column
 * coordinate. This is needed as Rhino only offers absolute positions for each
 * node. Some other languages like XML and Apex use this, too.
 *
 * Idea from:
 * http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/SourceFile.java
 */
public class SourceCodePositioner {

    private int[] lineOffsets;
    private int sourceCodeLength;

    public SourceCodePositioner(String sourceCode) {
        analyzeLineOffsets(sourceCode);
    }

    private void analyzeLineOffsets(String sourceCode) {
        String[] lines = sourceCode.split("\n");
        sourceCodeLength = sourceCode.length();

        int startOffset = 0;
        int lineNumber = 0;

        lineOffsets = new int[lines.length];

        for (String line : lines) {
            lineOffsets[lineNumber] = startOffset;
            lineNumber++;
            startOffset += line.length() + 1; // +1 for the "\n" character
        }
    }

    public int lineNumberFromOffset(int offset) {
        int search = Arrays.binarySearch(lineOffsets, offset);
        int lineNumber;
        if (search >= 0) {
            lineNumber = search;
        } else {
            int insertionPoint = search;
            insertionPoint += 1;
            insertionPoint *= -1;
            lineNumber = insertionPoint - 1; // take the insertion point one
            // before
        }
        return lineNumber + 1; // 1-based line numbers
    }

    public int columnFromOffset(int lineNumber, int offset) {
        int lineIndex = lineNumber - 1;
        if (lineIndex < 0 || lineIndex >= lineOffsets.length) {
            // no line number found...
            return 0;
        }
        int columnOffset = offset - lineOffsets[lineNumber - 1];
        return columnOffset + 1; // 1-based column offsets
    }

    public int getLastLine() {
        return lineOffsets.length;
    }

    public int getLastLineColumn() {
        return columnFromOffset(getLastLine(), sourceCodeLength - 1);
    }
}
