import { Slide } from '@netless/slide';
import { delay } from './utils';

export interface PoolItem {
  key: string;
  slide: Slide;
}

export class SlidePool {
  private pool: PoolItem[] = [];
  private maxActiveSize: number = 2;
  private renderingQueue: string[] = [];
  private renderingQueueCallback: (() => void) | undefined;
  
  constructor(maxActiveSize?: number) {
    if (maxActiveSize) {
      this.setMaxActiveSize(maxActiveSize);
    }
  }
  
  private async freeze(poolItem: PoolItem): Promise<boolean> {
    return new Promise<boolean>((resolve, _reject) => {
      if (!poolItem.slide.view) {
        resolve(false);
      } else {
        poolItem.slide.frozen(() => {
          resolve(true);
        });
      }
    });
  }
  
  private async unfreeze(poolItem: PoolItem): Promise<boolean> {
    return new Promise<boolean>((resolve, _reject) => {
      if (poolItem.slide.view) {
        resolve(false);
      } else {
        poolItem.slide.release(() => {
          resolve(true);
        });
      }
    });
  }

  private async checkPool() {
    for (let i = 0; i < this.pool.length; i ++) {
      const poolItem = this.pool[i];
      if (i < this.maxActiveSize) {
        await this.unfreeze(poolItem);
      } else {
        await this.freeze(poolItem);
      }
    }
  }

  onRenderEnd(appId: string, isFocus: boolean) {
    const index = this.renderingQueue.findIndex(item => item === appId);
    if (index > -1) {
      this.renderingQueue.splice(index, 1);
    }
    if (isFocus) {
      this.renderingQueueCallback = () => {
        const poolItem = this.pool.find(item => item.key === appId);
        if (poolItem) {
          this.active(appId, poolItem.slide);
        }
      };
    }
    if (this.renderingQueue.length === 0 && this.renderingQueueCallback) {
      this.renderingQueueCallback();
    }
  }

  setMaxActiveSize(maxActiveSize: number) {
    this.maxActiveSize = maxActiveSize > 8 ? 8 : maxActiveSize;
    if (maxActiveSize > 8) {
      console.warn('maxActiveSize should not be greater than 8');
    }
  }

  async waitUntilReady(appId: string) {
    if (this.renderingQueue.length < this.maxActiveSize) {
      this.renderingQueue.push(appId);
      return;
    } else {
      await delay(200);
      await this.waitUntilReady(appId);
    }
  }

  async active(key: string, slide: Slide) {
    const index = this.pool.findIndex(item => item.key === key);
    if (index < 0) {
      this.pool.unshift({ key, slide });
    } else {
      this.pool.splice(index, 1);
      this.pool.unshift({ key, slide });
    }
    await this.checkPool();
  }

  remove(key: string) {
    const index = this.pool.findIndex(item => item.key === key);
    if (index >= 0) {
      this.pool.splice(index, 1);
    }
  }
}

export const slidePool = new SlidePool();