import { HttpClient } from 'aurelia-fetch-client';
import { customAttribute, autoinject, useView, bindable, bindingMode, BindingEngine, Disposable } from "aurelia-framework";
import { DialogService, DialogController } from "aurelia-dialog";
import { UIPictureEdit } from "./ui-picture-edit";
import { Config } from '../../config';
import { RemoteFileStorage } from './../../services/file-storage-service';
import { UIInternal } from '../../utils/ui-internal';

@customAttribute("ui-lightbox")
@useView(null)
@autoinject
export class UILightbox {
  @bindable({ defaultBindingMode: bindingMode.twoWay }) public src: string;
  @bindable({ defaultBindingMode: bindingMode.toView }) public pictures: Array<{ url: string }>;
  @bindable({ defaultBindingMode: bindingMode.toView }) public canEdit: boolean;
  @bindable({ defaultBindingMode: bindingMode.toView }) public canDownload: boolean;
  @bindable({ defaultBindingMode: bindingMode.toView }) public showName: boolean;
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public imageType: "url" | "base64" = "url";
  @bindable({ defaultBindingMode: bindingMode.oneTime }) public isBase64Prefixed: boolean = true;
  @bindable public clickable: boolean = true;

  private suspendObservers = false;
  private disposables: Disposable[] = [];

  constructor(private image: Element, private dialogService: DialogService, private httpClient: HttpClient, private config: Config, private fileStorageService: RemoteFileStorage.Service, private bindingEngine: BindingEngine) { }

  private async toPreviewUrl(url: string) {
    if (url == null || url.trim().length == 0) return null;
    return url.match('base64,') ? url : await this.fileStorageService.fileUrl(url);
  }

  public async attached() {
    const setImageSrc = async () => {
      let src = (<HTMLImageElement>this.image).getAttribute("src") || this.src;
      if (!src?.startsWith("http") || this.fileStorageService.secure) {
        let url = this.image.getAttribute("data-url");
        if (url == null) {
          url = src;
          if (url == null) return;
          this.image.setAttribute('data-url', url);
        }

        let previewUrl: string = " ";
        this.suspendObservers = true;
        // Momently set empty url to avoid console errors
        (<HTMLImageElement>this.image).src = previewUrl;

        if (url?.trim().length > 0) {
          previewUrl = await this.toPreviewUrl(url);
        }

        UIInternal.queueTask(() => {
          (<HTMLImageElement>this.image).setAttribute("src", previewUrl);

          if (this.suspendObservers) {
            setTimeout(() => this.suspendObservers = false, 500);
          }
        });
      } else {
        // Ensure src is set
        (<HTMLImageElement>this.image).setAttribute("src", src);
      }
    }

    await setImageSrc();

    const srcObserver = async (newValue: string, oldValue: string) => {
      if (newValue != oldValue && !this.suspendObservers) {
        this.suspendObservers = true;
        this.image.setAttribute('data-url', newValue);
        setImageSrc();
      }
    }

    this.disposables.splice(0, this.disposables.length, ...[
      this.bindingEngine.propertyObserver(this.image, "src").subscribe((newValue, oldValue) => {
        if (newValue != oldValue) {
          // Take real value of src attribute
          newValue = (<HTMLImageElement>this.image).getAttribute("src") || newValue;
          srcObserver(newValue, oldValue);
        }
      }),
      this.bindingEngine.propertyObserver(this, "src").subscribe(srcObserver)
    ]);

    if (this.clickable) {
      (<HTMLImageElement>this.image).onclick = async () => {
        let url = this.image.getAttribute("data-url");
        if (url == null) {
          url = (<HTMLImageElement>this.image).getAttribute("src") || this.src;
          this.image.setAttribute('data-url', url);
        }
        let previewUrl = await this.toPreviewUrl(url);

        this.dialogService.open({ viewModel: UILightboxModal, model: { previewSrc: previewUrl, src: url, pictures: this.pictures, caption: this.image.getAttribute("alt"), canEdit: this.canEdit, canDownload: this.canDownload, showName: this.showName, imageType: this.imageType }, lock: true }).whenClosed((closeResult) => {
          if (!closeResult.wasCancelled && this.imageType == "base64") {
            let base64: string = closeResult.output as string;
            if (!this.isBase64Prefixed) {
              base64 = base64.substring(base64.indexOf(",") + 1);
            }
            this.src = base64;
          }
        });
      };
    }

  }

  private detached() {
    this.disposables?.forEach(x => x.dispose());
  }
}

@useView('./ui-lightbox.html')
@autoinject
export class UILightboxModal {
  public src: string;
  public pictures: Array<{ url: string, previewSrc: string, name: string }>;
  public caption: string;
  public canEdit: boolean;
  public canDownload: boolean;
  public showName: boolean;
  public imageType: "url" | "base64" = "url";

  private currentIndex: number;
  public image: Element;
  private handleShortcutsEvent: (event: KeyboardEvent) => void;

  constructor(private dialogController: DialogController, private dialogService: DialogService, private fileStorageService: RemoteFileStorage.Service) { }

  public get fileName() {
    if (this.pictures.length == 0) {
      return '';
    }
    return this.pictures[this.currentIndex]?.name ?? '';
  }

  public activate(model: { previewSrc: string, src: string, pictures: Array<{ url: string, previewSrc: string, name: string }>, caption: string, canEdit: boolean, canDownload: boolean, showName: boolean, imageType: "url" | "base64" }) {
    this.src = model.previewSrc;
    this.pictures = model.pictures;
    this.caption = model.caption;
    this.canEdit = model.canEdit;
    this.canDownload = model.canDownload;
    this.showName = model.showName;
    this.imageType = model.imageType;

    if (this.pictures != null && this.pictures.length > 0) {
      this.currentIndex = this.pictures.findIndex(x => x.url == model.src);
    }
  }

  public attached() {
    if (this.currentIndex != null) {
      this.handleShortcutsEvent = (event: KeyboardEvent) => {
        if (event.key == "ArrowRight") {
          event.stopImmediatePropagation();
          event.preventDefault();
          this.nextPicture();
        }
        else if (event.key == "ArrowLeft") {
          event.stopImmediatePropagation();
          event.preventDefault();
          this.previousPicture();
        }
      };
      document.addEventListener("keydown", this.handleShortcutsEvent, false);
    }
  }

  public detached() {
    if (this.currentIndex != null) {
      document.removeEventListener("keydown", this.handleShortcutsEvent, false);
    }
  }

  public async previousPicture() {
    this.image.classList.remove("slide-left");
    this.image.classList.remove("slide-right");
    this.currentIndex = (this.currentIndex <= 0 ? this.pictures.length : this.currentIndex) - 1;
    let previewUrl = await this.fileStorageService.fileUrl(this.pictures[this.currentIndex].url);
    this.src = previewUrl;
    this.image.classList.add("slide-left");
    setTimeout(() => {
      this.image.classList.remove("slide-left");
    }, 250);

    this.caption = null;
  }

  public async nextPicture() {
    this.image.classList.remove("slide-left");
    this.image.classList.remove("slide-right");
    this.currentIndex = (this.currentIndex == this.pictures.length - 1 ? 0 : this.currentIndex + 1);
    let previewUrl = await this.fileStorageService.fileUrl(this.pictures[this.currentIndex].url);
    this.src = previewUrl;

    this.image.classList.add("slide-right");
    setTimeout(() => {
      this.image.classList.remove("slide-right");
    }, 250);

    this.caption = null;
  }

  public async editPicture() {
    let previewUrl = await this.fileStorageService.fileUrl(this.pictures[this.currentIndex].url);
    await this.dialogService.open({ viewModel: UIPictureEdit, model: { url: (this.currentIndex != null ? this.pictures[this.currentIndex].url : this.src), previewUrl: previewUrl, type: this.imageType }, lock: true }).whenClosed(async (closeResult) => {
      if (!closeResult.wasCancelled) {
        let cache = await caches.open("pictures");
        this.src = null;
        if (this.currentIndex != null) {
          this.pictures[this.currentIndex].url = null;
          if (this.imageType == "url") {
            cache.delete(closeResult.output);
            cache.add(closeResult.output);
          }
          this.pictures[this.currentIndex].url = closeResult.output;
          previewUrl = await this.fileStorageService.fileUrl(this.pictures[this.currentIndex].url);
          this.pictures[this.currentIndex].previewSrc = previewUrl;
          (this.pictures[this.currentIndex] as any).entityAspect?.rejectChanges();
        }
        else {
          if (this.imageType == "url") {
            cache.delete(closeResult.output);
            cache.add(closeResult.output);
          }
        }
        // this.src = closeResult.output;
        this.src = previewUrl;
      }
    });
  }

  public async download() {
    this.fileStorageService.download(this.pictures[this.currentIndex].name);
  }
}
