"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@angular/core");
const material_1 = require("@angular/material");
const store_1 = require("@ngrx/store");
const JSON5 = require("json5");
const BehaviorSubject_1 = require("rxjs/BehaviorSubject");
const project_1 = require("../redux/project");
const console_service_1 = require("./console.service");
const message_1 = require("./messages/message");
/**
 * Number of pixels of upwards scroll after which we'll no longer
 * automatically scroll down when new messages come in.
 */
const scrollThreshold = 32;
/**
 * The host component holds the arrangement of macroscopic editor components.
 */
let ConsoleComponent = class ConsoleComponent {
    constructor(console, snackRef, store, project) {
        this.console = console;
        this.snackRef = snackRef;
        this.store = store;
        this.project = project;
        /**
         * Filter currently in use in the console display.
         */
        this.filter = this.store.map(state => state.console.filter);
        /**
         * An observable of console messages to display.
         */
        this.messages = this.filter
            .switchMap(filter => this.console.read(filter))
            .do(() => {
            this.rescroll();
        });
        /**
         * Returns the number of messages omitted by the filter.
         */
        this.omitted = this.filter.switchMap(filter => this.console.omitted(filter));
        /**
         * Template hoist for the Message enum.
         */
        this.Message = message_1.Message;
        /**
         * An observable that contains the ID of the message that the user has
         * expanded, if any.
         */
        this.expandedId = new BehaviorSubject_1.BehaviorSubject(null);
        /**
         * The ID of the message that's a pair to the one currently being hovered.
         */
        this.hoveredPair = new BehaviorSubject_1.BehaviorSubject(null);
        /**
         * repl input to be sent to the controls.
         */
        this.commandInput = new BehaviorSubject_1.BehaviorSubject('');
        /**
         * Whether to show the search filter.
         */
        this.showFilter = new BehaviorSubject_1.BehaviorSubject(false);
    }
    ngAfterContentInit() {
        const el = this.container.nativeElement;
        el.scrollTop = el.scrollHeight;
        const filterInput = this.filterInput.nativeElement;
        filterInput.addEventListener('keydown', ev => {
            switch (ev.keyCode) {
                case 13:// enter
                    this.showFilter.next(false);
                    break;
                case 27:// escape
                    this.setPattern('');
                    this.showFilter.next(false);
                    break;
                default:
                    return;
            }
            ev.preventDefault();
        });
    }
    /**
     * Updates the filter pattern for the console.
     */
    setPattern(pattern) {
        this.filter.take(1).subscribe(filter => {
            this.project.setConsoleFilter(Object.assign({}, filter, { pattern }));
        });
    }
    /**
     * Toggles whether one kind of message is show in the console, or not.
     */
    toggleFilterKind(kind) {
        this.filter.take(1).subscribe(existing => {
            this.project.setConsoleFilter(Object.assign({}, existing, { kinds: Object.assign({}, existing.kinds, { [kind]: existing.kinds[kind] === false }) }));
        });
    }
    /**
     * Returns whether the filter of the given kind is turned on.
     */
    hasFilterKind(kind) {
        return this.filter.map(f => f.kinds[kind] !== false);
    }
    /**
     * Called when the user clicks on a message. If they click on a message
     * that's already expanded, it's closed, otherwise it toggles to that one.
     */
    toggleExpansion(messageId) {
        if (!window.getSelection().isCollapsed) {
            return; // noop if they're selecting text
        }
        if (messageId === this.expandedId.getValue()) {
            this.expandedId.next(null);
        }
        else {
            this.expandedId.next(messageId);
        }
    }
    /**
     * Called when the mouse hovers over a message. We'll try to find the
     * appropriate response/reply and show it as hovered, too.
     */
    hoveredMessage(message) {
        const pair = this.console.getPairMessage(message);
        if (pair) {
            this.hoveredPair.next(pair.message.id);
        }
    }
    /**
     * Called when the user wants to send a command to the frame.
     */
    sendCommand() {
        const data = this.commandInput.getValue().trim();
        const paren = data.indexOf('(');
        if (paren === -1 || !data.endsWith(')')) {
            this.showCommandError('Your command should be in the format "method({ arguments: "here" })"');
            return;
        }
        const name = data.slice(0, paren);
        const innerJSON = data.slice(paren + 1, -1);
        let params;
        try {
            params = innerJSON ? JSON5.parse(innerJSON) : null;
        }
        catch (e) {
            this.showCommandError(`Could not parse your command arguments: ${e}`);
            return;
        }
        this.console.run(name, params);
        this.commandInput.next('');
    }
    /**
     * Opens a toast with the provided error message.
     */
    showCommandError(message) {
        this.snackRef.open(message, undefined, { duration: 5000 });
    }
    /**
     * Scrolls the console to the bottom of the screen, if it's near it.
     */
    rescroll() {
        if (this.expandedId.getValue() !== null) {
            return;
        }
        const el = this.container.nativeElement;
        if (el.scrollTop + el.clientHeight > el.scrollHeight - scrollThreshold) {
            setTimeout(() => (el.scrollTop = el.scrollHeight));
        }
    }
};
__decorate([
    core_1.ViewChild('container'),
    __metadata("design:type", core_1.ElementRef)
], ConsoleComponent.prototype, "container", void 0);
__decorate([
    core_1.ViewChild('filterInput'),
    __metadata("design:type", core_1.ElementRef)
], ConsoleComponent.prototype, "filterInput", void 0);
ConsoleComponent = __decorate([
    core_1.Component({
        selector: 'editor-console',
        templateUrl: './console.component.html',
        styleUrls: ['./console.component.scss'],
        changeDetection: core_1.ChangeDetectionStrategy.OnPush,
    }),
    __metadata("design:paramtypes", [console_service_1.ConsoleService,
        material_1.MatSnackBar,
        store_1.Store,
        project_1.ProjectService])
], ConsoleComponent);
exports.ConsoleComponent = ConsoleComponent;
