"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Peer_1 = require("../../../../../api/peer/Peer");
const _1 = require("../../../../../Class/$");
const Holder_1 = require("../../../../../Class/Holder");
const _ids_1 = require("../../../../_ids");
class PeerTransmitter extends Holder_1.Holder {
    constructor(system) {
        super({
            fi: ['opt'],
            i: ['answer', 'stream'],
            o: ['offer', 'channel'],
        }, {
            input: {
                stream: {
                    ref: true,
                },
            },
            output: {
                channel: {
                    ref: true,
                },
            },
        }, system, _ids_1.ID_PEER_TRANSMITTER, 'close');
        this._peer = undefined;
        this._unlisten = undefined;
        this._connected = false;
        this._offered = false;
        this._flag_err_peer_construct = false;
        this._flag_err_answer_without_offer = false;
        this._stream = null;
        this._add_stream = (stream) => {
            // console.log('Peer', '_add_stream')
            this._stream = stream;
            if (this._peer) {
                this._peer.addStream(stream);
            }
        };
        this._remove_stream = (stream) => {
            // console.log('Transmitter', '_remove_stream')
            this._peer.removeStream();
        };
        this._output_port = () => {
            // console.log('Transmitter', '_output_port')
            const peer = this._peer;
            const channel = new (class Channel extends _1.$ {
                constructor() {
                    super(...arguments);
                    this.__ = ['MS'];
                }
                async send(data) {
                    peer.send(data);
                    return;
                }
            })(this.__system);
            this._output.channel.push(channel);
        };
        this._setup_peer = () => {
            // console.log('Transmitter', '_setup_peer')
            const signal_listener = (signal) => {
                // console.log('Transmitter', 'signal', signal)
                const { sdp } = signal;
                this._output.offer.push(sdp);
            };
            const connect_listener = () => {
                // console.log('Transmitter', 'connect')
                this._connected = true;
                this._output_port();
            };
            const error_listener = (err) => {
                // console.log('Transmitter', 'error', err)
                this.err(err.message);
            };
            const close_listener = () => {
                // console.log('Transmitter', 'close')
                this._disconnect();
            };
            this._peer.addListener('connect', connect_listener);
            this._peer.addListener('error', error_listener);
            this._peer.addListener('close', close_listener);
            return () => {
                this._peer.removeListener('connect', connect_listener);
                this._peer.removeListener('error', error_listener);
                this._peer.removeListener('close', close_listener);
            };
        };
        this._disconnect = () => {
            if (!this._connected) {
                return;
            }
            this._forward_empty('offer');
            this._forward_empty('channel');
        };
        this.addListener('take_err', () => {
            if (!this._backwarding) {
                if (this._flag_err_answer_without_offer) {
                    this._flag_err_answer_without_offer = false;
                    this._backward('answer');
                }
                else if (this._flag_err_peer_construct) {
                    this._flag_err_peer_construct = false;
                    this._backward('opt');
                }
                else {
                    throw new Error('PeerTransmitter: invalid err state');
                }
            }
        });
    }
    async f({ opt }) {
        try {
            this._peer = new Peer_1.Peer(this.__system, true, opt);
        }
        catch (err) {
            const { message } = err;
            const FAIL_TO_CONSTRUCT_MSG_START = `Failed to construct 'RTCPeerConnection': `;
            if (message.startsWith(FAIL_TO_CONSTRUCT_MSG_START)) {
                const _err = message
                    .substr(0, message.length - 1)
                    .replace(FAIL_TO_CONSTRUCT_MSG_START, '');
                this.err(_err);
                return;
            }
            else {
                this.err(err.message.toLowerCase());
                return;
            }
        }
        this._unlisten = this._setup_peer();
        const offer = await this._peer.offer();
        this._offered = true;
        this._output.offer.push(offer);
    }
    d() {
        if (this._connected) {
            const unlisten = this._unlisten;
            unlisten();
            this._unlisten = undefined;
            this._peer.close();
            this._peer = undefined;
            this._connected = false;
        }
    }
    async onRefInputData(name, unit) {
        // if (name === 'stream') {
        const _stream = await unit.mediaStream();
        if (_stream === null) {
            if (this._stream) {
                this._remove_stream(this._stream);
            }
            this._stream = null;
        }
        else {
            if (this._stream) {
                this._remove_stream(this._stream);
            }
            this._add_stream(_stream);
            this._stream = _stream;
        }
        // }
    }
    async onIterDataInputData(name, data) {
        // console.log('Transmitter', 'onIterDataInputData', name, data)
        super.onIterDataInputData(name, data);
        if (this.hasErr()) {
            this._backwarding = true;
            this.takeErr();
            this._backwarding = false;
        }
        if (name === 'answer') {
            const sdp = data;
            if (this._offered) {
                try {
                    await this._peer.acceptAnswer(sdp);
                }
                catch (err) {
                    this.err(err.message);
                    return;
                }
            }
            else {
                this._flag_err_answer_without_offer = true;
                this.err('cannot answer without offer');
            }
        }
    }
    onDataInputDrop(name) {
        // console.log('PeerTransmitter', 'onDataInputDrop', name)
        if (name === 'answer') {
            if (this._flag_err_answer_without_offer) {
                this.takeErr();
            }
            if (this._connected) {
                this._disconnect();
            }
        }
    }
    onDataOutputDrop(name) {
        // console.log('PeerTransmitter', 'onDataOutputDrop', name)
        if (this._connected) {
            this._output_port();
        }
    }
    onRefInputDrop(name) {
        // if (name === 'stream') {
        if (this._unlisten_stream) {
            this._unlisten_stream();
            this._unlisten_stream = undefined;
        }
        if (this._stream) {
            this._stream = null;
            if (this._peer) {
                this._remove_stream(this._stream);
            }
        }
        // }
    }
    async _send_data(data) {
        return this._send(data);
    }
    async _send(data) {
        this._peer.send(data);
        return;
    }
    async send(data) {
        return this._send_data(data);
    }
}
exports.default = PeerTransmitter;
//# sourceMappingURL=index.js.map