const COMMAND = 0;
const NUMBER = 1;
const EOD = 2;
const PARAMS = { A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0 };
function tokenize(d) {
    const tokens = new Array();
    while (d !== '') {
        if (d.match(/^([ \t\r\n,]+)/)) {
            d = d.substr(RegExp.$1.length);
        }
        else if (d.match(/^([aAcChHlLmMqQsStTvVzZ])/)) {
            tokens[tokens.length] = { type: COMMAND, text: RegExp.$1 };
            d = d.substr(RegExp.$1.length);
        }
        else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) {
            tokens[tokens.length] = { type: NUMBER, text: `${parseFloat(RegExp.$1)}` };
            d = d.substr(RegExp.$1.length);
        }
        else {
            return [];
        }
    }
    tokens[tokens.length] = { type: EOD, text: '' };
    return tokens;
}
function isType(token, type) {
    return token.type === type;
}
export function parsePath(d) {
    const segments = [];
    const tokens = tokenize(d);
    let mode = 'BOD';
    let index = 0;
    let token = tokens[index];
    while (!isType(token, EOD)) {
        let paramsCount = 0;
        const params = [];
        if (mode === 'BOD') {
            if (token.text === 'M' || token.text === 'm') {
                index++;
                paramsCount = PARAMS[token.text];
                mode = token.text;
            }
            else {
                return parsePath('M0,0' + d);
            }
        }
        else if (isType(token, NUMBER)) {
            paramsCount = PARAMS[mode];
        }
        else {
            index++;
            paramsCount = PARAMS[token.text];
            mode = token.text;
        }
        if ((index + paramsCount) < tokens.length) {
            for (let i = index; i < index + paramsCount; i++) {
                const numbeToken = tokens[i];
                if (isType(numbeToken, NUMBER)) {
                    params[params.length] = +numbeToken.text;
                }
                else {
                    throw new Error('Param not a number: ' + mode + ',' + numbeToken.text);
                }
            }
            if (typeof PARAMS[mode] === 'number') {
                const segment = { key: mode, data: params };
                segments.push(segment);
                index += paramsCount;
                token = tokens[index];
                if (mode === 'M')
                    mode = 'L';
                if (mode === 'm')
                    mode = 'l';
            }
            else {
                throw new Error('Bad segment: ' + mode);
            }
        }
        else {
            throw new Error('Path data ended short');
        }
    }
    return segments;
}
export function serialize(segments) {
    const tokens = [];
    for (const { key, data } of segments) {
        tokens.push(key);
        switch (key) {
            case 'C':
            case 'c':
                tokens.push(data[0], `${data[1]},`, data[2], `${data[3]},`, data[4], data[5]);
                break;
            case 'S':
            case 's':
            case 'Q':
            case 'q':
                tokens.push(data[0], `${data[1]},`, data[2], data[3]);
                break;
            default:
                tokens.push(...data);
                break;
        }
    }
    return tokens.join(' ');
}
