/* IMPORT */
import detokenize from './detokenize.js';
import parse from './parse.js';
import tokenize from './tokenize/index.js';
import Utils from './utils.js';
/* MAIN */
//FIXME: This is wonky, it should be much more robust, it should probably be rewritten from scratch
const getLookupToken = (ast, position) => {
    let tokenPosition = null;
    let offsetCurrent = 0;
    const checkPositionToken = (token) => {
        if (token.start > position)
            return;
        if (token.end <= (position - 1))
            return;
        if (tokenPosition && Utils.isTokenLiteral(tokenPosition))
            return;
        if (tokenPosition && Utils.isTokenIgnored(token))
            return;
        tokenPosition = token;
    };
    const parseChild = (token, parent, depth, index) => {
        const { type, source } = token;
        const start = offsetCurrent;
        const end = (offsetCurrent += source.length);
        const ltoken = { type, source, token, parent, depth, index, start, end };
        checkPositionToken(ltoken);
        return ltoken;
    };
    const parseParent = (token, parent, depth, index) => {
        const { type } = token;
        const ltoken = { type, children: [], token, parent, depth, index };
        ltoken.children = token.children.map((child, index) => parseToken(child, ltoken, depth + 1, index)).filter(Utils.isTokenLiteral);
        ltoken.children.forEach((token, index) => token.index = index);
        return ltoken;
    };
    const parseToken = (token, parent, depth, index) => {
        if ('children' in token)
            return parseParent(token, parent, depth, index);
        return parseChild(token, parent, depth, index);
    };
    parseToken(ast, null, -1, -1);
    return tokenPosition;
};
const getLookupPath = (token) => {
    if (!token)
        return [];
    const path = [];
    while (token) {
        const parent = token.parent;
        if (!parent)
            break;
        if (Utils.isTokenLiteral(token)) {
            if (parent.type === 'Object') {
                if (Utils.isEven(token.index)) {
                    path.unshift(JSON.parse(token.source));
                }
                else {
                    path.unshift(JSON.parse(parent.children[token.index - 1].source));
                }
            }
            else if (parent.type === 'Array') {
                path.unshift(token.index);
            }
        }
        token = parent;
    }
    return path;
};
const getLookupIsInsideProperty = (token) => {
    if (!token)
        return false;
    const parentType = token.parent?.type;
    if (parentType === 'Object')
        return Utils.isTokenLiteral(token) ? Utils.isEven(token.index) : token.parent?.parent?.type === 'Array';
    if (parentType === 'Array')
        return Utils.isTokenLiteral(token) || token.parent?.parent?.type === 'Array';
    return false;
};
const getLookupIsInsideValue = (token) => {
    if (!token)
        return false;
    const isParentEmpty = !token.parent?.children.length;
    const parentType = token.parent?.type;
    if (parentType === 'Object')
        return isParentEmpty || Utils.isTokenDelimiter(token) || (Utils.isTokenLiteral(token) && Utils.isOdd(token.index));
    if (parentType === 'Array')
        return (token.depth > 1);
    return false;
};
const getLookupProperty = (token, isInsideProperty) => {
    if (!isInsideProperty || !token)
        return;
    const parentType = token.parent?.type;
    if (Utils.isTokenLiteral(token)) {
        if (parentType === 'Array')
            return token.index;
        return parse(detokenize(token));
    }
    else {
        if (parentType === 'Array')
            return token.parent?.index;
    }
};
const getLookupValue = (token, isInsideValue, usePartialScanning) => {
    if (!isInsideValue || !token)
        return;
    if (Utils.isTokenLiteral(token))
        return parse(detokenize(token));
    if (usePartialScanning)
        return;
    const { parent } = token;
    if (!parent || !parent.token)
        return;
    if (parent.type !== 'Array' && parent.type !== 'Object')
        return;
    return parse(detokenize(parent.token));
};
const lookup = (text, position, usePartialScanning = true) => {
    const limit = usePartialScanning ? position : Infinity;
    const ast = tokenize(text, limit);
    const token = getLookupToken(ast, position);
    const path = getLookupPath(token);
    const isInsideProperty = getLookupIsInsideProperty(token);
    const isInsideValue = getLookupIsInsideValue(token);
    const property = getLookupProperty(token, isInsideProperty);
    const value = getLookupValue(token, isInsideValue, usePartialScanning);
    const t = token ? { type: token.type, start: token.start, end: token.end, source: token.source } : undefined;
    const result = { path, property, value, token: t, isInsideProperty, isInsideValue };
    return result;
};
/* EXPORT */
export default lookup;
