import { assert } from "../../assert.js";
import { FunctionCompiler } from "../../function/FunctionCompiler.js";
import { BinaryDataType } from "../type/BinaryDataType.js";
import { DataTypeByteSizes } from "../type/DataTypeByteSizes.js";
import { DataType2DataViewReaders } from "./DataType2DataViewReaders.js";
import { DataType2DataViewWriters } from "./DataType2DataViewWriters.js";

/**
 * @param {Object} param
 * @param {Object<BinaryDataType>} param.schema
 * @param {Object} param.target
 * @param {string} [param.data_variable_name]
 * @param {string} [param.offset_variable_name]
 * @param {boolean} [param.little_endian]
 * @return {number}
 */
export function buildAccessor({
                                  schema,
                                  target,
                                  data_variable_name = "this.$data",
                                  offset_variable_name = "this.$offset",
                                  little_endian = false
                              }) {

    assert.defined(schema, 'schema');
    assert.isBoolean(little_endian, 'little_endian');

    assert.isString(data_variable_name, 'data_variable_name')
    assert.isString(offset_variable_name, 'offset_variable_name')

    assert.defined(target, 'target');
    assert.notNull(target, 'target');

    const props = Object.keys(schema);
    const property_count = props.length;

    let offset = 0;
    for (let i = 0; i < property_count; i++) {
        const property = props[i];
        const type = schema[property];

        assert.enum(type, BinaryDataType, "type");

        assert.arrayHasNo(["bind", "$data", "$offset", "this", "constructor", "get", "set"], property, `Illegal property name ${property}`);

        //
        Object.defineProperty(target, property, {
            get: FunctionCompiler.INSTANCE.compile({
                code: `return ${data_variable_name}.${DataType2DataViewReaders[type]}(${offset_variable_name} + ${offset}, ${little_endian})`
            }),
            set: FunctionCompiler.INSTANCE.compile({
                args: ["value"],
                code: `${data_variable_name}.${DataType2DataViewWriters[type]}(${offset_variable_name} + ${offset}, value, ${little_endian})`
            })
        })

        offset += DataTypeByteSizes[type];
    }

    return offset;
}