

import { ClientThreadViewsShape, TextDocumentLocationProviderIDRegistry, PanelViewWithComponent, PanelViewData } from '@workbench-stack/extension-api';
import { PanelView } from 'cdecode/client';
import { handleRequests } from '@workbench-stack/extension-api';
import { ITextDocumentPositionParams } from '@workbench-stack/core';
import { ContributableViewContainer, Connection } from '@workbench-stack/extension-api';
import { map } from 'rxjs/operators';
import { ReplaySubject, Subject, Subscription, Observable } from 'rxjs';
import { ViewProviderRegistry } from '../../services/view-service';
import { SubscriptionMap } from '../common';

/** @internal */
export class ClientViews implements ClientThreadViewsShape {
    private subscriptions = new Subscription();
    private panelViews = new Map<number, Subject<PanelViewData>>();
    private registrations = new SubscriptionMap();

    constructor(
        connection: Connection,
        private viewRegistry: ViewProviderRegistry,
        private textDocumentLocations: TextDocumentLocationProviderIDRegistry,
    ) {
        this.subscriptions.add(this.registrations);
        handleRequests(connection, 'views', this);
    }

    public $unregister(id: number): void {
        this.registrations.remove(id);
    }

    public $registerPanelViewProvider(id: number, provider: { id: string }): void {
        //TODO: This will probably hang forever if an extension neglects to set any of the fields on a
        // PanelView because this subject will never emit.
        const panelView = new ReplaySubject<PanelViewData>(1);
        this.panelViews.set(id, panelView);
        const registryUnsubscribable = this.viewRegistry.registerProvider(
            { ...provider, container: ContributableViewContainer.Panel },
            panelView.pipe(
                map(
                    ({ title, content, priority, component }) =>
                        ({
                            title,
                            content,
                            priority,
                            locationProvider: component
                                ? (params: ITextDocumentPositionParams) =>
                                    this.textDocumentLocations.getLocations(component.locationProvider, params)
                                : undefined as Observable<null>,
                        } as PanelViewWithComponent),
                ),
            ),
        );
        this.registrations.add(id, {
            unsubscribe: () => {
                registryUnsubscribable.unsubscribe();
                this.panelViews.delete(id);
            },
        });
    }

    public $acceptPanelViewUpdate(id: number, data: PanelViewData): void {
        const panelView = this.panelViews.get(id);
        if (panelView === undefined) {
            throw new Error(`no panel view with ID ${id}`);
        }
        panelView.next(data);
    }

    public unsubscribe(): void {
        this.subscriptions.unsubscribe();
    }
}
