"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LanguageControl = void 0;
exports.makeMicroscopeParser = makeMicroscopeParser;
const Parser_js_1 = require("../Parser.js");
const Microscope_js_1 = require("./Microscope.js");
const Timeline_js_1 = require("./Timeline.js");
const Interpolator_js_1 = require("./Interpolator.js");
const Autocompletion_js_1 = require("../core/Autocompletion.js");
class LanguageControl {
    constructor(microscope = undefined) {
        this.timeline = new Timeline_js_1.Timeline();
        this.microscope = microscope !== undefined ? microscope : new Microscope_js_1.Microscope();
    }
    reset() {
        this.globalStart = new Date();
        this.timeline.clear();
        this.microscope.reset();
    }
    getTimeline() {
        return this.timeline;
    }
    getMicroscope() {
        return this.microscope;
    }
    initParser() {
        const definedChannels = [];
        const definedRegions = [];
        let parser = new Parser_js_1.Parser();
        parser.addParseStartListener(() => {
            definedChannels.length = 0;
            definedRegions.length = 0;
        });
        parser.defineType("led", "385nm", _e => Microscope_js_1.LED.LED_385);
        parser.defineType("led", "470nm", _e => Microscope_js_1.LED.LED_470);
        parser.defineType("led", "567nm", _e => Microscope_js_1.LED.LED_567);
        parser.defineType("led", "625nm", _e => Microscope_js_1.LED.LED_625);
        parser.defineType("led-power", "{<led-power>:int}%", e => e.evaluate("<led-power>"), true);
        parser.defineType("exposure-time", "{<exposure-time>:int}ms", e => e.evaluate("<exposure-time>"), true);
        parser.defineType("led-setting", "{led-power:led-power} at {wavelength:led}", e => {
            const power = e.evaluate("led-power");
            const led = e.evaluate("wavelength");
            return new Microscope_js_1.LEDSetting(led, power);
        }, true);
        parser.defineType("another-led-setting", ", {led-setting:led-setting}", e => e.evaluate("led-setting"), true);
        parser.defineType("channel-name", "'{<name>:[A-Za-z0-9]:+}'", e => e.getParsedString("<name>"), true);
        parser.defineSentence("Define channel {channel-name:channel-name}:" +
            "{\n  }excite with {led-setting:led-setting}{another-led-setting:another-led-setting:0-3}" +
            "{\n  }use an exposure time of {exposure-time:exposure-time}.", e => {
            const name = e.evaluate("channel-name");
            const firstLedSetting = e.evaluate("led-setting");
            const otherLedSettings = e.evaluate("another-led-setting");
            const exposureTime = e.evaluate("exposure-time");
            const channel = new Microscope_js_1.Channel(name, firstLedSetting, otherLedSettings, exposureTime);
            this.microscope.addChannel(channel);
            return undefined;
        }).onSuccessfulParsed(n => {
            definedChannels.push(n.getParsedString("channel-name"));
        });
        parser.defineType("region-name", "'{<region-name>:[a-zA-Z0-9]:+}'", e => e.getParsedString("<region-name>"), true);
        parser.defineType("region-dimensions", "{<width>:float} x {<height>:float} x {<depth>:float} microns", e => {
            const w = e.evaluate("<width>");
            const h = e.evaluate("<height>");
            const d = e.evaluate("<depth>");
            return [w, h, d];
        }, true);
        parser.defineType("region-center", "{<center>:tuple<float,x,y,z>} microns", e => e.evaluate("<center>"), true);
        parser.defineSentence("Define a position {region-name:region-name}:" +
            "{\n  }{region-dimensions:region-dimensions}" +
            "{\n  }centered at {region-center:region-center}.", e => {
            const name = e.evaluate("region-name");
            const dimensions = e.evaluate("region-dimensions");
            const center = e.evaluate("region-center");
            this.microscope.addPosition(new Microscope_js_1.Position(name, center, dimensions));
            return undefined;
        }).onSuccessfulParsed(n => {
            definedRegions.push(n.getParsedString("region-name"));
        });
        parser.defineType("defined-channels", "'{channel:[A-Za-z0-9]:+}'", e => e.getParsedString("channel"), e => Autocompletion_js_1.Autocompletion.literal(e, definedChannels));
        parser.defineType("defined-positions", "'{position:[A-Za-z0-9]:+}'", e => e.getParsedString("position"), e => Autocompletion_js_1.Autocompletion.literal(e, definedRegions));
        parser.defineType("time-unit", "second(s)", _e => 1);
        parser.defineType("time-unit", "minute(s)", _e => 60);
        parser.defineType("time-unit", "hour(s)", _e => 3600);
        parser.defineType("time-interval", "{n:float} {time-unit:time-unit}", e => {
            const nUnits = e.evaluate("n");
            const unitInSeconds = e.evaluate("time-unit");
            return Math.round(nUnits * unitInSeconds);
        }, true);
        parser.defineType("repetition", "once", _e => [1, 0]);
        parser.defineType("repetition", "every {interval:time-interval} for {duration:time-interval}", e => {
            const interval = e.evaluate("interval");
            const duration = e.evaluate("duration");
            return [interval, duration];
        }, true);
        parser.defineType("z-distance", "{z-distance:float} microns", e => e.evaluate("z-distance"), true);
        parser.defineType("lens", "5x lens", _e => Microscope_js_1.Lens.FIVE);
        parser.defineType("lens", "20x lens", _e => Microscope_js_1.Lens.TWENTY);
        parser.defineType("mag", "0.5x magnification changer", _e => Microscope_js_1.MagnificationChanger.ZERO_FIVE);
        parser.defineType("mag", "1.0x magnification changer", _e => Microscope_js_1.MagnificationChanger.ONE_ZERO);
        parser.defineType("mag", "2.0x magnification changer", _e => Microscope_js_1.MagnificationChanger.TWO_ZERO);
        parser.defineType("binning", "1 x 1", _e => Microscope_js_1.Binning.ONE);
        parser.defineType("binning", "2 x 2", _e => Microscope_js_1.Binning.TWO);
        parser.defineType("binning", "3 x 3", _e => Microscope_js_1.Binning.THREE);
        parser.defineType("binning", "4 x 4", _e => Microscope_js_1.Binning.FOUR);
        parser.defineType("binning", "5 x 5", _e => Microscope_js_1.Binning.FIVE);
        parser.defineType("start", "At the beginning", _e => this.globalStart);
        parser.defineType("start", "At {time:time}", e => e.evaluate("time"), true);
        parser.defineType("start", "After {delay:time-interval}", e => {
            const afterSeconds = e.evaluate("delay");
            const d = new Date(this.globalStart);
            d.setSeconds(d.getSeconds() + afterSeconds);
            return d;
        }, true);
        parser.defineType("position-list", "all positions", _e => [Microscope_js_1.ALL_POSITIONS]);
        parser.defineType("position-list", "position(s) {positions:list<defined-positions>}", e => e.evaluate("positions"));
        parser.defineType("channel-list", "all channels", _e => [Microscope_js_1.ALL_CHANNELS]);
        parser.defineType("channel-list", "channel(s) {channels:list<defined-channels>}", e => e.evaluate("channels"));
        parser.defineSentence("{start:start}{, }acquire..." +
            "{\n  }{repetition:repetition}" +
            "{\n  }{position-list:position-list}" +
            "{\n  }{channel-list:channel-list}" +
            "{\n  }with a plane distance of {dz:z-distance}" +
            "{\n  }using the {lens:lens} with the {magnification:mag} and a binning of {binning:binning}.", e => {
            const time = e.evaluate("start");
            const repetition = e.evaluate("repetition");
            const interval = repetition[0];
            const duration = repetition[1];
            const positionNames = e.evaluate("position-list");
            const channelNames = e.evaluate("channel-list");
            const lens = e.evaluate("lens");
            const mag = e.evaluate("magnification");
            const binning = e.evaluate("binning");
            const dz = e.evaluate("dz");
            const start = new Date();
            start.setHours(this.globalStart.getHours());
            start.setMinutes(this.globalStart.getMinutes());
            start.setSeconds(this.globalStart.getSeconds());
            start.setMilliseconds(this.globalStart.getMilliseconds());
            if (this.globalStart.getTime() > start.getTime())
                start.setDate(start.getDate() + 1); // add one day
            const nCycles = duration < interval ? 1 : Math.floor(duration / interval) + 1;
            for (let c = 0; c < nCycles; c++) {
                const plannedExecutionTime = new Date(start);
                plannedExecutionTime.setSeconds(start.getSeconds() + (c * interval));
                this.timeline.put(plannedExecutionTime, () => {
                    this.microscope.setLens(lens);
                    this.microscope.setMagnificationChanger(mag);
                    this.microscope.setBinning(binning);
                    this.microscope.acquire(positionNames, channelNames, dz);
                });
            }
            return undefined;
        });
        parser.defineSentence("{start:start}{, }adjust..." +
            "{\n  }{repetition:repetition}" +
            "{\n  }the power of the {led:led} led of channel {channel:defined-channels} to {power:led-power}.", e => {
            const time = e.evaluate("start");
            const repetition = e.evaluate("repetition");
            const interval = repetition[0];
            const duration = repetition[1];
            const led = e.evaluate("led");
            const channel = e.evaluate("channel");
            const power = e.evaluate("power");
            const start = new Date();
            start.setHours(this.globalStart.getHours());
            start.setMinutes(this.globalStart.getMinutes());
            start.setSeconds(this.globalStart.getSeconds());
            start.setMilliseconds(this.globalStart.getMilliseconds());
            if (this.globalStart.getTime() > start.getTime())
                start.setDate(start.getDate() + 1); // add one day
            const nCycles = duration < interval ? 1 : Math.floor(duration / interval) + 1;
            const interpolator = new Interpolator_js_1.Interpolator(() => { var _a, _b; return (_b = (_a = this.microscope.getChannel(channel)) === null || _a === void 0 ? void 0 : _a.getLEDSetting(led)) === null || _b === void 0 ? void 0 : _b.getIntensity(); }, (_c, v) => { var _a, _b; return (_b = (_a = this.microscope.getChannel(channel)) === null || _a === void 0 ? void 0 : _a.getLEDSetting(led)) === null || _b === void 0 ? void 0 : _b.setIntensity(Math.round(v)); }, power, nCycles);
            for (let c = 0; c < nCycles; c++) {
                const cycle = c;
                const plannedExecutionTime = new Date(start);
                plannedExecutionTime.setSeconds(start.getSeconds() + (c * interval));
                this.timeline.put(plannedExecutionTime, () => interpolator.interpolate(cycle));
            }
            return undefined;
        });
        parser.defineSentence("{start:start}{, }adjust..." +
            "{\n  }{repetition:repetition}" +
            "{\n  }the exposure time of channel {channel:defined-channels} to {exposure-time:exposure-time}.", e => {
            const time = e.evaluate("start");
            const repetition = e.evaluate("repetition");
            const interval = repetition[0];
            const duration = repetition[1];
            const channel = e.evaluate("channel");
            const exposureTime = e.evaluate("exposure-time");
            const start = new Date();
            start.setHours(this.globalStart.getHours());
            start.setMinutes(this.globalStart.getMinutes());
            start.setSeconds(this.globalStart.getSeconds());
            start.setMilliseconds(this.globalStart.getMilliseconds());
            if (this.globalStart.getTime() > start.getTime())
                start.setDate(start.getDate() + 1); // add one day
            const nCycles = duration < interval ? 1 : Math.floor(duration / interval) + 1;
            const interpolator = new Interpolator_js_1.Interpolator(() => { var _a; return (_a = this.microscope.getChannel(channel)) === null || _a === void 0 ? void 0 : _a.getExposureTime(); }, (_c, v) => { var _a; return (_a = this.microscope.getChannel(channel)) === null || _a === void 0 ? void 0 : _a.setExposureTime(Math.round(v)); }, exposureTime, nCycles);
            for (let c = 0; c < nCycles; c++) {
                const cycle = c;
                const plannedExecutionTime = new Date(start);
                plannedExecutionTime.setSeconds(start.getSeconds() + (c * interval));
                this.timeline.put(plannedExecutionTime, () => interpolator.interpolate(cycle));
            }
            return undefined;
        });
        parser.defineType("temperature", "{temperature:float}\u00B0C", e => e.evaluate("temperature"), true);
        parser.defineType("co2-concentration", "{CO2 concentration:float}%", e => e.evaluate("CO2 concentration"), true);
        parser.defineSentence("{start:start}{, }adjust..." +
            "{\n  }{repetition:repetition}" +
            "{\n  }the CO2 concentration to {co2-concentration:co2-concentration}.", e => {
            const time = e.evaluate("start");
            const repetition = e.evaluate("repetition");
            const interval = repetition[0];
            const duration = repetition[1];
            const co2Concentration = e.evaluate("co2-concentration");
            const start = new Date();
            start.setHours(this.globalStart.getHours());
            start.setMinutes(this.globalStart.getMinutes());
            start.setSeconds(this.globalStart.getSeconds());
            start.setMilliseconds(this.globalStart.getMilliseconds());
            if (this.globalStart.getTime() > start.getTime())
                start.setDate(start.getDate() + 1); // add one day
            const nCycles = duration < interval ? 1 : Math.floor(duration / interval) + 1;
            const interpolator = new Interpolator_js_1.Interpolator(() => this.microscope.getCO2Concentration(), (_c, v) => this.microscope.setCO2Concentration(v), co2Concentration, nCycles);
            for (let c = 0; c < nCycles; c++) {
                const cycle = c;
                const plannedExecutionTime = new Date(start);
                plannedExecutionTime.setSeconds(start.getSeconds() + (c * interval));
                this.timeline.put(plannedExecutionTime, () => interpolator.interpolate(cycle));
            }
            return undefined;
        });
        parser.defineSentence("{start:start}{, }adjust..." +
            "{\n  }{repetition:repetition}" +
            "{\n  }the temperature to {temperature:temperature}.", e => {
            const time = e.evaluate("start");
            const repetition = e.evaluate("repetition");
            const interval = repetition[0];
            const duration = repetition[1];
            const temperature = e.evaluate("temperature");
            const start = new Date();
            start.setHours(this.globalStart.getHours());
            start.setMinutes(this.globalStart.getMinutes());
            start.setSeconds(this.globalStart.getSeconds());
            start.setMilliseconds(this.globalStart.getMilliseconds());
            if (this.globalStart.getTime() > start.getTime())
                start.setDate(start.getDate() + 1); // add one day
            const nCycles = duration < interval ? 1 : Math.floor(duration / interval) + 1;
            const interpolator = new Interpolator_js_1.Interpolator(() => this.microscope.getTemperature(), (_c, v) => this.microscope.setTemperature(v), temperature, nCycles);
            for (let c = 0; c < nCycles; c++) {
                const cycle = c;
                const plannedExecutionTime = new Date(start);
                plannedExecutionTime.setSeconds(start.getSeconds() + (c * interval));
                this.timeline.put(plannedExecutionTime, () => interpolator.interpolate(cycle));
            }
            return undefined;
        });
        return parser;
    }
}
exports.LanguageControl = LanguageControl;
function makeMicroscopeParser() {
    let parser = new Parser_js_1.Parser();
    const definedChannels = [];
    const definedRegions = [];
    parser.addParseStartListener(() => {
        definedChannels.length = 0;
        definedRegions.length = 0;
    });
    parser.defineType("led", "385nm", _e => 385);
    parser.defineType("led", "470nm", _e => 470);
    parser.defineType("led", "567nm", _e => 567);
    parser.defineType("led", "625nm", _e => 625);
    parser.defineType("led-power", "{<led-power>:int}%", e => e.evaluate("<led-power>"), true);
    parser.defineType("exposure-time", "{<exposure-time>:int}ms", e => e.evaluate("<exposure-time>"), true);
    parser.defineType("led-setting", "{led-power:led-power} at {wavelength:led}", e => {
        const power = e.evaluate("led-power");
        const led = e.evaluate("wavelength");
        return { led: led, power: power };
    }, true);
    parser.defineType("another-led-setting", ", {led-setting:led-setting}", e => e.evaluate("led-setting"), true);
    parser.defineType("channel-name", "'{<name>:[A-Za-z0-9]:+}'", e => e.getParsedString("<name>"), true);
    parser.defineSentence("Define channel {channel-name:channel-name}:" +
        "{\n  }excite with {led-setting:led-setting}{another-led-setting:another-led-setting:0-3}" +
        "{\n  }use an exposure time of {exposure-time:exposure-time}.", undefined).onSuccessfulParsed(n => {
        definedChannels.push(n.getParsedString("channel-name"));
    });
    parser.defineType("region-name", "'{<region-name>:[a-zA-Z0-9]:+}'", undefined, true);
    parser.defineType("region-dimensions", "{<width>:float} x {<height>:float} x {<depth>:float} microns", undefined, true);
    parser.defineType("region-center", "{<center>:tuple<float,x,y,z>} microns", undefined, true);
    parser.defineType("sentence", "Define a position {region-name:region-name}:" +
        "{\n  }{region-dimensions:region-dimensions}" +
        "{\n  }centered at {region-center:region-center}.", undefined).onSuccessfulParsed(n => {
        definedRegions.push(n.getParsedString("region-name"));
    });
    parser.defineType("defined-channels", "'{channel:[A-Za-z0-9]:+}'", _e => undefined, e => Autocompletion_js_1.Autocompletion.literal(e, definedChannels));
    parser.defineType("defined-positions", "'{position:[A-Za-z0-9]:+}'", e => e.getParsedString("position"), e => Autocompletion_js_1.Autocompletion.literal(e, definedRegions));
    parser.defineType("time-unit", "second(s)", _e => 1);
    parser.defineType("time-unit", "minute(s)", _e => 60);
    parser.defineType("time-unit", "hour(s)", _e => 3600);
    parser.defineType("time-interval", "{n:float} {time-unit:time-unit}", e => {
        const n = e.evaluate("n");
        const unit = e.evaluate("time-unit");
        const seconds = Math.round(n * unit);
        return seconds;
    }, true);
    parser.defineType("repetition", "once", _e => [1, 0]);
    parser.defineType("repetition", "every {interval:time-interval} for {duration:time-interval}", e => {
        const interval = e.evaluate("interval");
        const duration = e.evaluate("duration");
        return [interval, duration];
    }, true);
    parser.defineType("z-distance", "{z-distance:float} microns", undefined, true);
    parser.defineType("lens", "5x lens", undefined);
    parser.defineType("lens", "20x lens", undefined);
    parser.defineType("mag", "0.5x magnification changer", undefined);
    parser.defineType("mag", "1.0x magnification changer", undefined);
    parser.defineType("mag", "2.0x magnification changer", undefined);
    parser.defineType("binning", "1 x 1", _e => 1);
    parser.defineType("binning", "2 x 2", _e => 2);
    parser.defineType("binning", "4 x 4", _e => 4);
    parser.defineType("binning", "8 x 8", _e => 8);
    parser.defineType("temperature", "{temperature:float}\u00B0C", undefined, true);
    parser.defineType("co2-concentration", "{CO2 concentration:float}%", undefined, true);
    parser.defineType("incubation", "set the temperature to {temperature:temperature}", undefined);
    parser.defineType("incubation", "set the CO2 concentration to {co2-concentration:co2-concentration}", undefined);
    parser.defineType("acquisition", "acquire..." +
        "{\n  }every {interval:time-interval} for {duration:time-interval}" +
        "{\n  }position(s) {positions:list<defined-positions>}" +
        "{\n  }channel(s) {channels:list<defined-channels>}" +
        //				"{\n  }with a resolution of {dx:float} x {dy:float} x {dz:float} microns.",
        "{\n  }with a plane distance of {dz:z-distance}" +
        "{\n  }using the {lens:lens} with the {magnification:mag} and a binning of {binning:binning}", undefined);
    parser.defineType("start", "At the beginning", undefined);
    parser.defineType("start", "At {time:time}", undefined, true);
    parser.defineType("start", "After {delay:time-interval}", undefined, true);
    parser.defineSentence("{start:start}, {acquisition:acquisition}.", undefined);
    parser.defineSentence("{start:start}, {incubation:incubation}.", undefined);
    return parser;
}
//# sourceMappingURL=LanguageControl.js.map