import { NgModule, Injectable, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { __awaiter } from 'tslib';
import '@iotize/tap-scripts/ext';
import { bufferToHexString, bufferToAsciiString } from '@iotize/common/byte-converter';
import { GlobalEventsService } from '@iotize/ionic';
import { GrammarHelper, ScriptRunner } from '@iotize/tap-scripts';
import { Subject } from 'rxjs';
import { share } from 'rxjs/operators';

/**
 * @fileoverview added by tsickle
 * Generated from: lib/tap-script.module.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
class TapScriptModule {
}
TapScriptModule.decorators = [
    { type: NgModule, args: [{
                declarations: [],
                imports: [
                    CommonModule
                ]
            },] }
];

/**
 * @fileoverview added by tsickle
 * Generated from: lib/tap-script-runner.service.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
/**
 * @record
 */
function ScriptOutputType() { }
if (false) {
    /** @type {?} */
    ScriptOutputType.prototype.level;
    /** @type {?} */
    ScriptOutputType.prototype.message;
}
class TapScriptRunnerService {
    /**
     * @param {?} events
     */
    constructor(events) {
        this.events = events;
        this.scriptTypes = {
            NORMAL: 1,
            CONFIGURATION: 2
        };
        this._outputMessages = [];
        this._errors = new Subject();
        this.output = new Subject();
        /**
         * The ScriptRunner object is given options with what is done with values retrieved by the script
         * The output property defines the actions to perform on data retrieving.
         * The display property defines how to log informations given back by the scriptRunner.
         * @see {ScriptRunner}
         */
        this.scriptRunnerOptions = {
            output: (/**
             * @param {?=} response
             * @return {?}
             */
            (response) => {
                /**
                 * Waiting for ScriptRunner update, display tweak
                 * If response is 1 or 2 bytes or contains 0, displays it as an hex string
                 * else displays it as an ASCII string
                 */
                if (response) {
                    /** @type {?} */
                    let responseDisplayed;
                    /** @type {?} */
                    const responseBody = response.body();
                    if (responseBody.length <= 2 || responseBody.includes(0)) {
                        responseDisplayed = bufferToHexString(responseBody);
                    }
                    else {
                        responseDisplayed = bufferToAsciiString(responseBody);
                    }
                    this.addOutput('info', `${responseDisplayed}`);
                }
            }),
            display: (/**
             * @param {?} level
             * @param {?} content
             * @return {?}
             */
            (level, content) => {
                console.log(`[${level}] : ${content}`);
                if (level == 'message') {
                    this.addOutput('info', `${content}`);
                    return;
                }
                this.addOutput(level, content);
            })
        };
        this.scriptFile = {
            name: '',
            scriptString: ''
        };
        this.scriptType = this.scriptTypes.NORMAL;
    }
    /**
     * @return {?}
     */
    get isRunning() {
        return this.scriptRunner !== undefined && this.scriptRunner.isRunning();
    }
    /**
     * @return {?}
     */
    get hasScript() {
        return this.scriptFile.scriptString !== '';
    }
    /**
     * Converts script content, read as a string, into a list of instructions
     * @param {?} scriptString content of the loaded script file
     * @return {?}
     */
    getProgInstructions(scriptString) {
        return GrammarHelper.toProgInstructions(scriptString);
    }
    /**
     * @param {?} instructions
     * @param {?} tap
     * @return {?}
     */
    runScript(instructions, tap) {
        this._outputMessages = [];
        return this.executeScriptFromInstructions(tap, instructions);
    }
    /**
     * @return {?}
     */
    stopScript() {
        // TODO
    }
    /**
     * @return {?}
     */
    get errors() {
        return this._errors;
    }
    /**
     * Reads and executes the given instruction list.
     * @param {?} tap
     * @param {?} instructions
     * @return {?}
     */
    executeScriptFromInstructions(tap, instructions) {
        this._outputMessages = [];
        /** @type {?} */
        let checkErrorCount = 0;
        /** @type {?} */
        const scriptRunner = this.scriptRunner = new ScriptRunner(this.scriptRunnerOptions);
        scriptRunner.errorHandler = (/**
         * @param {?} error
         * @return {?}
         */
        (error) => {
            // this.addOutput('error', error.message);
            this._errors.next(error);
            // console.error(error);
            if (/Check instruction/.test(error.message)) {
                checkErrorCount++;
            }
        });
        return scriptRunner.run(instructions, tap)
            .pipe(
        // rxTap({
        //   next: (event: ScriptRunnerEvent) => {
        //     console.warn(event);
        //     if (event.type == 'after') {
        //       if ('cmd' in event.instruction) {
        //         // this.addOutput('verbose', `Command ${event.instruction['cmd'].toString()}`);
        //       }
        //     }
        //   },
        //   complete: () => {
        //     console.log("Script execution: DONE");
        //     // this.addOutput('info', '\n');
        //     // this.addOutput('info', '--------Script execution: DONE--------');
        //     if (checkErrorCount > 0) {
        //       // this.addOutput('error', `Failed Check instructions: ${checkErrorCount}`);
        //     }
        //     // this.isRunning = false;
        //   },
        //   error: (error) => {
        //     console.error(error);
        //     // this.isRunning = false;
        //   }
        // }),
        share());
    }
    /**
     * @return {?}
     */
    stop() {
        if (this.scriptRunner) {
            this.scriptRunner.stop();
        }
    }
    /**
     * @param {?} level
     * @param {?} message
     * @return {?}
     */
    addOutput(level, message) {
        console.info('SCRIPT', level, message);
        this._outputMessages.push({
            level: level,
            message: message
        });
        this.output.next(this._outputMessages);
    }
    /**
     * Converts, reads and executes instructions from the raw content of a script file.
     * In the configuration routine, script execution is preceded by:
     * - Log as admin
     * - Factory reset
     * - Wait time
     * And succeeded with
     * - Wait time
     * - Configuration reload
     * - Wait time
     *
     * The configuration routine has succeeded if all these steps did.
     *
     * TODO remove as it's in library
     *
     * @param {?} scriptString
     * @param {?} tap
     * @return {?}
     */
    executeConfigurationScriptRoutine(scriptString, tap) {
        return __awaiter(this, void 0, void 0, function* () {
            /** @type {?} */
            const waitingTime = 400;
            /** @type {?} */
            const instructions = this.getProgInstructions(scriptString);
            try {
                console.log('@@@@@@@@@ Logging as admin @@@@@@@@@');
                // TODO only if not already logged in as an admin
                yield tap.auth.login({
                    username: 'admin',
                    password: 'admin'
                });
                console.log('@@@@@@@@@  factory reset @@@@@@@@@');
                this.events.publish('configuration', 'factory reset');
                /** @type {?} */
                const factoryResetResponse = tap.service.device.factoryReset();
                /** @type {?} */
                const result = (yield factoryResetResponse).body();
                console.log(`factoryReset : ${result}`);
            }
            catch (error) {
                console.error(error);
                throw new Error('factory reset failed');
            }
            this.events.publish('configuration', 'executing script');
            console.log('@@@@@@@@@  Wait after factory reset, before script execution @@@@@@@@@');
            yield this.sleep(waitingTime);
            try {
                yield this.executeScriptFromInstructions(tap, instructions);
            }
            catch (error) {
                console.error(error);
                throw new Error('Script execution failed');
            }
            try {
                console.log('@@@@@@@@@  Wait after script execution, before reloading config @@@@@@@@@');
                //  5. wait 400ms
                yield this.sleep(waitingTime);
                //  6. reload config. Custom call to post /1024//10 while reloadConfig method is not implemented
                /** @type {?} */
                const reloadResponse = tap.service.interface.reloadConfig();
                console.log('Waiting for reloadResponse');
                yield reloadResponse;
            }
            catch (error) {
                console.error(error);
                throw new Error('Reloading config failed');
            }
            //  7. wait 400ms
            yield this.sleep(waitingTime);
            console.log('@@@@@@@@@ configuration complete @@@@@@@@@');
            this.events.publish('configuration', 'configuration completed');
        });
    }
    /**
     * Executes currently loaded script, if no script is running, using the routine defined by the scriptType property
     * @param {?} tap
     * @return {?}
     */
    executeLoadedScript(tap) {
        if (this.isRunning) {
            console.error('Another script is running');
            return;
        }
        if (this.scriptType === this.scriptTypes.CONFIGURATION) {
            return this.executeConfigurationScriptRoutine(this.scriptFile.scriptString, tap);
        }
        if (this.scriptType === this.scriptTypes.NORMAL) {
            return this.executeScriptFromInstructions(tap, this.getProgInstructions(this.scriptFile.scriptString));
        }
    }
    /**
     * Returns a promise that resolve after given time in milliseconds
     * @param {?} milliseconds
     * @return {?}
     */
    sleep(milliseconds) {
        return new Promise((/**
         * @param {?} resolve
         * @return {?}
         */
        (resolve) => {
            setTimeout((/**
             * @return {?}
             */
            () => resolve()), milliseconds);
        }));
    }
}
TapScriptRunnerService.decorators = [
    { type: Injectable, args: [{
                providedIn: 'root'
            },] }
];
/** @nocollapse */
TapScriptRunnerService.ctorParameters = () => [
    { type: GlobalEventsService }
];
/** @nocollapse */ TapScriptRunnerService.ɵprov = ɵɵdefineInjectable({ factory: function TapScriptRunnerService_Factory() { return new TapScriptRunnerService(ɵɵinject(GlobalEventsService)); }, token: TapScriptRunnerService, providedIn: "root" });
if (false) {
    /** @type {?} */
    TapScriptRunnerService.prototype.scriptTypes;
    /** @type {?} */
    TapScriptRunnerService.prototype.scriptRunner;
    /** @type {?} */
    TapScriptRunnerService.prototype._outputMessages;
    /** @type {?} */
    TapScriptRunnerService.prototype._errors;
    /** @type {?} */
    TapScriptRunnerService.prototype.output;
    /**
     * The ScriptRunner object is given options with what is done with values retrieved by the script
     * The output property defines the actions to perform on data retrieving.
     * The display property defines how to log informations given back by the scriptRunner.
     * @see {ScriptRunner}
     * @type {?}
     */
    TapScriptRunnerService.prototype.scriptRunnerOptions;
    /** @type {?} */
    TapScriptRunnerService.prototype.scriptFile;
    /** @type {?} */
    TapScriptRunnerService.prototype.scriptType;
    /** @type {?} */
    TapScriptRunnerService.prototype.events;
}

/**
 * @fileoverview added by tsickle
 * Generated from: lib/definitions.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */
/**
 * @record
 */
function ScriptFile() { }
if (false) {
    /** @type {?} */
    ScriptFile.prototype.name;
    /** @type {?} */
    ScriptFile.prototype.scriptString;
}

/**
 * @fileoverview added by tsickle
 * Generated from: public_api.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */

/**
 * @fileoverview added by tsickle
 * Generated from: iotize-ionic-scripts.ts
 * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
 */

export { TapScriptModule, TapScriptRunnerService };
//# sourceMappingURL=iotize-ionic-scripts.js.map
