import { SlideApplicationOption } from './SlideApplication';
import EventEmitter from 'eventemitter3';
import { kvStore } from '@netless/forge-room';

interface SideBarViewEvents {
    pageChange: (index: number) => void;
}

export class SideBarView extends EventEmitter<SideBarViewEvents> {
  public readonly root: HTMLDivElement = document.createElement('div');
  private isShow: boolean = false;
  private eventsMap: Map<HTMLDivElement, (() => void)[]> = new Map();
  private taskId: string = '';
  private prefix: string = '';
  private addImagesPool: number[] = [];
  private scheduleId: number = 0;
  private isSchedule = false;

  public constructor() {
    super();
    this.root.style.backgroundColor = '#eee';
    this.root.className = 'forge-slide-sidebar';
    this.root.style.width = '240px';
    this.root.style.height = '100%';
    this.root.style.position = 'absolute';
    this.root.style.left = '-240px';
    this.root.style.top = '0';
    this.root.style.zIndex = '5';
    this.root.style.transition = 'left 0.3s ease-in-out';
    this.root.style.overflow = 'auto';
    this.root.style.display = 'flex';
    this.root.style.flexDirection = 'column';
    this.root.style.justifyContent = 'flex-start';
    this.root.style.alignItems = 'center';
    this.root.style.userSelect = 'none';
  }

  get isShowSideBar(): boolean {
    return this.isShow;
  }

  public async getPreviewImage(imageUrl: string) {
    const image = await fetch(imageUrl);
    const blob = await image.blob();
    return new Promise<PreviewImage>(resolve => {
      const reader = new FileReader();
      reader.onloadend = () => {
        const base64Data = reader.result as string;
        const img = document.createElement('img');
        img.src = base64Data;
        img.onload = () => {
          resolve({
            url: imageUrl,
            src: base64Data,
            width: img.width,
            height: img.height,
          });
        };
      };
      reader.readAsDataURL(blob);
    });
  }

  private async appendPreviewImage(pageIndex: number) {
    const itemContainer = document.createElement('div');
    itemContainer.style.width = '60%';
    itemContainer.style.display = 'flex';
    itemContainer.style.justifyContent = 'center';
    itemContainer.style.alignItems = 'flex-start';
    itemContainer.style.position = 'relative';
    itemContainer.style.borderRadius = '4px';
    itemContainer.style.transition = 'border-color .3s';
    itemContainer.style.marginBottom = '10px';

    const onMouseOverHandle = () => {
      itemContainer.style.borderColor = '#ccc';
    };
    const onMouseOutHandle = () => {
      itemContainer.style.borderColor = 'transparent';
    };
    const onClickHandle = () => {
      this.emit('pageChange', pageIndex);
    };

    itemContainer.addEventListener('click', onClickHandle);
    itemContainer.addEventListener('mouseover', onMouseOverHandle);
    itemContainer.addEventListener('mouseout', onMouseOutHandle);
    this.eventsMap.set(itemContainer, [onClickHandle, onMouseOverHandle, onMouseOutHandle]);

    const pageIndexContainer = document.createElement('span');
    pageIndexContainer.textContent = `${pageIndex}`;
    pageIndexContainer.style.position = 'absolute';
    pageIndexContainer.style.top = '1px';
    pageIndexContainer.style.left = '-10px';
    pageIndexContainer.style.transform = 'translate(-100%)';
    pageIndexContainer.style.fontSize = '12px';
    pageIndexContainer.style.color = '#5f5f5f';

    const previewUrl = `${this.prefix}/${this.taskId}/preview/${pageIndex}.png`;
    const preview = document.createElement('img');
    const cachePreview = await kvStore.getItem(previewUrl);
    if (cachePreview) {
      const { src } = JSON.parse(cachePreview);
      preview.src = src;
    } else {
      const previewInfo = await this.getPreviewImage(previewUrl);
      await kvStore.setItem(previewUrl, JSON.stringify(previewInfo));
      const { src } = previewInfo;
      preview.src = src;
    }

    preview.style.width = '100%';
    preview.style.display = 'inline-block';

    itemContainer.appendChild(preview);
    itemContainer.appendChild(pageIndexContainer);

    this.root.appendChild(itemContainer);
  }

  private async scheduleGetPreviewImage() {
    if (!this.isSchedule) {
      this.scheduleId = setTimeout(async () => {
        await this.scheduleGetPreviewImage();
      }, 32) as unknown as number;
      return;
    }
    const pageIndex = this.addImagesPool.shift();
    if (!pageIndex) {
      clearTimeout(this.scheduleId);
      this.scheduleId = 0;
      return;
    }
    await this.appendPreviewImage(pageIndex);
    this.scheduleId = setTimeout(async () => {
      await this.scheduleGetPreviewImage();
    }, 32) as unknown as number;
  }

  public async startGetPreviewImageSchedule() {
    this.isSchedule = true;
  }

  public pauseGetPreviewImageSchedule() {
    this.isSchedule = false;
  }

  public initialize(slideCount: number, option: SlideApplicationOption): void {
    this.taskId = option.taskId;
    this.prefix = option.prefix;
    for (let i = 1; i <= slideCount; i++) {
      this.addImagesPool.push(i);
    }
    this.scheduleGetPreviewImage();
  }

  public hidden() {
    if (!this.root || !this.isShow) {
      return;
    }
    this.root.style.left = '-240px';
    this.root.style.border = 'none';
    this.root.style.boxShadow = 'none';
    this.isShow = false;
  }

  public show() {
    if (!this.root) {
      return;
    }
    this.root.style.left = '0';
    this.root.style.border = '1px solid #ccc';
    this.root.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
    this.isShow = true;
  }

  public dispose() {
    this.removeAllListeners();
    this.eventsMap.forEach((handlers, element) => {
      const [clickEvent, mouseOverEvent, mouseOutEvent] = handlers;
      element.removeEventListener('click', clickEvent);
      element.removeEventListener('mouseover', mouseOverEvent);
      element.removeEventListener('mouseout', mouseOutEvent);
    });
  }
}

interface PreviewImage {
  url: string;
  src: string;
  width: number;
  height: number;
}
