"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
 * @module Scripting Environment
 *
 */
const path = require("path");
const fs = require("fs");
const luxon_1 = require("luxon");
const us_scheduler_1 = require("us-scheduler");
const core_1 = require("@ha4us/core");
const rxjs = require("rxjs");
const rxjsoperators = require("rxjs/operators");
const got = require("got");
const cheerio = require("cheerio");
const winston = require("winston");
const luxon_2 = require("luxon");
class Sandbox {
    constructor(_script) {
        this._script = _script;
        this.stop$ = new rxjs.Subject();
        this.onlyRetained = core_1.Ha4usOperators.onlyRetained;
        this.noRetained = core_1.Ha4usOperators.noRetained;
        this.pick = core_1.Ha4usOperators.pick;
        this.pickEach = core_1.Ha4usOperators.pickEach;
        this.render = core_1.render;
        this.compileTemplate = core_1.compile;
        this.randomString = core_1.randomString;
        this._modules = { path, fs, rxjs, 'rxjs/operators': rxjsoperators };
        this.http = {
            parse: (url, query) => {
                return got
                    .get(url, { query })
                    .then(response => cheerio.load(response.body));
            },
            get: (url, query) => {
                return got.get(url, { query }).then(response => response.body);
            },
        };
        this.console = {
            log: (message, ...args) => {
                this._script.log('debug', message, args);
                // this._script.domain.bind(this._log.debug)(...args)
            },
            debug: (message, ...args) => {
                this._script.log('debug', message, args);
                // this._script.domain.bind(this._log.debug)(...args)
            },
            info: (message, ...args) => {
                this._script.log('info', message, args);
                // this._script.domain.bind(this._log.debug)(...args)
            },
            warn: (message, ...args) => {
                this._script.log('warn', message, args);
                // this._script.domain.bind(this._log.debug)(...args)
            },
            error: (message, ...args) => {
                this._script.log('error', message, args);
                // this._script.domain.bind(this._log.debug)(...args)
            },
        };
        this.log = this.console;
        this._log = this._setupLogger();
        luxon_2.Settings.defaultLocale = 'de';
        this._$states = _script.opts.$states;
        this._$objects = _script.opts.$objects;
        this._$yaml = _script.opts.$yaml;
        this.$media = _script.opts.$media;
        this._scheduler = new us_scheduler_1.Scheduler({
            latitude: _script.opts.$args.lat,
            longitude: _script.opts.$args.long,
            skipPast: true,
        });
    }
    get sandbox() {
        const resultingSandbox = {};
        Object.getOwnPropertyNames(Object.getPrototypeOf(this))
            .filter(key => key !== 'sandbox' && key !== 'constructor' && key.charAt(0) !== '_')
            .forEach(prop => (resultingSandbox[prop] = this[prop].bind(this)));
        Object.getOwnPropertyNames(this)
            .filter(key => key.charAt(0) !== '_')
            .forEach(prop => (resultingSandbox[prop] = this[prop]));
        resultingSandbox.__dirname = this._script.path;
        resultingSandbox.DateTime = luxon_1.DateTime;
        return resultingSandbox;
    }
    _setupLogger() {
        const logPath = path.resolve(this._script.path, this._script.name.replace('/', '_') + '.log');
        /*const myFormat = format.printf(info => {
          return `${info.timestamp} ${info.level}: ${info.message}`
        })*/
        return new winston.Logger({
            transports: [
                /* new winston.transports.Console({
                  json: false,
                  level: 'debug',
                  colorize: false,
                  prettyPrint: true,
                  stderrLevels: ['error', 'warn'],
                  timestamp: true,
                }),*/
                new winston.transports.File({
                    filename: logPath,
                    json: false,
                    level: 'debug',
                    colorize: false,
                    prettyPrint: true,
                    stderrLevels: ['error', 'warn'],
                    timestamp: true,
                }),
            ],
        });
        /*   winston.configure({
          transports: [
            new transports.File({
            }),
          ],
        })*/
    }
    require(md) {
        this._log.debug('requiring', md);
        if (this._modules.hasOwnProperty(md)) {
            return this._modules[md];
        }
        let tmp;
        if (md.match(/^\.\//) || md.match(/^\.\.\//)) {
            tmp = './' + path.relative(__dirname, path.join(this._script.path, md));
        }
        else {
            tmp = md;
            this._log.debug('Looking in', path.join(this._script.path, 'node_modules', md, 'package.json'));
            if (fs.existsSync(path.join(this._script.path, 'node_modules', md, 'package.json'))) {
                // must be in node_modules of scriptDir
                tmp = path.resolve(path.join(this._script.path, 'node_modules', md));
                this._log.debug('Requiring from node_modules in scriptdir', md);
            }
        }
        this._log.debug('Finally requiring', path.resolve(tmp));
        this._modules[md] = require(tmp);
        return this._modules[md];
    }
    observe(topic, ...params) {
        this._script.enterDomain();
        return this._script.opts.$states
            .observe(topic, ...params)
            .pipe(rxjsoperators.takeUntil(this.stop$));
    }
    combine(...pattern) {
        return this._script.opts.$states
            .observeLatest(...pattern)
            .pipe(rxjsoperators.takeUntil(this.stop$));
    }
    set(topic, value) {
        return this._$states.set(topic, value);
    }
    get(topic) {
        return this._$states.get(topic);
    }
    status(topic, value, retained = true) {
        return this._$states.status(core_1.MqttUtil.join(this._script.name, topic), value, retained);
    }
    emit(topic, value, retained = true) {
        return this._$states.status(core_1.MqttUtil.join(this._script.name, topic), value, retained);
    }
    schedule(...times) {
        this._script.enterDomain();
        return this._scheduler
            .schedule(...times)
            .pipe(rxjsoperators.takeUntil(this.stop$));
    }
    cron(cronPattern) {
        return us_scheduler_1.cron(cronPattern, luxon_1.DateTime.local()).pipe(rxjsoperators.takeUntil(this.stop$));
    }
    doIn(duration, data) {
        this._script.enterDomain();
        return this._scheduler
            .in(duration, data)
            .pipe(rxjsoperators.takeUntil(this.stop$));
    }
    doAt(date) {
        this._script.enterDomain();
        return this._scheduler.at(date).pipe(rxjsoperators.takeUntil(this.stop$));
    }
    load(file) {
        return this._$yaml.load(file, this._script.path);
    }
    save(data, file) {
        return this._$yaml.save(data, file, {
            filePath: this._script.path,
        });
    }
    createObject(topic, data) {
        return this._$objects
            .create([data, {}], {
            root: core_1.MqttUtil.join('$', this._script.topic, topic),
            mode: 'update',
        })
            .toPromise();
    }
    $onDestroy(destroyFunc) {
        this._onDestroy = destroyFunc;
    }
    random(maxOrMin, max) {
        if (!max) {
            max = maxOrMin;
            maxOrMin = 0;
        }
        return Math.floor(Math.random() * (max - maxOrMin + 1)) + maxOrMin;
    }
    downloadImage(url, fileOptions) {
        return this.$media.postUrl(url, fileOptions).then(data => data.urn);
    }
    event(msg, opt) {
        // this.$event
    }
}
exports.Sandbox = Sandbox;
//# sourceMappingURL=sandbox.class.js.map