/*
 * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
 *
 * from https://github.com/dsehnal/CIFTools.js
 * @author David Sehnal <david.sehnal@gmail.com>
 */
/**
 * Efficient integer and float parsers.
 *
 * For the purposes of parsing numbers from the mmCIF data representations,
 * up to 4 times faster than JS parseInt/parseFloat.
 */

function parseInt(str, start, end) {
    var ret = 0, neg = 1;
    if (str.charCodeAt(start) === 45 /* - */) {
        neg = -1;
        start++;
    }
    for (; start < end; start++) {
        var c = str.charCodeAt(start) - 48;
        if (c > 9 || c < 0)
            { return (neg * ret) | 0; }
        else
            { ret = (10 * ret + c) | 0; }
    }
    return neg * ret;
}
function parseScientific(main, str, start, end) {
    // handle + in '1e+1' separately.
    if (str.charCodeAt(start) === 43 /* + */)
        { start++; }
    return main * Math.pow(10.0, parseInt(str, start, end));
}

function parseFloat(str, start, end) {
    var neg = 1.0, ret = 0.0, point = 0.0, div = 1.0;
    if (str.charCodeAt(start) === 45) {
        neg = -1.0;
        ++start;
    }
    while (start < end) {
        var c = str.charCodeAt(start) - 48;
        if (c >= 0 && c < 10) {
            ret = ret * 10 + c;
            ++start;
        }
        else if (c === -2) {
            ++start;
            while (start < end) {
                c = str.charCodeAt(start) - 48;
                if (c >= 0 && c < 10) {
                    point = 10.0 * point + c;
                    div = 10.0 * div;
                    ++start;
                }
                else if (c === 53 || c === 21) {
                    return parseScientific(neg * (ret + point / div), str, start + 1, end);
                }
                else {
                    return neg * (ret + point / div);
                }
            }
            return neg * (ret + point / div);
        }
        else if (c === 53 || c === 21) {
            return parseScientific(neg * ret, str, start + 1, end);
        }
        else
            { break; }
    }
    return neg * ret;
}

/**
 * Eat everything until a newline occurs.
 */
function eatLine(state) {
    while (state.position < state.length) {
        switch (state.data.charCodeAt(state.position)) {
            case 10:// \n
                state.currentTokenEnd = state.position;
                ++state.position;
                ++state.currentLineNumber;
                return;
            case 13:// \r
                state.currentTokenEnd = state.position;
                ++state.position;
                ++state.currentLineNumber;
                if (state.data.charCodeAt(state.position) === 10) {
                    ++state.position;
                }
                return;
            default:
                ++state.position;
        }
    }
    state.currentTokenEnd = state.position;
}
/**
 * Eat everything until a whitespace/newline occurs.
 */
function eatValue(state) {
    while (state.position < state.length) {
        switch (state.data.charCodeAt(state.position)) {
            case 9: // \t
            case 10: // \n
            case 13: // \r
            case 32:// ' '
                state.currentTokenEnd = state.position;
                return;
            default:
                ++state.position;
                break;
        }
    }
    state.currentTokenEnd = state.position;
}
/**
 * Skips all the whitespace - space, tab, newline, CR
 * Handles incrementing line count.
 */
function skipWhitespace(state) {
    var prev = 10;
    while (state.position < state.length) {
        var c = state.data.charCodeAt(state.position);
        switch (c) {
            case 9: // '\t'
            case 32:// ' '
                prev = c;
                ++state.position;
                break;
            case 10:// \n
                // handle \r\n
                if (prev !== 13) {
                    ++state.currentLineNumber;
                }
                prev = c;
                ++state.position;
                break;
            case 13:// \r
                prev = c;
                ++state.position;
                ++state.currentLineNumber;
                break;
            default:
                return prev;
        }
    }
    return prev;
}

var Tokens;
(function (Tokens) {
    function resize(tokens) {
        // scale the size using golden ratio, because why not.
        var newBuffer = new Int32Array((1.61 * tokens.indices.length) | 0);
        newBuffer.set(tokens.indices);
        tokens.indices = newBuffer;
        tokens.indicesLenMinus2 = (newBuffer.length - 2) | 0;
    }
    function add(tokens, start, end) {
        if (tokens.count > tokens.indicesLenMinus2) {
            resize(tokens);
        }
        tokens.indices[tokens.count++] = start;
        tokens.indices[tokens.count++] = end;
    }
    Tokens.add = add;
    function addUnchecked(tokens, start, end) {
        tokens.indices[tokens.count++] = start;
        tokens.indices[tokens.count++] = end;
    }
    Tokens.addUnchecked = addUnchecked;
    function create(size) {
        return {
            indicesLenMinus2: (size - 2) | 0,
            count: 0,
            indices: new Int32Array(size)
        };
    }
    Tokens.create = create;
})(Tokens || (Tokens = {}));

/**
 * Represents a column that is not present.
 */
var _UndefinedColumn = function _UndefinedColumn() {
    this.isDefined = false;
};
_UndefinedColumn.prototype.getString = function getString (row) { return null; };

_UndefinedColumn.prototype.getInteger = function getInteger (row) { return 0; };
_UndefinedColumn.prototype.getFloat = function getFloat (row) { return 0.0; };
_UndefinedColumn.prototype.getValuePresence = function getValuePresence (row) { return 1 /* NotSpecified */; };
_UndefinedColumn.prototype.areValuesEqual = function areValuesEqual (rowA, rowB) { return true; };
_UndefinedColumn.prototype.stringEquals = function stringEquals (row, value) { return value === null; };
var UndefinedColumn = new _UndefinedColumn();

/*
 * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
 *
 * from https://github.com/dsehnal/CIFTools.js
 * @author David Sehnal <david.sehnal@gmail.com>
 */
var ShortStringPool;
(function (ShortStringPool) {
    function create() { return Object.create(null); }
    ShortStringPool.create = create;
    function get(pool, str) {
        if (str.length > 6)
            { return str; }
        var value = pool[str];
        if (value !== void 0)
            { return value; }
        pool[str] = str;
        return str;
    }
    ShortStringPool.get = get;
})(ShortStringPool || (ShortStringPool = {}));

/**
 * Represents a single column.
 */
var TextColumn = function TextColumn(table, data, name, index) {
    this.data = data;
    this.name = name;
    this.index = index;
    this.stringPool = ShortStringPool.create();
    this.isDefined = true;
    this.indices = table.indices;
    this.columnCount = table.columnCount;
};
/**
 * Returns the string value at given row.
 */
TextColumn.prototype.getString = function getString (row) {
    var i = (row * this.columnCount + this.index) * 2;
    return ShortStringPool.get(this.stringPool, this.data.substring(this.indices[i], this.indices[i + 1]));
};
/**
 * Returns the integer value at given row.
 */
TextColumn.prototype.getInteger = function getInteger (row) {
    var i = (row * this.columnCount + this.index) * 2;
    return parseInt(this.data, this.indices[i], this.indices[i + 1]);
};
/**
 * Returns the float value at given row.
 */
TextColumn.prototype.getFloat = function getFloat (row) {
    var i = (row * this.columnCount + this.index) * 2;
    return parseFloat(this.data, this.indices[i], this.indices[i + 1]);
};
/**
 * Returns true if the token has the specified string value.
 */
TextColumn.prototype.stringEquals = function stringEquals (row, value) {
        var this$1 = this;

    var aIndex = (row * this.columnCount + this.index) * 2, s = this.indices[aIndex], len = value.length;
    if (len !== this.indices[aIndex + 1] - s)
        { return false; }
    for (var i = 0; i < len; i++) {
        if (this$1.data.charCodeAt(i + s) !== value.charCodeAt(i))
            { return false; }
    }
    return true;
};
/**
 * Determines if values at the given rows are equal.
 */
TextColumn.prototype.areValuesEqual = function areValuesEqual (rowA, rowB) {
        var this$1 = this;

    var aIndex = (rowA * this.columnCount + this.index) * 2;
    var bIndex = (rowB * this.columnCount + this.index) * 2;
    var aS = this.indices[aIndex];
    var bS = this.indices[bIndex];
    var len = this.indices[aIndex + 1] - aS;
    if (len !== this.indices[bIndex + 1] - bS)
        { return false; }
    for (var i = 0; i < len; i++) {
        if (this$1.data.charCodeAt(i + aS) !== this$1.data.charCodeAt(i + bS)) {
            return false;
        }
    }
    return true;
};
TextColumn.prototype.getValuePresence = function getValuePresence (row) {
    var index = 2 * (row * this.columnCount + this.index);
    if (this.indices[index] === this.indices[index + 1]) {
        return 1 /* NotSpecified */;
    }
    return 0 /* Present */;
};
var CifColumn = (function (TextColumn) {
    function CifColumn () {
        TextColumn.apply(this, arguments);
    }

    if ( TextColumn ) CifColumn.__proto__ = TextColumn;
    CifColumn.prototype = Object.create( TextColumn && TextColumn.prototype );
    CifColumn.prototype.constructor = CifColumn;

    CifColumn.prototype.getString = function getString (row) {
        var ret = TextColumn.prototype.getString.call(this, row);
        if (ret === '.' || ret === '?')
            { return null; }
        return ret;
    };
    /**
     * Returns true if the value is not defined (. or ? token).
     */
    CifColumn.prototype.getValuePresence = function getValuePresence (row) {
        var index = 2 * (row * this.columnCount + this.index);
        var s = this.indices[index];
        if (this.indices[index + 1] - s !== 1)
            { return 0 /* Present */; }
        var v = this.data.charCodeAt(s);
        if (v === 46 /* . */)
            { return 1 /* NotSpecified */; }
        if (v === 63 /* ? */)
            { return 2 /* Unknown */; }
        return 0 /* Present */;
    };

    return CifColumn;
}(TextColumn));

/**
 * Represents a table backed by a string.
 */
var TextTable = function TextTable(data, name, columns, tokens) {
    this.name = name;
    this.indices = tokens.indices;
    this.data = data;
    this.columnCount = columns.length;
    this.rowCount = (tokens.count / 2 / columns.length) | 0;
    this.initColumns(columns);
};

var prototypeAccessors = { columnNames: {} };

prototypeAccessors.columnNames.get = function () {
    return this.columnNameList;
};
/**
 * Get a column object that makes accessing data easier.
 */
TextTable.prototype.getColumn = function getColumn (name) {
    var i = this.columnIndices.get(name);
    if (i !== void 0)
        { return new TextColumn(this, this.data, name, i); }
    return UndefinedColumn;
};
TextTable.prototype.initColumns = function initColumns (columns) {
        var this$1 = this;

    this.columnIndices = new Map();
    this.columnNameList = [];
    for (var i = 0; i < columns.length; i++) {
        this$1.columnIndices.set(columns[i], i);
        this$1.columnNameList.push(columns[i]);
    }
};

Object.defineProperties( TextTable.prototype, prototypeAccessors );
var CifTable = (function (TextTable) {
    function CifTable () {
        TextTable.apply(this, arguments);
    }

    if ( TextTable ) CifTable.__proto__ = TextTable;
    CifTable.prototype = Object.create( TextTable && TextTable.prototype );
    CifTable.prototype.constructor = CifTable;

    CifTable.prototype.getColumn = function getColumn (name) {
        var i = this.columnIndices.get(name);
        if (i !== void 0)
            { return new CifColumn(this, this.data, name, i); }
        return UndefinedColumn;
    };
    CifTable.prototype.initColumns = function initColumns (columns) {
        var this$1 = this;

        this.columnIndices = new Map();
        this.columnNameList = [];
        for (var i = 0; i < columns.length; i++) {
            var colName = columns[i].substr(this$1.name.length + 1);
            this$1.columnIndices.set(colName, i);
            this$1.columnNameList.push(colName);
        }
    };

    return CifTable;
}(TextTable));

/*
 * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
 *
 * from https://github.com/dsehnal/CIFTools.js
 * @author David Sehnal <david.sehnal@gmail.com>
 */
var ParserResult;
(function (ParserResult) {
    function error(message, line) {
        if ( line === void 0 ) line = -1;

        return new ParserError(message, line);
    }
    ParserResult.error = error;
    function success(result, warnings) {
        if ( warnings === void 0 ) warnings = [];

        return new ParserSuccess(result, warnings);
    }
    ParserResult.success = success;
})(ParserResult || (ParserResult = {}));
var ParserError = function ParserError(message, line) {
    this.message = message;
    this.line = line;
    this.isError = true;
};
ParserError.prototype.toString = function toString () {
    if (this.line >= 0) {
        return ("[Line " + (this.line) + "] " + (this.message));
    }
    return this.message;
};
var ParserSuccess = function ParserSuccess(result, warnings) {
    this.result = result;
    this.warnings = warnings;
    this.isError = false;
};

/*
 * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 */
var GroFile = function GroFile(data) {
    this.blocks = [];
    this.data = data;
};
var GroBlock = function GroBlock(data) {
    this.data = data;
    this.tableMap = new Map();
    this.tableList = [];
};

GroBlock.prototype.getTable = function getTable (name) {
    return this.tableMap.get(name);
};
/**
 * Adds a table.
 */
GroBlock.prototype.addTable = function addTable (table) {
    this.tableList[this.tableList.length] = table;
    this.tableMap.set(table.name, table);
};
function createTokenizer(data) {
    return {
        data: data,
        position: 0,
        length: data.length,
        currentLineNumber: 1,
        currentTokenStart: 0,
        currentTokenEnd: 0,
        numberOfAtoms: 0,
        hasVelocities: false,
        numberOfDecimalPlaces: 3
    };
}
/**
 * title string (free format string, optional time in ps after 't=')
 */
function handleTitleString(state, tokens) {
    eatLine(state);
    // console.log('title', state.data.substring(state.currentTokenStart, state.currentTokenEnd))
    var start = state.currentTokenStart;
    var end = state.currentTokenEnd;
    var valueStart = state.currentTokenStart;
    var valueEnd = start;
    while (valueEnd < end && !isTime(state.data, valueEnd))
        { ++valueEnd; }
    if (isTime(state.data, valueEnd)) {
        var timeStart = valueEnd + 2;
        while (valueEnd > start && isSpaceOrComma(state.data, valueEnd - 1))
            { --valueEnd; }
        Tokens.add(tokens, valueStart, valueEnd); // title
        while (timeStart < end && state.data.charCodeAt(timeStart) === 32)
            { ++timeStart; }
        while (valueEnd > timeStart && state.data.charCodeAt(valueEnd - 1) === 32)
            { --valueEnd; }
        Tokens.add(tokens, timeStart, end); // time
    }
    else {
        Tokens.add(tokens, valueStart, valueEnd); // title
        Tokens.add(tokens, valueEnd, valueEnd); // empty token for time
    }
}
function isSpaceOrComma(data, position) {
    var c = data.charCodeAt(position);
    return c === 32 || c === 44;
}
function isTime(data, position) {
    // T/t
    var c = data.charCodeAt(position);
    if (c !== 84 && c !== 116)
        { return false; }
    // =
    if (data.charCodeAt(position + 1) !== 61)
        { return false; }
    return true;
}
// function isDot(state: TokenizerState): boolean {
//     // .
//     if (state.data.charCodeAt(state.currentTokenStart) !== 46) return false;
//     return true;
// }
// function numberOfDecimalPlaces (state: TokenizerState) {
//     // var ndec = firstLines[ 2 ].length - firstLines[ 2 ].lastIndexOf('.') - 1
//     const start = state.currentTokenStart
//     const end = state.currentTokenEnd
//     for (let i = end; start < i; --i) {
//         // .
//         if (state.data.charCodeAt(i) === 46) return end - start - i
//     }
//     throw new Error('Could not determine number of decimal places')
// }
/**
 * number of atoms (free format integer)
 */
function handleNumberOfAtoms(state, tokens) {
    skipWhitespace(state);
    state.currentTokenStart = state.position;
    eatValue(state);
    state.numberOfAtoms = parseInt(state.data, state.currentTokenStart, state.currentTokenEnd);
    Tokens.add(tokens, state.currentTokenStart, state.currentTokenEnd);
    eatLine(state);
}
// function checkForVelocities (state: GroState) {
// }
/**
 * This format is fixed, ie. all columns are in a fixed position.
 * Optionally (for now only yet with trjconv) you can write gro files
 * with any number of decimal places, the format will then be n+5
 * positions with n decimal places (n+1 for velocities) in stead
 * of 8 with 3 (with 4 for velocities). Upon reading, the precision
 * will be inferred from the distance between the decimal points
 * (which will be n+5). Columns contain the following information
 * (from left to right):
 *     residue number (5 positions, integer)
 *     residue name (5 characters)
 *     atom name (5 characters)
 *     atom number (5 positions, integer)
 *     position (in nm, x y z in 3 columns, each 8 positions with 3 decimal places)
 *     velocity (in nm/ps (or km/s), x y z in 3 columns, each 8 positions with 4 decimal places)
 */
function handleAtoms(state, block) {
    var name = 'atoms';
    var columns = ['residueNumber', 'residueName', 'atomName', 'atomNumber', 'x', 'y', 'z'];
    if (state.hasVelocities) {
        columns.push('vx', 'vy', 'vz');
    }
    var fieldSizes = [5, 5, 5, 5, 8, 8, 8, 8, 8, 8];
    var columnCount = columns.length;
    var tokens = Tokens.create(state.numberOfAtoms * 2 * columnCount);
    var start;
    var end;
    var valueStart;
    var valueEnd = state.position;
    for (var i = 0; i < state.numberOfAtoms; ++i) {
        state.currentTokenStart = state.position;
        end = state.currentTokenStart;
        for (var j = 0; j < columnCount; ++j) {
            start = end;
            end = start + fieldSizes[j];
            // trim
            valueStart = start;
            valueEnd = end;
            while (valueStart < valueEnd && state.data.charCodeAt(valueStart) === 32)
                { ++valueStart; }
            while (valueEnd > valueStart && state.data.charCodeAt(valueEnd - 1) === 32)
                { --valueEnd; }
            Tokens.addUnchecked(tokens, valueStart, valueEnd);
        }
        state.position = valueEnd;
        eatLine(state);
    }
    block.addTable(new TextTable(state.data, name, columns, tokens));
}
/**
 * box vectors (free format, space separated reals), values:
 * v1(x) v2(y) v3(z) v1(y) v1(z) v2(x) v2(z) v3(x) v3(y),
 * the last 6 values may be omitted (they will be set to zero).
 * Gromacs only supports boxes with v1(y)=v1(z)=v2(z)=0.
 */
function handleBoxVectors(state, tokens) {
    // just read the first three values, ignore any remaining
    for (var i = 0; i < 3; ++i) {
        skipWhitespace(state);
        state.currentTokenStart = state.position;
        eatValue(state);
        Tokens.add(tokens, state.currentTokenStart, state.currentTokenEnd);
    }
}
/**
 * Creates an error result.
 */
// function error(line: number, message: string) {
//     return ParserResult.error<GroFile>(message, line);
// }
/**
 * Creates a data result.
 */
function result(data) {
    return ParserResult.success(data);
}
function parseInternal(data) {
    var state = createTokenizer(data);
    var file = new GroFile(data);
    var block = new GroBlock(data);
    file.blocks.push(block);
    var headerColumns = ['title', 'timeInPs', 'numberOfAtoms', 'boxX', 'boxY', 'boxZ'];
    var headerTokens = Tokens.create(2 * headerColumns.length);
    var header = new TextTable(state.data, 'header', headerColumns, headerTokens);
    block.addTable(header);
    handleTitleString(state, headerTokens);
    handleNumberOfAtoms(state, headerTokens);
    handleAtoms(state, block);
    handleBoxVectors(state, headerTokens);
    return result(file);
}
function parse(data) {
    return parseInternal(data);
}

export { parse as groReader };
//# sourceMappingURL=molio.esm.js.map