import { VariableType } from '@iotize/tap/service/impl/variable';
import '@iotize/tap/service/impl/target';
import { GPIOMode } from '@iotize/tap/service/impl/gpio';
import { createDebugger } from '@iotize/common/debug';

const CONFIGURE_MASK = 0x80000000;
const DIRECT_ACCESS_GPIO = 0x40;
const DIRECT_ACCESS_GPIO_FORMAT = DIRECT_ACCESS_GPIO | VariableType.Data.INT8;

const prefix = '@iotize/tap/gpio';
const debug = createDebugger(prefix);

const GPIO_MAPPING = [
    { index: 0, name: 'RST', connector: 'Target.10' },
    { index: 1, name: 'TDI', connector: 'Target.8' },
    { index: 2, name: 'TDO', connector: 'Target.6' },
    { index: 3, name: 'SWDCK', connector: 'Target.4' },
    { index: 4, name: 'SWDIO', connector: 'Target.2' },
    { index: 5, name: 'SCL_FACTORYRST', connector: '' },
    { index: 6, name: 'EXT_A2', connector: 'PA1.2' },
    { index: 7, name: 'EXT_A3', connector: 'PA1.3' },
    { index: 8, name: 'EXT_A4', connector: 'PA1.4' },
    { index: 9, name: 'EXT_A5', connector: 'PA1.5' },
    { index: 10, name: 'EXT_A6', connector: 'PA1.6' },
    { index: 11, name: 'EXT_A7', connector: 'PA1.7' },
    { index: 12, name: 'EXT_A8', connector: 'PA1.8' },
    { index: 13, name: 'EXT_A9', connector: 'PA1.9' },
    { index: 14, name: 'EXT_A10', connector: 'PA1.10' },
    { index: 15, name: 'EXT_A11', connector: 'PA1.11' },
    { index: 16, name: 'EXT_B2', connector: 'PA2.2' },
    { index: 17, name: 'EXT_B3', connector: 'PA2.3' },
    { index: 18, name: 'EXT_B4', connector: 'PA2.4' },
    { index: 19, name: 'EXT_B5', connector: 'PA2.5' },
    { index: 20, name: 'EXT_B6', connector: 'PA2.6' },
    { index: 21, name: 'EXT_B7', connector: 'PA2.7' },
    { index: 22, name: 'EXT_B8', connector: 'PA2.8' },
    { index: 23, name: 'EXT_B9', connector: 'PA2.9' },
    { index: 24, name: 'EXT_B10', connector: 'PA2.10' },
    { index: 25, name: 'EXT_B11', connector: 'PA2.11' },
];

const ASCENDING_SORT_NUMBER = (a, b) => a - b;
const ɵ0 = ASCENDING_SORT_NUMBER;
// export function encodeGPIOWriteConfig(options: GPIOWriteConfig) {
//     if (options.indexs.length === 0) {
//         throw new Error(`Invalid GPIO config. You must give at least one pin index`);
//     }
//     const indexes = options.indexs.sort();
//     const data = encodeMultiGPIOData(indexes, options.mode);
//     return {
//         data,
//         info: encodeMultiAddressInfo(indexes, true)
//     };
// }
function encodeMultiAddressInfo(indexes, configure = false) {
    indexes = indexes.sort(ASCENDING_SORT_NUMBER);
    const firstIndex = indexes[0];
    const lastIndex = indexes[indexes.length - 1];
    const length = lastIndex - firstIndex + 1;
    return {
        address: (configure ? CONFIGURE_MASK : 0) | firstIndex,
        format: DIRECT_ACCESS_GPIO_FORMAT,
        length,
    };
}
function encodeMultiIndexData(indexValues) {
    const indexes = indexValues.map((v) => v.index).sort(ASCENDING_SORT_NUMBER);
    const firstIndex = indexes[0];
    const data = [];
    const lastIndex = indexes[indexes.length - 1];
    for (let pinIndex = firstIndex, loopIndex = 0; pinIndex <= lastIndex; pinIndex++, loopIndex++) {
        const indexValue = indexValues.find((v) => v.index == pinIndex);
        if (indexValue) {
            data[loopIndex] =
                indexValue.value === true
                    ? 1
                    : indexValue.value === false
                        ? 0
                        : indexValue.value;
        }
        else {
            data[loopIndex] = 0xff;
        }
    }
    return Uint8Array.from(data);
}
// export function encodeMultiGPIOData(indexes: number[], value: number): Uint8Array {
//     indexes = indexes.sort(ASCENDING_SORT_NUMBER);
//     const firstIndex = indexes[0];
//     const data: number[] = [];
//     const lastIndex = indexes[indexes.length - 1];
//     for (let pinIndex = firstIndex, loopIndex = 0; pinIndex <= lastIndex; pinIndex++, loopIndex++) {
//         if (indexes.indexOf(pinIndex) >= 0) {
//             data[loopIndex] = value;
//         }
//         else {
//             data[loopIndex] = 0xFF;
//         }
//     }
//     return Uint8Array.from(data);
// }

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
class GPIOManager {
    constructor(tap) {
        this.tap = tap;
        this._cache = {};
    }
    getPinByIndex(index) {
        if (!(index in this._cache)) {
            this._cache[index] = new GPIOPin(this.tap, index);
        }
        return this._cache[index];
    }
    /**
     * TODO min api version ?
     * @param options
     */
    readValues(indexs) {
        return __awaiter(this, void 0, void 0, function* () {
            return this._readMuliple(indexs || GPIO_MAPPING.map((pin) => pin.index), false);
        });
    }
    /**
     * TODO min api version ?
     * @param options
     */
    readConfig(indexs) {
        return __awaiter(this, void 0, void 0, function* () {
            return this._readMuliple(indexs || GPIO_MAPPING.map((pin) => pin.index), true);
        });
    }
    _readMuliple(indexs, config) {
        return __awaiter(this, void 0, void 0, function* () {
            const memoryInfo = encodeMultiAddressInfo(indexs, config);
            const results = (yield this.tap.service.target.readAddress(memoryInfo)).body();
            const formwattedResult = Array.from(results).map((v, index) => {
                return {
                    index: memoryInfo.address + index,
                    value: (config ? v : !!v),
                };
            });
            return formwattedResult;
        });
    }
    writeValues(indexValues) {
        return __awaiter(this, void 0, void 0, function* () {
            if (typeof indexValues === 'boolean') {
                indexValues = GPIO_MAPPING.map((pin) => ({
                    index: pin.index,
                    value: indexValues,
                }));
            }
            const indexs = indexValues.map((info) => info.index);
            const memoryInfo = encodeMultiAddressInfo(indexs);
            const encodedData = encodeMultiIndexData(indexValues);
            (yield this.tap.service.target.writeAddress({
                data: encodedData,
                info: memoryInfo,
            })).successful();
        });
    }
    /**
     * TODO min api version ?
     * @param options
     */
    writeConfig(indexValues) {
        return __awaiter(this, void 0, void 0, function* () {
            if (typeof indexValues === 'number') {
                indexValues = GPIO_MAPPING.map((pin) => ({
                    index: pin.index,
                    value: indexValues,
                }));
            }
            const indexs = indexValues.map((info) => info.index);
            const memoryInfo = encodeMultiAddressInfo(indexs, true);
            const encodedData = encodeMultiIndexData(indexValues);
            (yield this.tap.service.target.writeAddress({
                data: encodedData,
                info: memoryInfo,
            })).successful();
        });
    }
}
function pinIndexToConfigureAddress(index) {
    return (CONFIGURE_MASK | index) >>> 0;
}
class GPIOPin {
    constructor(tap, index) {
        this.tap = tap;
        this.index = index;
    }
    readConfig() {
        return __awaiter(this, void 0, void 0, function* () {
            debug(`Read config for pin=${this.index}...`);
            const body = (yield this.tap.service.target.readAddress({
                address: pinIndexToConfigureAddress(this.index),
                format: DIRECT_ACCESS_GPIO_FORMAT,
                length: 1,
            })).body();
            // TODO check body.length >= 1
            return body[0];
        });
    }
    writeConfig(options) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!(options.mode in GPIOMode)) {
                throw new Error(`Unknown mode GPIO mode ${options.mode}`);
            }
            debug(`writeConfig on pin=${this.index}`, GPIOMode[options.mode]);
            (yield this.tap.service.target.writeAddress({
                data: Uint8Array.from([options.mode]),
                info: {
                    address: pinIndexToConfigureAddress(this.index),
                    format: DIRECT_ACCESS_GPIO_FORMAT,
                    length: 1,
                },
            })).successful();
        });
    }
    readValue() {
        return __awaiter(this, void 0, void 0, function* () {
            const rawBody = (yield this.tap.service.target.readAddress({
                address: this.index,
                format: DIRECT_ACCESS_GPIO_FORMAT,
                length: 1,
            })).body();
            if (rawBody.length !== 1) {
                throw new Error(`Unexpected Tap response for read Address. Should be 1 byte but received ${rawBody.length} bytes`);
            }
            const value = !!rawBody[0];
            debug(`Read value for pin=${this.index} ==> ${value}`);
            return value;
        });
    }
    writeValue(value) {
        return __awaiter(this, void 0, void 0, function* () {
            debug(`Write value on pin=${this.index}; ${value}`);
            return (yield this.tap.service.target.writeAddress({
                data: value ? Uint8Array.from([1]) : Uint8Array.from([0]),
                info: {
                    address: this.index,
                    format: DIRECT_ACCESS_GPIO_FORMAT,
                    length: 1,
                },
            })).body();
        });
    }
}

/**
 * Generated bundle index. Do not edit.
 */

export { CONFIGURE_MASK, DIRECT_ACCESS_GPIO_FORMAT, GPIOManager, GPIOPin, GPIO_MAPPING };
//# sourceMappingURL=iotize-tap-gpio.js.map
