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());
    });
};
import { combineLatest, Observable, Subject } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { createMonitoringTicker, MonitoringController, } from '../monitor/monitor-engine';
import { TapDataError } from '../tap-data-error';
import { ModbusAccessVariable } from '../target-variable/modbus/modbus-access-variable';
import { createTapBundleFromConfig, } from '../target-variable/tap-bundle/tap-bundle';
import { createTapVariableFromConfig, } from '../target-variable/tap-variable/tap-variable';
import { TargetMemoryVariable } from '../target-variable/target-memory/target-memory-variable';
import { createDataManagerConfigFromTapDataConfig } from './create-data-manager-config-from-tap-data-config';
import { createTapBundlesFromConfig, readDataManagerTapConfigFromTap, } from './create-data-manager-from-tap-config';
const TAG = 'DataManager';
/**
 * Typed data manager is usually build from Tap configuration
 *
 * It allow you to have compile time check for bundle/variable types.
 * However, you cannot add/remove/edit variables (as it would change types)
 * Use `DataManager` if you want this features
 */
export class TypedDataManager {
    constructor(tap, bundles, 
    // variables: Record<VariableKey, EditableDataStream<VariableByType[VariableKey]>>
    monitoring = new MonitoringController()) {
        // const variablesCache: Record<string, EditableDataStream<VariableByType[VariableKey], unknown>> = {};
        // for (const bundleKey in this.bundles) {
        //     const bundle = this.bundles[bundleKey];
        //     for (const variableKey in bundle.variables) {
        //         const variable = bundle.variables[variableKey];
        //         variablesCache[variable.config.name] = variable;
        //     }
        // }
        // this.variablesCache = variablesCache as Record<VariableKey, EditableDataStream<VariableByType[VariableKey], unknown>>;
        // this.setupStreams();
        this.tap = tap;
        this.bundles = bundles;
        this.monitoring = monitoring;
        // switchableStream!: SwitchableStream;
        // dataStream: Observable<Record<string, any>>;
        // tlvConverter: TlvBundleConverter<Record<string, Uint8Array>>;
        // sourceControllers: Record<string, DataSourceController> = {};
        // protected variablesCache: Record<VariableKey, EditableDataStream<VariableByType[VariableKey], unknown>>;
        // private tickers: Record<string, {
        //     controller: MonitoringControllerSubject,
        //     ticker: Observable<MonitoringTickerState>
        // }> = {};
        // private ticker = createMonitoringTicker(this.monitoring);
        this._events = new Subject();
        // this.variablesCache = {} as any;
        const ticker = createMonitoringTicker(this.monitoring);
        ticker.subscribe((newTick) => __awaiter(this, void 0, void 0, function* () {
            // console.log('New tick', newTick);
            yield this.refreshValues();
        }));
    }
    get values() {
        const bundles = this.listBundles();
        return combineLatest(bundles.map((v) => v.values.pipe(startWith(undefined)))).pipe(map((values) => {
            return values.reduce((accumulator, v, index) => {
                const bundleStreamId = bundles[index].id;
                accumulator[bundleStreamId] = v;
                return accumulator;
            }, {});
        }));
    }
    get events() {
        return this._events.asObservable();
    }
    listVariables() {
        return this.listBundles().reduce((acc, bundle) => {
            acc.push(...bundle.variables);
            return acc;
        }, []);
    }
    bundle(key) {
        if (!(key in this.bundles)) {
            throw TapDataError.bundleNotFound(key);
        }
        return this.bundles[key];
    }
    // TODO fix typing
    variable(key) {
        const variable = this.listVariables().find((v) => v.id === key);
        if (!variable) {
            throw TapDataError.variableNotFound(key);
        }
        return variable;
    }
    // public addBundleFromConfig(key: BundleKey, bundleConfig: BundleConfig) {
    //     // const key = bundleConfig.name || bundleConfig.id.toString();
    //     // this.bundles[key] = createTapBundleDataFromConfig(bundleConfig);
    //     throw new Error(`Not implemented yet`);
    //     return this;
    // }
    destroy() {
        this.monitoring.stop();
    }
    refreshValues() {
        return __awaiter(this, void 0, void 0, function* () {
            const values = {};
            for (const bundle of this.listBundles()) {
                values[bundle.id] = yield bundle
                    .read()
                    .catch((error) => {
                    this._events.next({
                        error,
                        type: DataManager.EventType.error,
                    });
                    return error;
                });
            }
            return values;
        });
    }
    listBundles() {
        return Object.values(this.bundles);
    }
    // public setupStreams() {
    //     // this.bundle('x').monitor.start();
    //     // this.variable('y').monitor.
    //     const variableStream = this.ticker
    //         .pipe(
    //             switchMap(() => {
    //                 return merge(
    //                     this.listVariables().map(v => defer(() => v.read()))
    //                 );
    //             })
    //         );
    //     const bundleStream = this.ticker
    //         .pipe(
    //             switchMap(() => {
    //                 return merge(
    //                     this.listBundles().map(v => defer(() => v.read()))
    //                 );
    //             })
    //         );
    //     // const profileStream = this.ticker
    //     //     .pipe(
    //     //         switchMap(async () => {
    //     //             // debug(TAG, `New call ${time} to read profile`);
    //     //             const profileData = (await this.tap.service.variable.readProfile()).body();
    //     //             const decoded = this.tlvConverter.decode(profileData);
    //     //             this.rawDataStream.next(decoded);
    //     //         })
    //     //     );
    //     this.switchableStream
    //         .addStream(
    //             'variable',
    //             variableStream
    //         )
    //         .addStream(
    //             'bundle',
    //             bundleStream
    //         )
    //     // .addStream(
    //     //     'profile',
    //     //     profileStream
    //     // );
    // }
    hasBundle(bundleKey) {
        return bundleKey in this.bundles;
    }
    createConfiguredBundle(key, config) {
        return createTapBundleFromConfig(this.tap, key, config);
    }
    createConfiguredVariable(key, config) {
        return createTapVariableFromConfig(this.tap.service.variable, key, config);
    }
    /**
     * Create a variable with direct modbus access
     * May/May not be available according to the configured target protocol on your device
     *
     * @param id
     * @param options
     */
    createModbusAccessVariable(id, options) {
        return new ModbusAccessVariable(id, {
            config: options.config,
            targetService: this.tap.service.target,
            converter: options.converter,
        });
    }
    /**
     * Create direct target access to variable
     * May/May not be available according to configured target protocol on your device
     * @param id
     * @param options
     */
    createTargetMemoryVariable(id, options) {
        return new TargetMemoryVariable(id, {
            config: options.config,
            targetService: this.tap.service.target,
            converter: options.converter,
        });
    }
}
export class DataManager extends TypedDataManager {
    constructor(tap, bundles = {}, 
    // variables: Record<VariableKey, EditableDataStream<VariableByType[VariableKey]>>
    monitoring = new MonitoringController()) {
        super(tap, bundles, monitoring);
    }
    // registerConfiguredBundle(config: BundleConfig) {
    //     throw new Error('Not implemented yet');
    // }
    // registerConfiguredVariable(config: VariableConfig) {
    //     // this.variables.push();
    //     throw new Error('Not implemented yet');
    // }
    // createTapBundle<T>(name: string, bundleId: number, variables: T): BundleDataStreamInterface<T> {
    //     return createTapBundleFromConfig(
    //         this.tap,
    //         name,
    //         {
    //             id: bundleId,
    //             variables
    //         }
    //     );
    // }
    setBundles(bundles) {
        this.bundles = bundles;
    }
    addBundle(bundle) {
        this.bundles[bundle.id] = bundle;
    }
    removeBundle(bundleKey) {
        delete this.bundles[bundleKey];
    }
    addVariable(bundleKey, variable) {
        const bundle = this.bundle(bundleKey);
        bundle.variables.push(variable); // TODO fix typing
    }
    /**
     * Register bundles configured in the Tap
     */
    registerBundles(bundlesConfig) {
        const tapBundles = createTapBundlesFromConfig(this.tap, bundlesConfig);
        this.setBundles(tapBundles);
        return tapBundles;
    }
    registerBundle(key, config) {
        const tapBundle = createTapBundleFromConfig(this.tap, key, config);
        this.addBundle(tapBundle);
    }
    clear() {
        this.bundles = {};
    }
    /**
     * Synchronize DataManager bundles and variables with the connected Tap device
     * It will the current profile accessible variables/bundles
     */
    synchronizeTapConfig() {
        return new Observable((observer) => {
            try {
                readDataManagerTapConfigFromTap(this.tap, {
                    observer,
                })
                    .then((config) => {
                    const newBundles = this.registerBundles(config);
                    observer.next({
                        step: 'done',
                        bundles: newBundles,
                    });
                    observer.complete();
                })
                    .catch((err) => {
                    observer.error(err);
                });
            }
            catch (err) {
                observer.error(err);
            }
        });
    }
    /**
     * Configure DataManager bundles and variables from a DataConfig object (found in Tap configuration files)
     * Data decoder will be dedecuced from dataType/length field.
     */
    configureWithDataConfig(config) {
        this.clear();
        this.registerBundles(createDataManagerConfigFromTapDataConfig(config).bundles);
    }
}
(function (DataManager) {
    let EventType;
    (function (EventType) {
        EventType["error"] = "error";
    })(EventType = DataManager.EventType || (DataManager.EventType = {}));
})(DataManager || (DataManager = {}));
//# sourceMappingURL=data:application/json;base64, 