Загрузка данных


import type { PriceScaleMode } from 'lightweight-charts';

export interface PriceScaleTarget {
  paneId: number;
  priceScaleId: string;
}

export interface PriceScaleSnapshot extends PriceScaleTarget {
  mode: PriceScaleMode;
}


import { PriceScaleMode } from 'lightweight-charts';
import { BehaviorSubject, distinctUntilChanged, map, Observable } from 'rxjs';

import type { Pane } from '@core/Pane';

import type { PriceScaleSnapshot, PriceScaleTarget } from './types';

interface PriceScaleControllerParams {
  getPaneById: (paneId: number) => Pane | undefined;
  initialPriceScales?: PriceScaleSnapshot[];
}

export class PriceScaleController {
  private readonly getPaneById: (paneId: number) => Pane | undefined;
  private readonly userModes = new Map<number, Map<string, PriceScaleSnapshot>>();
  private readonly policyModes = new Map<number, Map<string, PriceScaleMode>>();
  private readonly changes$ = new BehaviorSubject(0);

  constructor({ getPaneById, initialPriceScales = [] }: PriceScaleControllerParams) {
    this.getPaneById = getPaneById;
    this.setSnapshot(initialPriceScales);
  }

  public changes(): Observable<number> {
    return this.changes$.asObservable();
  }

  public mode$(target: PriceScaleTarget): Observable<PriceScaleMode> {
    return this.changes$.pipe(
      map(() => this.getMode(target)),
      distinctUntilChanged(),
    );
  }

  public getMode(target: PriceScaleTarget): PriceScaleMode {
    const policyMode = this.getPolicyMode(target);

    if (policyMode !== PriceScaleMode.Normal) {
      return policyMode;
    }

    return this.getUserMode(target);
  }

  public getUserMode(target: PriceScaleTarget): PriceScaleMode {
    return this.userModes.get(target.paneId)?.get(target.priceScaleId)?.mode ?? PriceScaleMode.Normal;
  }

  public setMode(target: PriceScaleTarget, mode: PriceScaleMode): void {
    if (this.getUserMode(target) === mode) {
      return;
    }

    this.setUserMode(target, mode);
    this.applyMode(target);
    this.emitChange();
  }

  public toggleMode(target: PriceScaleTarget, mode: PriceScaleMode): void {
    this.setMode(
      target,
      this.getUserMode(target) === mode ? PriceScaleMode.Normal : mode,
    );
  }

  public setPolicyMode(target: PriceScaleTarget, mode: PriceScaleMode): void {
    if (this.getPolicyMode(target) === mode) {
      return;
    }

    this.setPolicyModeState(target, mode);
    this.applyMode(target);
    this.emitChange();
  }

  public applyAll(): void {
    this.getSnapshot().forEach(({ paneId, priceScaleId }) => {
      this.applyMode({ paneId, priceScaleId });
    });

    this.policyModes.forEach((paneModes, paneId) => {
      paneModes.forEach((_, priceScaleId) => {
        this.applyMode({ paneId, priceScaleId });
      });
    });
  }

  public getSnapshot(): PriceScaleSnapshot[] {
    return Array.from(this.userModes.values())
      .flatMap((paneModes) => Array.from(paneModes.values()))
      .filter(({ paneId }) => Boolean(this.getPaneById(paneId)));
  }

  public setSnapshot(snapshot: PriceScaleSnapshot[]): void {
    this.userModes.clear();

    snapshot.forEach(({ paneId, priceScaleId, mode }) => {
      this.setUserMode({ paneId, priceScaleId }, mode);
    });

    this.emitChange();
  }

  public destroy(): void {
    this.userModes.clear();
    this.policyModes.clear();
    this.changes$.complete();
  }

  private getPolicyMode(target: PriceScaleTarget): PriceScaleMode {
    return this.policyModes.get(target.paneId)?.get(target.priceScaleId) ?? PriceScaleMode.Normal;
  }

  private setUserMode(target: PriceScaleTarget, mode: PriceScaleMode): void {
    if (mode === PriceScaleMode.Normal) {
      this.userModes.get(target.paneId)?.delete(target.priceScaleId);
      return;
    }

    const paneModes = this.userModes.get(target.paneId) ?? new Map<string, PriceScaleSnapshot>();

    paneModes.set(target.priceScaleId, {
      ...target,
      mode,
    });

    this.userModes.set(target.paneId, paneModes);
  }

  private setPolicyModeState(target: PriceScaleTarget, mode: PriceScaleMode): void {
    if (mode === PriceScaleMode.Normal) {
      this.policyModes.get(target.paneId)?.delete(target.priceScaleId);
      return;
    }

    const paneModes = this.policyModes.get(target.paneId) ?? new Map<string, PriceScaleMode>();

    paneModes.set(target.priceScaleId, mode);
    this.policyModes.set(target.paneId, paneModes);
  }

  private applyMode(target: PriceScaleTarget): void {
    const pane = this.getPaneById(target.paneId);

    if (!pane) {
      return;
    }

    pane.getPriceScale(target.priceScaleId).applyOptions({
      mode: this.getMode(target),
    });
  }

  private emitChange(): void {
    this.changes$.next(this.changes$.value + 1);
  }
}


export { PriceScaleController } from './controller';

export type {
  PriceScaleSnapshot,
  PriceScaleTarget,
} from './types';