const regFileExtension = /(?:\.([^.]+))?$/;

export class FileUtil {
  static getExtension(fileName: string): string | undefined {
    return regFileExtension.exec(fileName)[1];
  }

  static getExtensions(fileList: FileList): string[] {
    const extensions: string[] = [];
    // tslint:disable-next-line: prefer-for-of
    for (let i = 0; i < fileList.length; i++) {
      extensions.push(FileUtil.getExtension(fileList[i].name));
    }
    return extensions;
  }

  static fromBlobToDataUrl(blob: Blob): Promise<string | ArrayBuffer> {
    return new Promise<string | ArrayBuffer>((resolve, reject) => {
      const fileReader = new FileReader();
      fileReader.onload = () => {
        return resolve(fileReader.result);
      };
      fileReader.onerror = (event: ProgressEvent) => {
        fileReader.abort();
        return reject(fileReader.error);
      };
      fileReader.readAsDataURL(blob);
    });
  }

  static fromBlobToBuffer(blob: Blob): Promise<Buffer> {
    return new Promise<Buffer>((resolve, reject) => {
      const fileReader = new FileReader();
      fileReader.onload = () => {
        return resolve(Buffer.from(fileReader.result as ArrayBuffer));
      };
      fileReader.onerror = (event: ProgressEvent) => {
        fileReader.abort();
        return reject(fileReader.error);
      };
      fileReader.readAsArrayBuffer(blob);
    });
  }

  static fromBlobToString(blob: Blob): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      const fileReader = new FileReader();
      fileReader.onload = () => {
        return resolve(fileReader.result.toString());
      };
      fileReader.onerror = (event: ProgressEvent) => {
        fileReader.abort();
        return reject(fileReader.error);
      };
      fileReader.readAsArrayBuffer(blob);
    });
  }

  static downloadFromBlob(blob: Blob, fileName: string) {
    const a = document.createElement('a');
    document.body.appendChild(a);
    a.style.display = 'none';

    const url = URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
    a.remove();
  }

  static save(
    buffer: Buffer,
    fileName: string,
    mimeType: string
  ): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      const defaultMime = 'application/octet-stream';
      const mime = !!mimeType ? mimeType : defaultMime;
      const blob = new Blob([buffer], { type: mime });

      if (navigator.msSaveBlob) {
        // IE10+ : (has Blob, but not a[download] or URL)
        navigator.msSaveBlob(blob, fileName);
        resolve(fileName);
        return;
      }

      const download = (url: string, mode: boolean = false) => {
        const anchor = document.createElement('a');

        if ('download' in anchor) {
          // html5 A[download]
          anchor.href = url;
          anchor.setAttribute('download', fileName);
          anchor.className = 'download-js-link';
          anchor.innerHTML = 'downloading...';
          anchor.style.display = 'none';
          document.body.appendChild(anchor);

          setTimeout(() => {
            anchor.click();
            document.body.removeChild(anchor);
            resolve(fileName);
            if (mode) {
              setTimeout(() => {
                window.URL.revokeObjectURL(anchor.href);
              }, 250);
            }
          }, 66);
          return;
        }

        // handle non-a[download] safari as best we can:
        if (
          /(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(
            navigator.userAgent
          )
        ) {
          url = url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
          if (!window.open(url)) {
            // popup blocked, offer direct download:
            if (
              confirm(
                'Displaying New Document\n\nUse Save As... to download, then click back to return to this page.'
              )
            ) {
              location.href = url;
            }
          }
          resolve(fileName);
          return;
        }

        // do iframe dataURL download (old ch+FF):
        const f = document.createElement('iframe');
        document.body.appendChild(f);

        if (!mode) {
          // force a mime that will download:
          url = 'data:' + url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
        }
        f.src = url;
        setTimeout(() => {
          document.body.removeChild(f);
        }, 333);
      };

      if (window.URL) {
        download(window.URL.createObjectURL(blob), true);
      } else {
        if (
          typeof blob === 'string' ||
          (blob as any).constructor === this.toString
        ) {
          try {
            download(
              'data:' + mime + ';base64,' + window.btoa((blob as any) as string)
            );
          } catch (e) {
            download(
              'data:' +
                mime +
                ';base64,' +
                encodeURIComponent((blob as any) as string)
            );
          }
        } else {
          const fileReader = new FileReader();
          fileReader.onload = () => {
            download(fileReader.result as string);
          };
          fileReader.readAsDataURL(blob);
        }
      }
    });
  }

  static thumbnail(file: File): Promise<File> {
    return new Promise<File>((resolve, reject) => {
      const fileReader = new FileReader();
      fileReader.onload = () => {
        const blob = new Blob([fileReader.result], { type: file.type });
        const url = URL.createObjectURL(blob);
        const video = document.createElement('video');

        const snapImage = () =>
          new Promise<Blob>((imgResolve, imgReject) => {
            const canvas = document.createElement('canvas');

            // thumbnail image resize
            const width = video.videoWidth;
            const height = video.videoHeight;
            let resizeWidth = 0;
            let resizeHeight = 0;

            // max size
            const maxWidth = 320;
            const maxHeight = 320;

            if (width > maxWidth || height > maxHeight) {
              if (width > height) {
                resizeWidth = maxWidth;
                resizeHeight = Math.round((height * resizeWidth) / width);
              } else {
                resizeHeight = maxHeight;
                resizeWidth = Math.round((width * resizeHeight) / height);
              }
            } else {
              resizeWidth = width;
              resizeHeight = height;
            }

            canvas.width = resizeWidth;
            canvas.height = resizeHeight;
            canvas
              .getContext('2d')
              .drawImage(video, 0, 0, canvas.width, canvas.height);

            canvas.toBlob(imgBlob => {
              imgResolve(imgBlob);
            });
          });

        const timeupdate = () => {
          snapImage().then(imgBlob => {
            video.removeEventListener('timeupdate', timeupdate);
            video.pause();
            resolve(new File([imgBlob], file.name));
          });
        };

        video.onloadeddata = () => {
          snapImage().then(imgBlob => {
            video.removeEventListener('timeupdate', timeupdate);
            resolve(new File([imgBlob], file.name));
          });
        };

        video.addEventListener('timeupdate', timeupdate);
        video.preload = 'metadata';
        video.src = url;
        // Load video in Safari / IE11
        video.muted = true;
        // tslint:disable-next-line: no-string-literal
        video['playsInline'] = true;
        video.play();
      };
      fileReader.onerror = (event: ProgressEvent) => {
        fileReader.abort();
        return reject(fileReader.error);
      };
      fileReader.readAsArrayBuffer(file);
    });
  }
}
