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';

var CONFIGURE_MASK = 0x80000000;
var DIRECT_ACCESS_GPIO = 0x40;
var DIRECT_ACCESS_GPIO_FORMAT = (DIRECT_ACCESS_GPIO | VariableType.Data.INT8);

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

var 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" }
];

var ASCENDING_SORT_NUMBER = function (a, b) { return a - b; };
var ɵ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) {
    if (configure === void 0) { configure = false; }
    indexes = indexes.sort(ASCENDING_SORT_NUMBER);
    var firstIndex = indexes[0];
    var lastIndex = indexes[indexes.length - 1];
    var length = lastIndex - firstIndex + 1;
    return {
        address: (configure ? CONFIGURE_MASK : 0) | firstIndex,
        format: DIRECT_ACCESS_GPIO_FORMAT,
        length: length
    };
}
function encodeMultiIndexData(indexValues) {
    var indexes = indexValues.map(function (v) { return v.index; }).sort(ASCENDING_SORT_NUMBER);
    var firstIndex = indexes[0];
    var data = [];
    var lastIndex = indexes[indexes.length - 1];
    var _loop_1 = function (pinIndex, loopIndex) {
        var indexValue = indexValues.find(function (v) { return v.index == pinIndex; });
        if (indexValue) {
            data[loopIndex] = indexValue.value === true
                ? 1
                : indexValue.value === false ? 0 : indexValue.value;
        }
        else {
            data[loopIndex] = 0xFF;
        }
    };
    for (var pinIndex = firstIndex, loopIndex = 0; pinIndex <= lastIndex; pinIndex++, loopIndex++) {
        _loop_1(pinIndex, loopIndex);
    }
    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());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var GPIOManager = /** @class */ (function () {
    function GPIOManager(tap) {
        this.tap = tap;
        this._cache = {};
    }
    GPIOManager.prototype.getPinByIndex = function (index) {
        if (!(index in this._cache)) {
            this._cache[index] = new GPIOPin(this.tap, index);
        }
        return this._cache[index];
    };
    /**
     * TODO min api version ?
     * @param options
     */
    GPIOManager.prototype.readValues = function (indexs) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                return [2 /*return*/, this._readMuliple(indexs || GPIO_MAPPING.map(function (pin) { return pin.index; }), false)];
            });
        });
    };
    /**
     * TODO min api version ?
     * @param options
     */
    GPIOManager.prototype.readConfig = function (indexs) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                return [2 /*return*/, this._readMuliple(indexs || GPIO_MAPPING.map(function (pin) { return pin.index; }), true)];
            });
        });
    };
    GPIOManager.prototype._readMuliple = function (indexs, config) {
        return __awaiter(this, void 0, void 0, function () {
            var memoryInfo, results, formwattedResult;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        memoryInfo = encodeMultiAddressInfo(indexs, config);
                        return [4 /*yield*/, this.tap.service.target.readAddress(memoryInfo)];
                    case 1:
                        results = (_a.sent()).body();
                        formwattedResult = Array.from(results).map(function (v, index) {
                            return {
                                index: memoryInfo.address + index,
                                value: (config ? v : !!v)
                            };
                        });
                        return [2 /*return*/, formwattedResult];
                }
            });
        });
    };
    GPIOManager.prototype.writeValues = function (indexValues) {
        return __awaiter(this, void 0, void 0, function () {
            var indexs, memoryInfo, encodedData;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (typeof indexValues === 'boolean') {
                            indexValues = GPIO_MAPPING.map(function (pin) { return ({
                                index: pin.index,
                                value: indexValues
                            }); });
                        }
                        indexs = indexValues.map(function (info) { return info.index; });
                        memoryInfo = encodeMultiAddressInfo(indexs);
                        encodedData = encodeMultiIndexData(indexValues);
                        return [4 /*yield*/, this.tap.service.target.writeAddress({
                                data: encodedData,
                                info: memoryInfo
                            })];
                    case 1:
                        (_a.sent()).successful();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * TODO min api version ?
     * @param options
     */
    GPIOManager.prototype.writeConfig = function (indexValues) {
        return __awaiter(this, void 0, void 0, function () {
            var indexs, memoryInfo, encodedData;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (typeof indexValues === 'number') {
                            indexValues = GPIO_MAPPING.map(function (pin) { return ({
                                index: pin.index,
                                value: indexValues
                            }); });
                        }
                        indexs = indexValues.map(function (info) { return info.index; });
                        memoryInfo = encodeMultiAddressInfo(indexs, true);
                        encodedData = encodeMultiIndexData(indexValues);
                        return [4 /*yield*/, this.tap.service.target.writeAddress({
                                data: encodedData,
                                info: memoryInfo
                            })];
                    case 1:
                        (_a.sent()).successful();
                        return [2 /*return*/];
                }
            });
        });
    };
    return GPIOManager;
}());
function pinIndexToConfigureAddress(index) {
    return (CONFIGURE_MASK | index) >>> 0;
}
var GPIOPin = /** @class */ (function () {
    function GPIOPin(tap, index) {
        this.tap = tap;
        this.index = index;
    }
    GPIOPin.prototype.readConfig = function () {
        return __awaiter(this, void 0, void 0, function () {
            var body;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        debug("Read config for pin=" + this.index + "...");
                        return [4 /*yield*/, this.tap.service.target.readAddress({
                                address: pinIndexToConfigureAddress(this.index),
                                format: DIRECT_ACCESS_GPIO_FORMAT,
                                length: 1
                            })];
                    case 1:
                        body = (_a.sent()).body();
                        // TODO check body.length >= 1
                        return [2 /*return*/, body[0]];
                }
            });
        });
    };
    GPIOPin.prototype.writeConfig = function (options) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!(options.mode in GPIOMode)) {
                            throw new Error("Unknown mode GPIO mode " + options.mode);
                        }
                        debug("writeConfig on pin=" + this.index, GPIOMode[options.mode]);
                        return [4 /*yield*/, this.tap.service.target.writeAddress({
                                data: Uint8Array.from([options.mode]),
                                info: {
                                    address: pinIndexToConfigureAddress(this.index),
                                    format: DIRECT_ACCESS_GPIO_FORMAT,
                                    length: 1
                                }
                            })];
                    case 1:
                        (_a.sent()).successful();
                        return [2 /*return*/];
                }
            });
        });
    };
    GPIOPin.prototype.readValue = function () {
        return __awaiter(this, void 0, void 0, function () {
            var rawBody, value;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.tap.service.target.readAddress({
                            address: this.index,
                            format: DIRECT_ACCESS_GPIO_FORMAT,
                            length: 1
                        })];
                    case 1:
                        rawBody = (_a.sent()).body();
                        if (rawBody.length !== 1) {
                            throw new Error("Unexpected Tap response for read Address. Should be 1 byte but received " + rawBody.length + " bytes");
                        }
                        value = !!rawBody[0];
                        debug("Read value for pin=" + this.index + " ==> " + value);
                        return [2 /*return*/, value];
                }
            });
        });
    };
    GPIOPin.prototype.writeValue = function (value) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        debug("Write value on pin=" + this.index + "; " + value);
                        return [4 /*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
                                }
                            })];
                    case 1: return [2 /*return*/, (_a.sent()).body()];
                }
            });
        });
    };
    return GPIOPin;
}());

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

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