"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const Peer_1 = require("../../../../../api/peer/Peer");
const Semifunctional_1 = require("../../../../../Class/Semifunctional");
const evaluate_1 = require("../../../../../spec/evaluate");
const stringify_1 = require("../../../../../spec/stringify");
const MediaStream_1 = require("../../../../../wrap/MediaStream");
const _ids_1 = require("../../../../_ids");
class PeerReceiver extends Semifunctional_1.Semifunctional {
    constructor(system) {
        super({
            fi: ['offer', 'opt'],
            fo: ['answer'],
            i: ['close'],
            o: ['stream'],
        }, {
            input: {},
        }, system, _ids_1.ID_PEER_RECEIVER);
        this._peer = null;
        this._unlisten = undefined;
        this._connected = false;
        this._flag_err_invalid_offer = false;
        this._send_data = (data, callback) => {
            this._send({ type: 'data', data }, callback);
        };
        this._send = (data, callback) => {
            if (this._connected) {
                const message = (0, stringify_1.stringify)(data);
                this._peer.send(message);
                callback();
            }
            else {
                callback(undefined, 'peer not connected');
            }
        };
        this._setup_peer = () => {
            const connect_listener = () => {
                console.log('Receiver', 'Peer', 'connect');
                this._connected = true;
            };
            const error_listener = (err) => {
                console.log('Receiver', 'Peer', 'error', err);
                this.err(err.message);
            };
            const close_listener = () => {
                console.log('Receiver', 'Peer', 'close');
                this._disconnect();
            };
            const message_listener = (message) => {
                const specs = this.__system.specs;
                const classes = this.__system.classes;
                const data = (0, evaluate_1.evaluate)(message, specs, classes);
                console.log('Receiver', 'Peer', 'data', data);
                this.emit('message', data);
            };
            const start_listener = (stream) => {
                console.log('Receiver', 'Peer', 'start', stream);
                const _stream = (0, MediaStream_1.wrapMediaStream)(stream, this.__system);
                this._output.stream.push(_stream);
            };
            const stop_listener = () => {
                console.log('Receiver', 'Peer', 'stop');
                this._output.stream.pull();
            };
            this._peer.addListener('connect', connect_listener);
            this._peer.addListener('error', error_listener);
            this._peer.addListener('close', close_listener);
            this._peer.addListener('message', message_listener);
            this._peer.addListener('start', start_listener);
            this._peer.addListener('stop', stop_listener);
            return () => {
                this._peer.removeListener('connect', connect_listener);
                this._peer.removeListener('error', error_listener);
                this._peer.removeListener('close', close_listener);
                this._peer.removeListener('message', message_listener);
                this._peer.removeListener('start', start_listener);
                this._peer.removeListener('stop', stop_listener);
            };
        };
        this._disconnect = () => {
            if (this._unlisten) {
                this._unlisten();
                this._unlisten = undefined;
            }
            if (this._peer) {
                this._peer.destroy();
                this._peer = null;
            }
            this._connected = false;
        };
        this.addListener('destroy', () => {
            this._disconnect();
        });
        this.addListener('take_err', () => {
            if (this._flag_err_invalid_offer) {
                this._flag_err_invalid_offer = false;
                this._backward('offer');
            }
            else {
                // TODO
            }
        });
    }
    f({ offer, opt }) {
        return __awaiter(this, void 0, void 0, function* () {
            this._peer = new Peer_1.Peer(this.__system, false, opt);
            try {
                yield this._peer.acceptOffer(offer);
            }
            catch (err) {
                this._flag_err_invalid_offer = true;
                this.err('invalid offer');
                return;
            }
            const answer = yield this._peer.answer();
            this._output.answer.push(answer);
            this._setup_peer();
        });
    }
    onDataInputData(name, data) {
        return __awaiter(this, void 0, void 0, function* () {
            // console.log('Receiver', 'onDataInputData', name, data)
            if (name === 'close') {
                this._disconnect();
            }
        });
    }
    onDataInputDrop(name) {
        // console.log('Receiver', 'onDataInputDrop', name, data)
        if (name === 'opt') {
            if (!this._backwarding) {
                this._disconnect();
            }
        }
        else if (name === 'offer') {
            if (this._flag_err_invalid_offer) {
                this._flag_err_invalid_offer = false;
                this.takeErr();
                return;
            }
        }
    }
    onDataOutputDrop(name) {
        if (name === 'answer') {
            this._input.offer.pull();
        }
    }
    $send({ data }, callback) {
        this._send_data(data, callback);
    }
}
exports.default = PeerReceiver;
//# sourceMappingURL=index.js.map