"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.connect = exports.Client = exports.ClientState = void 0;
const events_1 = require("events");
const socket_io_client_1 = require("socket.io-client");
var ClientState;
(function (ClientState) {
    ClientState["CLIENT_DISCONNECT"] = "CLIENT_DISCONNECT";
    ClientState["CLIENT_CONNECTING"] = "CLIENT_CONNECTING";
    ClientState["CLIENT_OK"] = "CLIENT_OK";
})(ClientState = exports.ClientState || (exports.ClientState = {}));
class Client extends events_1.EventEmitter {
    constructor(appName, socket) {
        super();
        this.appName = appName;
        this.socket = socket;
        this._state = ClientState.CLIENT_CONNECTING;
        this.debug = false;
        this.routeEvents();
        this.routePublishEvents();
    }
    get lastApiVersionConnected() {
        return this.lastApiVersion;
    }
    set state(val) {
        console.warn(new Error(`set state: ${val}`));
        this._state = val;
    }
    get state() {
        return this._state;
    }
    onSplitsUpdated(data) {
        const splitsData = Object.assign({}, data);
        if (this.data) {
            splitsData.pageSettings = Object.assign({}, this.data.pageSettings);
        }
        if (this.lastApiVersion && this.lastApiVersion !== splitsData.api_version) {
            console.warn('new server version connected', {
                old: this.lastApiVersion,
                new: splitsData.api_version
            });
            if (typeof window !== 'undefined') {
                console.warn(`OBS BROWSER PAGE RELOADING: ${new Date().toISOString()}`);
                window.location.reload(true);
            }
            else {
                console.warn('terminating process: remote controller version has changed', splitsData.api_version);
                process.exit(69);
                return;
            }
        }
        this.lastApiVersion = splitsData.api_version;
        this.data = splitsData;
        this.emit('splits-updated', splitsData);
    }
    onNextSplit(time) {
        if (!this.data) {
            return;
        }
        const data = Object.assign({}, this.data);
        for (let i = 0; i < this.data.activeSplits.length; ++i) {
            if (this.data.activeSplits[i].active) {
                if (this.data.activeSplits[i + 1]) {
                    const oldSplit = this.data.activeSplits[i];
                    const newSplit = this.data.activeSplits[i + 1];
                    this.setNextActiveSplit(newSplit, oldSplit, time);
                }
                break;
            }
        }
        this.data = data;
        this.emit('splits-updated', data, 'next-split');
    }
    onPrevSplit(time) {
        if (!this.data) {
            return;
        }
        const data = Object.assign({}, this.data);
        for (let i = 0; i < this.data.activeSplits.length; ++i) {
            if (this.data.activeSplits[i].active) {
                if (this.data.activeSplits[i - 1]) {
                    const oldSplit = this.data.activeSplits[i];
                    const newSplit = this.data.activeSplits[i - 1];
                    this.setNextActiveSplit(newSplit, oldSplit, time);
                }
                break;
            }
        }
        this.data = data;
        this.emit('splits-updated', data, 'prev-split');
    }
    onDecrementCount(name) {
        if (!this.data) {
            return;
        }
        const data = Object.assign({}, this.data);
        for (let i = 0; i < data.activeSplits.length; ++i) {
            const split = data.activeSplits[i];
            if (split.active && split[name] > 0) {
                split[name] -= 1;
                break;
            }
        }
        this.data = data;
        this.emit('splits-updated', data, 'decrement-count');
    }
    onIncrementCount(name) {
        if (!this.data) {
            return;
        }
        const data = Object.assign({}, this.data);
        for (let i = 0; i < data.activeSplits.length; ++i) {
            const split = data.activeSplits[i];
            if (split.active && split[name] < 99) {
                split[name] += 1;
                break;
            }
        }
        this.data = data;
        this.emit('splits-updated', data, 'increment-count');
    }
    onDisconnect() {
        switch (this.state) {
            case ClientState.CLIENT_DISCONNECT: {
                break;
            }
            case ClientState.CLIENT_OK: {
                this.state = ClientState.CLIENT_CONNECTING;
                break;
            }
            case ClientState.CLIENT_CONNECTING: {
                this.state = ClientState.CLIENT_CONNECTING;
                break;
            }
        }
    }
    onServerRestartNow() {
        if (typeof window !== 'undefined') {
            console.warn('server-restart-now received, reloading with forceGet = true');
            window.location.reload(true);
        }
        else {
            this.emit('server-restart-now');
        }
    }
    onServerShutdownNow() {
        console.warn('server-shutdown-now called');
        this.emit('server-shutdown-now');
    }
    onConnectError(err) {
        this.emit('websocket-connect_error', err, 'connect_error');
    }
    onConnect() {
        this.state = ClientState.CLIENT_OK;
    }
    onPageSettingsUpdate(newPageSettings) {
        console.log('on page settings update', newPageSettings);
        if (this.data) {
            if (newPageSettings.viewerSettings) {
                const keys = Object.keys(newPageSettings.viewerSettings);
                for (let i = 0; i < keys.length; ++i) {
                    const key = keys[i];
                    newPageSettings.viewerSettings[key] = Object.assign(Object.assign({}, (this.data.pageSettings.viewerSettings[key] || {})), newPageSettings.viewerSettings[key]);
                }
            }
            else {
                newPageSettings.viewerSettings = this.data.pageSettings.viewerSettings;
            }
            this.data.pageSettings = Object.assign(Object.assign(Object.assign({}, this.data.pageSettings), newPageSettings), { viewerSettings: Object.assign(Object.assign({}, this.data.pageSettings.viewerSettings), (newPageSettings.viewerSettings || {})) });
            this.emit('splits-updated', this.data, 'page-settings-update');
        }
    }
    routePublishEvents() {
        super.on('notify-configuration-available', (payload, callback) => {
            this.configuration = payload.configuration;
            return this.socket.emit('notify-configuration-available', Object.assign({ name: this.appName }, payload), callback);
        });
    }
    routeEvents(socket = this.socket) {
        socket.io.on('reconnect', this.onReconnect.bind(this));
        socket.on('connect', this.onConnect.bind(this));
        socket.on('connect_error', this.onConnectError.bind(this));
        socket.on('splits-updated', this.onSplitsUpdated.bind(this));
        socket.on('next-split', this.onNextSplit.bind(this));
        socket.on('prev-split', this.onPrevSplit.bind(this));
        socket.on('decrement-count', this.onDecrementCount.bind(this));
        socket.on('increment-count', this.onIncrementCount.bind(this));
        socket.on('disconnect', this.onDisconnect.bind(this));
        socket.on('server-restart-now', this.onServerRestartNow.bind(this));
        socket.on('server-shutdown-now', this.onServerShutdownNow.bind(this));
        socket.on('page-settings-update', this.onPageSettingsUpdate.bind(this));
    }
    on(eventType, callback) {
        return super.on(eventType, callback);
    }
    setNextActiveSplit(newSplit, oldSplit, time) {
        oldSplit.active = false;
        newSplit.active = true;
        const { data } = this;
        if (!data) {
            return;
        }
        if (data.attemptDuration.attemptActiveAt > 0) {
            const { attemptDuration } = data;
            const splitOldDuration = attemptDuration.splits[oldSplit.id];
            const splitNewDuration = attemptDuration.splits[newSplit.id];
            const splitsDurationMap = attemptDuration.splits;
            splitsDurationMap[oldSplit.id] = {
                activeAt: 0,
                milliseconds: splitOldDuration.milliseconds + (time - splitOldDuration.activeAt),
            };
            splitsDurationMap[newSplit.id] = {
                activeAt: time,
                milliseconds: splitNewDuration ? splitNewDuration.milliseconds : 0,
            };
        }
    }
    onReconnect() {
        if (this.configuration) {
            this.socket.emit('notify-configuration-available', {
                configuration: this.configuration,
            });
        }
    }
    disconnect() {
        console.warn('manual disconnect: not reconnecting now');
        this.state = ClientState.CLIENT_DISCONNECT;
        if (!this.socket.disconnected) {
            console.log('disconnect', new Error(), this.state);
            this.socket.disconnect();
        }
    }
    static connect(opts) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = typeof opts !== 'undefined' ? `${opts.host}:${opts.port}` : '/';
            const appName = typeof opts !== 'undefined' ? opts.app_name : 'Unknown App Name';
            if (typeof window !== 'undefined' && window && !opts) {
                const portRegex = /port=[0-9]+/;
                const hostRegex = /host=([a-z]|[A-Z]|-|_|[0-9])+/;
                const { search } = window.location;
                const portMatch = portRegex.exec(search);
                const hostMatch = hostRegex.exec(search);
                if (portMatch && hostMatch) {
                    const port = (portMatch[0] || '').split('=')[1];
                    const host = (hostMatch[0] || '').split('=')[1];
                    url = `${host}:${port}`;
                }
            }
            const socket = this.ioclient(`http://${url}`);
            const client = new Client(appName, socket);
            return Promise.resolve(client);
        });
    }
}
exports.Client = Client;
Client.ioclient = socket_io_client_1.io;
const connect = (opts) => __awaiter(void 0, void 0, void 0, function* () {
    return Client.connect(opts);
});
exports.connect = connect;
//# sourceMappingURL=index.js.map