import { assert } from "../../../assert.js";
import { BinaryDataType } from "../../../binary/type/BinaryDataType.js";
import { validate_enum_schema } from "../../../model/validate_enum_schema.js";
import { RowFirstTableSpec } from "../RowFirstTableSpec.js";

export class TableRecord {

    /**
     *
     * @type {Object<number>}
     */
    #key_index = {};

    /**
     * @type {Object<BinaryDataType>}
     */
    #schema;

    /**
     * @type {RowFirstTable}
     */
    #table;
    #row_index = 0;

    #value = new Proxy(this, {
        get: (target, property) => {
            // resolve property to index
            const keyIndex = this.#key_index[property];

            if (keyIndex === undefined) {
                throw new Error(`Property '${property}' is not part of this schema`);
            }

            return this.#table.readCellValue(this.#row_index, keyIndex);
        },
        set: (target, property, value) => {

            // resolve property to index
            const keyIndex = this.#key_index[property];

            if (keyIndex === undefined) {
                throw new Error(`Property '${property}' is not part of this schema`);
            }

            this.#table.writeCellValue(this.#row_index, keyIndex, value);
            return true;
        }
    })

    get value() {
        return this.#value;
    }

    set value(object) {
        this.set(object)
    }


    set(object) {
        for (const key in object) {
            this.#value[key] = object[key];
        }
    }

    get() {
        return this.#value;
    }

    /**
     *
     * @param {Object} schema
     */
    constructor(schema) {
        assert.notNull(schema, 'schema');
        assert.defined(schema, 'schema');

        assert.ok(validate_enum_schema(schema, BinaryDataType, console.warn), 'invalid schema');

        this.#schema = schema;
        const keys = Object.keys(schema);

        for (let i = 0; i < keys.length; i++) {
            this.#key_index[keys[i]] = i;
        }
    }

    get table_schema() {
        return RowFirstTableSpec.get(Object.values(this.#schema));
    }

    /**
     *
     * @param {RowFirstTable} table
     * @param {number} row_index
     */
    bind(table, row_index) {
        this.#table = table;
        this.#row_index = row_index;
    }
}