import { ALIAS, TEXT, VECTOR } from '@based/schema/def';
import { EQUAL, EQUAL_CRC32, HAS, HAS_TO_LOWER_CASE, LIKE, MODE_DEFAULT_VAR, MODE_OR_VAR, getVectorFn, } from './types.js';
import { createFixedFilterBuffer } from './createFixedFilterBuffer.js';
import { crc32 } from '../../crc32.js';
import { ENCODER, concatUint8Arr } from '@saulx/utils';
const DEFAULT_SCORE = new Uint8Array(new Float32Array([0.5]).buffer);
const parseValue = (value, prop, ctx, lang) => {
    if (prop.transform) {
        value = prop.transform('filter', value);
    }
    if (ctx.operation === HAS_TO_LOWER_CASE && typeof value === 'string') {
        value = value.toLowerCase();
    }
    if (ctx.operation === LIKE && prop.typeIndex === VECTOR) {
        if (!(value instanceof ArrayBuffer)) {
            throw new Error('Vector should be an arrayBuffer');
        }
        const vector = new Uint8Array(value);
        let fn = new Uint8Array([getVectorFn(ctx.opts.fn)]);
        const score = ctx.opts.score
            ? new Uint8Array(new Float32Array([ctx.opts.score]).buffer)
            : DEFAULT_SCORE;
        const buf = new Uint8Array(vector.byteLength + fn.byteLength + score.byteLength);
        let off = 0;
        off += (buf.set(new Uint8Array(vector), off), vector.byteLength);
        off += (buf.set(fn, off), fn.byteLength);
        buf.set(score, off);
        value = buf;
    }
    if (value instanceof Uint8Array ||
        typeof value === 'string' ||
        !prop.separate ||
        ctx.operation !== EQUAL) {
        if (typeof value === 'string') {
            value = ENCODER.encode(value.normalize('NFKD'));
        }
        if (prop.typeIndex === TEXT) {
            // 1 + size
            const fallbacksSize = lang.lang === 0 ? 0 : lang.fallback.length;
            const tmp = new Uint8Array(value.byteLength + 2 + fallbacksSize);
            tmp.set(value);
            tmp[tmp.byteLength - 1] = fallbacksSize;
            tmp[tmp.byteLength - 2] = lang.lang;
            for (let i = 0; i < fallbacksSize; i++) {
                tmp[tmp.byteLength - 2 - fallbacksSize + i] = lang.fallback[i];
            }
            value = tmp;
        }
    }
    if (value?.BYTES_PER_ELEMENT > 1) {
        value = value.buffer;
    }
    if (!(value instanceof Uint8Array || value instanceof ArrayBuffer)) {
        throw new Error(`Incorrect value for filter: ${prop.path}`);
    }
    if (ctx.operation === LIKE && prop.typeIndex !== VECTOR) {
        const tmp = new Uint8Array(value.byteLength + 1);
        tmp.set(value instanceof ArrayBuffer ? new Uint8Array(value) : value);
        tmp[tmp.byteLength - 1] = ctx.opts.score ?? 2;
        value = tmp;
    }
    return value;
};
export const createVariableFilterBuffer = (value, prop, ctx, lang) => {
    let mode = MODE_DEFAULT_VAR;
    let val;
    let parsedCondition;
    if (Array.isArray(value)) {
        if (ctx.operation !== EQUAL || !prop.separate) {
            mode = MODE_OR_VAR;
            const x = [];
            for (const v of value) {
                const a = parseValue(v, prop, ctx, lang);
                const size = new Uint8Array(2);
                size[0] = a.byteLength;
                size[1] = a.byteLength >>> 8;
                x.push(size, a);
            }
            val = concatUint8Arr(x);
        }
        else {
            const x = [];
            for (const v of value) {
                x.push(parseValue(v, prop, ctx, lang));
            }
            val = x;
        }
    }
    else {
        val = parseValue(value, prop, ctx, lang);
    }
    if (ctx.operation === EQUAL ||
        ctx.operation === HAS ||
        ctx.operation === LIKE ||
        ctx.operation === HAS_TO_LOWER_CASE) {
        if (prop.separate) {
            if (ctx.operation === EQUAL &&
                prop.typeIndex !== ALIAS &&
                prop.typeIndex !== VECTOR) {
                if (prop.typeIndex === TEXT) {
                    const fbLen = 2 + val[val.byteLength - 1];
                    const crc = crc32(val.slice(0, -fbLen));
                    const len = val.byteLength - fbLen;
                    const v = new Uint8Array(8 + fbLen);
                    v[0] = crc;
                    v[1] = crc >>> 8;
                    v[2] = crc >>> 16;
                    v[3] = crc >>> 24;
                    v[4] = len;
                    v[5] = len >>> 8;
                    v[6] = len >>> 16;
                    v[7] = len >>> 24;
                    for (let i = 0; i < fbLen; i++) {
                        v[v.byteLength - (i + 1)] = val[val.byteLength - (i + 1)];
                    }
                    parsedCondition = writeVarFilter(mode, v, ctx, prop, 0, 0);
                }
                else {
                    parsedCondition = createFixedFilterBuffer(prop, 8, {
                        operation: EQUAL_CRC32,
                        type: ctx.type,
                        opts: ctx.opts,
                    }, val, false);
                }
            }
            else {
                if (val instanceof ArrayBuffer) {
                    val = new Uint8Array(val);
                }
                parsedCondition = writeVarFilter(mode, val, ctx, prop, 0, 0);
            }
        }
        else {
            parsedCondition = writeVarFilter(mode, val, ctx, prop, prop.start, prop.len);
        }
    }
    return parsedCondition;
};
function writeVarFilter(mode, val, ctx, prop, start, len) {
    const size = val.byteLength;
    const buf = new Uint8Array(12 + size);
    buf[0] = ctx.type;
    buf[1] = mode;
    buf[2] = prop.typeIndex;
    buf[3] = start;
    buf[4] = start >>> 8;
    buf[5] = len;
    buf[6] = len >>> 8;
    buf[7] = size;
    buf[8] = size >>> 8;
    buf[9] = size >>> 16;
    buf[10] = size >>> 24;
    buf[11] = ctx.operation;
    // need to pas LANG FROM QUERY
    // need to set on 12 if TEXT
    buf.set(val, 12);
    return buf;
}
//# sourceMappingURL=createVariableFilterBuffer.js.map