"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 });
exports.Peer = void 0;
const events_1 = require("./events");
const rainway_ffi_1 = require("./rainway-ffi");
const data_channel_1 = require("./data-channel");
const native_1 = require("./native");
const impls_1 = require("./events/impls");
const Callback_RainwayVoid__RainwayError_1 = require("./callbacks/Callback_RainwayVoid__RainwayError");
const Handler_RainwayPeerConnectionStreamRequest_1 = require("./events/Handler_RainwayPeerConnectionStreamRequest");
const Handler_RainwayPeerConnectionDataChannel_1 = require("./events/Handler_RainwayPeerConnectionDataChannel");
const Handler_RainwayPeerConnectionStateChanged_1 = require("./events/Handler_RainwayPeerConnectionStateChanged");
/**
 * An object representing a connection to a remote Rainway peer.
 */
class Peer extends events_1.EventEmitter {
    /** @internal */
    constructor(connection, id, externalId) {
        super();
        this.connection = connection;
        this.id = id;
        this.externalId = externalId !== null && externalId !== void 0 ? externalId : "";
        this._state = rainway_ffi_1.RainwayPeerState.New;
        this._dataChannels = new Map();
        this._outboundStreams = new Map();
        this._onStateChanged = new Handler_RainwayPeerConnectionStateChanged_1.Handler_RainwayPeerConnectionStateChanged((ev) => {
            const newState = ev;
            this._state = newState;
            this.emit("connection-state-change", new impls_1.PeerStateChangeEvent(newState));
            if (newState == rainway_ffi_1.RainwayPeerState.Failed) {
                this.emit("close", new impls_1.CloseEvent(new Error(`Transitioned to state ${rainway_ffi_1.RainwayPeerState[newState]}`)));
            }
        });
        (0, native_1.invokeOrError)("rainway_peer_connection_set_state_change_handler", () => native_1.native.rainway_peer_connection_set_state_change_handler(this.connection.id.toString(), this.id.toString(), this._onStateChanged.handler));
        this._onStreamRequest = new Handler_RainwayPeerConnectionStreamRequest_1.Handler_RainwayPeerConnectionStreamRequest((ev) => {
            this.emit("stream-request", new impls_1.StreamRequestEvent(this, ev, (stream) => {
                this._outboundStreams.set(stream.id, stream);
                stream.addEventListener("close", () => {
                    this._outboundStreams.delete(stream.id);
                });
            }));
        });
        (0, native_1.invokeOrError)("rainway_peer_connection_set_stream_request_handler", () => native_1.native.rainway_peer_connection_set_stream_request_handler(this.connection.id.toString(), this.id.toString(), this._onStreamRequest.handler));
        this._onDataChannel = new Handler_RainwayPeerConnectionDataChannel_1.Handler_RainwayPeerConnectionDataChannel((ev) => {
            var _a;
            const id = (_a = ev.name) !== null && _a !== void 0 ? _a : "";
            const dc = new data_channel_1.DataChannel(this, ev.mode, id);
            this._dataChannels.set(id, dc);
            this.emit("datachannel", new impls_1.DataChannelEvent(dc));
            // TODO(bengreenier): we currently don't have the ability to close via native ABI :(
            // when we do, hook up close handler to remove from this._dataChannels
        });
        (0, native_1.invokeOrError)("rainway_peer_connection_set_data_channel_opened_handler", () => native_1.native.rainway_peer_connection_set_data_channel_opened_handler(this.connection.id.toString(), this.id.toString(), this._onDataChannel.handler));
    }
    /**
     * A map from channel ids (labels) to Data Channels opened between us and this peer.
     */
    get dataChannels() {
        return this._dataChannels;
    }
    /**
     * A map from Stream IDs to active streams associated with this peer.
     */
    get outboundStreams() {
        return this._outboundStreams;
    }
    /**
     * The peer's network connectivity state.
     */
    get state() {
        return this._state;
    }
    /**
     * Creates a data channel.
     * @param options - data channel options
     * @returns data channel object
     */
    createDataChannel(options) {
        return __awaiter(this, void 0, void 0, function* () {
            const cb = new Callback_RainwayVoid__RainwayError_1.Callback_RainwayVoid__RainwayError();
            (0, native_1.invokeOrError)("rainway_peer_connection_create_data_channel", () => native_1.native.rainway_peer_connection_create_data_channel(new rainway_ffi_1.RainwayPeerConnectionCreateDataChannelOptionsDef({
                connectionId: this.connection.id.toString(),
                peerId: this.id.toString(),
                mode: options.mode,
                name: options.id,
                callbackState: cb.callbackState,
            })));
            return cb.promise.then(() => {
                const dc = new data_channel_1.DataChannel(this, options.mode, options.id);
                this._dataChannels.set(dc.id, dc);
                this.emit("datachannel", new impls_1.DataChannelEvent(dc));
                return dc;
            });
        });
    }
    /**
     * Destroys the peer connection, including all sub resources.
     */
    close() {
        (0, native_1.invokeOrError)("rainway_peer_connection_close", () => native_1.native.rainway_peer_connection_close(this.connection.id.toString(), this.id.toString()));
        this.emit("close", new impls_1.CloseEvent(new Error()));
    }
}
exports.Peer = Peer;
//# sourceMappingURL=peer.js.map