/*
 * ModeShape (http://www.modeshape.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.modeshape.sequencer.text;

import java.util.Arrays;
import org.modeshape.common.util.CheckArg;

/**
 * An text sequencer implementation that uses a {@link #setColumnStartPositions(String) list of column numbers} to split incoming
 * rows into fixed-width columns. By default, this class treats each row as a single column. There is an implicit column start
 * index of 0 for the first column.
 * 
 * @see AbstractTextSequencer
 */
public class FixedWidthTextSequencer extends AbstractTextSequencer {

    private int[] columnStartPositions = new int[] {};

    /**
     * Set the column start positions. The column start positions are 0-based. Everything before the first start position is
     * treated as the first column.
     * <p>
     * As an example, if the column start positions were {3, 6, 15} and the incoming stream was:
     * 
     * <pre>
     *           1         2
     * 012345678901234567890
     * supercallifragilistic
     * expialidocious
     * </pre>
     * This sequencer would return the following rows:
     * <pre>
     * row 1: "sup", "erc", "allifragi", "listic"
     * row 2: "exp:, "ial", "idocious"
     * </pre>
     * 
     * Note that there are only three columns returned in the second row, as there were not enough characters to reach the third
     * start position.
     * </p>
     * 
     * @param columnStartPositions the column startPositions; may not be null
     */
    public void setColumnStartPositions( int[] columnStartPositions ) {
        CheckArg.isNotNull(columnStartPositions, "columnStartPositions");

        this.columnStartPositions = columnStartPositions;
        Arrays.sort(columnStartPositions);
    }

    /**
     * Set the column start positions from a list of column start positions concatenated into a single, comma-delimited string.
     * 
     * @param commaDelimitedColumnStartPositions a list of column start positions concatenated into a single, comma-delimited
     *        string; may not be null
     * @see #setColumnStartPositions(int[])
     */
    public void setColumnStartPositions( String commaDelimitedColumnStartPositions ) {
        CheckArg.isNotNull(commaDelimitedColumnStartPositions, "commaDelimitedColumnStartPositions");

        String[] stringStartPositions = commaDelimitedColumnStartPositions.split(",");
        int[] columnStartPositions = new int[stringStartPositions.length];

        for (int i = 0; i < stringStartPositions.length; i++) {
            columnStartPositions[i] = Integer.valueOf(stringStartPositions[i]);
        }

        setColumnStartPositions(columnStartPositions);
    }

    @Override
    protected String[] parseLine( String line ) {
        assert line != null;

        int columnCount = columnStartPositions.length + 1;
        int currentPos = 0;

        String[] columns = new String[columnCount];

        for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
            int endPos = columnIndex >= columnCount - 1 ? Integer.MAX_VALUE : columnStartPositions[columnIndex];
            String chunk = parseColumn(line, currentPos, endPos);

            // The line was shorter than expected
            if (chunk == null) {
                assert columnIndex > 0 : "parseColumn failed to return the first column in string " + line;

                String[] truncatedColumns = new String[columnIndex - 1];
                System.arraycopy(columns, 0, truncatedColumns, 0, columnIndex - 1);
                return truncatedColumns;
            }
            columns[columnIndex] = chunk;
            currentPos = endPos;
        }

        return columns;
    }

    private String parseColumn( String line,
                                int startPos,
                                int endPos ) {
        int length = line.length();

        if (length < startPos) {
            return null;
        }

        if (length < endPos) {
            return line.substring(startPos);
        }

        return line.substring(startPos, endPos);
    }

}
