import { ipcRenderer, remote, IpcRendererEvent } from 'electron';

import { Observable, Subject } from 'rxjs';

import {
  NativeService,
  WindowState,
  NotificationRequest,
  WindowIdle
} from '@ucap-webmessenger/native';
import { share } from 'rxjs/operators';
import {
  NotificationChannel,
  UpdaterChannel,
  FileChannel,
  WindowStateChannel,
  IdleStateChannel
} from '../types/channel.type';

export class ElectronNativeService implements NativeService {
  private windowStateChangedSubject: Subject<WindowState> | null = null;
  private windowStateChanged$: Observable<WindowState> | null = null;

  private idleStateChangedSubject: Subject<WindowIdle> | null = null;
  private idleStateChanged$: Observable<WindowIdle> | null = null;

  notify(noti: NotificationRequest): void {
    ipcRenderer.send(NotificationChannel.Notify, noti);
  }

  closeAllNotify(): void {
    ipcRenderer.send(NotificationChannel.CloseAllNotify);
  }

  checkForUpdates(): Observable<boolean> {
    return new Observable<boolean>(subscriber => {
      try {
        subscriber.next(ipcRenderer.sendSync(UpdaterChannel.Check));
      } catch (error) {
        subscriber.error(error);
      } finally {
        subscriber.complete();
      }
    });
  }

  showImageViewer(): void {
    ipcRenderer.send(FileChannel.ShowImageViewer);
  }

  readFile(path: string): Observable<Buffer> {
    return new Observable<Buffer>(subscriber => {
      try {
        subscriber.next(ipcRenderer.sendSync(FileChannel.ReadFile, path));
      } catch (error) {
        subscriber.error(error);
      } finally {
        subscriber.complete();
      }
    });
  }

  saveFile(
    buffer: Buffer,
    fileName: string,
    path?: string
  ): Observable<string> {
    return new Observable<string>(subscriber => {
      try {
        subscriber.next(
          ipcRenderer.sendSync(FileChannel.SaveFile, buffer, fileName, path)
        );
      } catch (error) {
        subscriber.error(error);
      } finally {
        subscriber.complete();
      }
    });
  }

  windowStateChanged(): Observable<WindowState> {
    if (!this.windowStateChangedSubject) {
      this.windowStateChangedSubject = new Subject<WindowState>();
      this.windowStateChanged$ = this.windowStateChangedSubject
        .asObservable()
        .pipe(share());
    }

    ipcRenderer.on(
      WindowStateChannel.Changed,
      (event: IpcRendererEvent, windowState: WindowState) => {
        this.windowStateChangedSubject.next(windowState);
      }
    );
    return this.windowStateChanged$;
  }

  windowClose(): void {
    const currentWindow = remote.getCurrentWindow();
    if (!currentWindow) {
      return;
    }

    currentWindow.close();
  }

  windowMinimize(): void {
    const currentWindow = remote.getCurrentWindow();
    if (!currentWindow) {
      return;
    }

    currentWindow.minimize();
  }

  windowMaximize(): void {
    const currentWindow = remote.getCurrentWindow();
    if (!currentWindow) {
      return;
    }

    if (currentWindow.isMaximized()) {
      currentWindow.unmaximize();
    } else {
      currentWindow.maximize();
    }
  }

  idleStateChanged(): Observable<WindowIdle> {
    if (!this.idleStateChangedSubject) {
      this.idleStateChangedSubject = new Subject<WindowIdle>();
      this.idleStateChanged$ = this.idleStateChangedSubject
        .asObservable()
        .pipe(share());
    }

    ipcRenderer.send(IdleStateChannel.StartCheck);

    ipcRenderer.on(
      IdleStateChannel.Changed,
      (event: IpcRendererEvent, idleState: WindowIdle) => {
        this.idleStateChangedSubject.next(idleState);
      }
    );
    return this.idleStateChanged$;
  }

  constructor() {}
}
