"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;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@angular/core");
const Rx_1 = require("rxjs/Rx");
const log_message_1 = require("./messages/log-message");
const message_1 = require("./messages/message");
const method_message_1 = require("./messages/method-message");
const reply_message_1 = require("./messages/reply-message");
/**
 * The ConsoleService stores a log of data sent and received from the iframed
 * controls.
 */
let ConsoleService = ConsoleService_1 = class ConsoleService {
    /**
     * The ConsoleService stores a log of data sent and received from the iframed
     * controls.
     */
    constructor() {
        /**
         * Observable store of console messages.
         */
        this.messages = new Rx_1.BehaviorSubject([]);
        /**
         * A utility function that can be used in Angular *ngFor templates to
         * track messages.
         */
        this.trackBy = (message) => message.id;
    }
    /**
     * callToIframe records a call made into the iframed controls.
     */
    callToIframe(message) {
        this.push(new method_message_1.MethodMessage(message_1.Message.SentMethod, message));
    }
    /**
     * callFromIframe records a method call that the iframe made against us.
     */
    callFromIframe(message) {
        this.push(new method_message_1.MethodMessage(message_1.Message.ReceivedMethod, message));
    }
    /**
     * replyToIframe records a reply made into the iframed controls.
     */
    replyToIframe(message) {
        this.push(new reply_message_1.ReplyMessage(message_1.Message.SentReply, message));
    }
    /**
     * replyFromIframe records a method reply that the iframe made against us.
     */
    replyFromIframe(message) {
        this.push(new reply_message_1.ReplyMessage(message_1.Message.ReceivedReply, message));
    }
    /**
     * addLog records a log message the iframe sent to us.
     */
    addLog(entry) {
        this.push(new log_message_1.LogMessage(entry));
    }
    /**
     * read returns a feed of messages filtered with the provided options.
     */
    read(filter = {}) {
        const test = this.test(filter);
        return this.messages.map(messages => messages.filter(test));
    }
    /**
     * Returns the number of messages omitted by the provided filter.
     */
    omitted(filter = {}) {
        const test = this.test(filter);
        return this.messages.map(messages => messages.reduce((total, m) => total + (test(m) ? 0 : 1), 0));
    }
    /**
     * Wipes all messages from storage.
     */
    clear() {
        this.messages.next([]);
    }
    /**
     * Marks all messages as having been read.
     */
    markRead() {
        this.messages.next(this.messages.getValue().map(m => (Object.assign({}, m, { hasSeen: true }))));
    }
    /**
     * Sends a command to the frame.
     */
    run(method, params) {
        this.rpc.call(method, params, true).catch(() => undefined);
    }
    /**
     * Binds the console to read data from the RPC socket.
     */
    bindToRPC(rpc) {
        this.rpc = rpc;
        rpc.on('recvMethod', packet => {
            switch (packet.method) {
                case 'ready':
                    this.clear();
                    break;
                case 'log':
                    // handled separately, ignore
                    break;
                default:
                    this.callFromIframe(packet);
            }
        });
        rpc.on('recvReply', packet => {
            this.replyFromIframe(packet);
        });
        rpc.on('sendMethod', packet => {
            this.callToIframe(packet);
        });
        rpc.on('sendReply', packet => {
            this.replyToIframe(packet);
        });
    }
    /**
     * Tries to return the given message's paired friend. A method call if the
     * message is a reply, a method response if it's a call.
     */
    getPairMessage(message) {
        const available = this.messages.getValue();
        if (message.inReplyTo) {
            return available.find(m => m.message.id === message.inReplyTo);
        }
        else {
            return available.find(m => m.message.inReplyTo === message.id);
        }
    }
    /**
     * Adds the message to the console output.
     */
    push(message) {
        let messages = this.messages.getValue();
        // not pure, but the array isn't exposed externally, so it's fine.
        messages.push({
            message,
            hasSeen: false,
            createdAt: new Date(),
        });
        if (messages.length > ConsoleService_1.maxMessages) {
            messages = messages.slice(-Math.round(ConsoleService_1.maxMessages * 0.75));
        }
        this.messages.next(messages);
    }
    /**
     * Returns a function to test whether a message passes the filter.
     */
    test(filter) {
        let re;
        if (filter.pattern) {
            // Regexeption: match a /re/x pattern, body in group 1, flags in 2. BWAAAAA
            const exec = /^\/(.+)\/([a-z]+)?$/.exec(filter.pattern);
            if (exec) {
                re = new RegExp(exec[1], exec[2]);
            }
        }
        return ({ message }) => {
            if (re) {
                if (!re.test(message.text)) {
                    return false;
                }
            }
            else if (filter.pattern && !message.text.includes(filter.pattern)) {
                return false;
            }
            if (filter.kinds && filter.kinds[message.kind] === false) {
                return false;
            }
            return true;
        };
    }
};
/**
 * Max number of messages to display in the console.
 */
ConsoleService.maxMessages = 500;
ConsoleService = ConsoleService_1 = __decorate([
    core_1.Injectable()
], ConsoleService);
exports.ConsoleService = ConsoleService;
var ConsoleService_1;
