import { render } from "lit-html";

import "./style.css";

import { clientBuildImageUrl } from "./generator/client/clientGenerator";
import { backendBuildImageUrl } from "./generator/backend/backendGenerator";
import {
  template
} from "./uiTemplate";
import { defaultSettings } from "./defaultSettings";
import { AvatarOptions } from "./generator/client/avatar";
import { CanvasLayer } from "./generator/client/Layers";
import { createTracker, PiwikTracker } from "./tracker";

export type LanuageSettings = {
  imageWidth?: number;
  imageHeight?: number;
  borderWidth?: number;
  uploadButtonTitle?: string;
  imagePreview?: string;
  downloadButtonTitle?: string;
  languageSelectorLabel?: string;
  imageAltText?: string;
  fileName?: string;
  templateUrl?: string;
  templateRenderFunction?: (settings: AvatarOptions) => CanvasLayer[];
  initialLanguage?: string;
  privacyNote?: string;
  privacyLinkText?: string;
  privacyLink?: string;
  zoomLabel?: string;
};

export enum Generator {
  CLIENT = "client",
  BACKEND = "backend"
}

export type ProfilePictureGeneratorSettings = {
  container: HTMLElement;
  tracking: boolean;
  piwikHost: string;
  piwikPageId: number;
  generator: Generator.BACKEND | Generator.CLIENT;
  containerClass?: string[];
  buttonGroupClass?: string[];
  dropDownClass?: string[];
  dropDownButtonClass?: string[];
  dropDownMenuClass?: string[];
  zoom: boolean;
  uploadButtonClass?: string[];
  languageSelectorClass?: string[];
  imageClass?: string[];
  downloadButtonClass?: string[];
  font?: string;
  currentLanguage?: string;
  languages?: {
    [key: string]: LanuageSettings & { name: string };
  };
} & LanuageSettings;

export type ProfilePictureGenerator = (
  settings: ProfilePictureGeneratorSettings
) => Promise<void>;

declare global {
  interface Window {
    ProfilePictureGenerator: ProfilePictureGenerator;
  }
}

window.ProfilePictureGenerator =
  window.ProfilePictureGenerator ||
  async function(settings: ProfilePictureGeneratorSettings) {
    const originalSettings = settings;
    settings = { ...defaultSettings, ...settings };
    const container = settings.container;
    const state: {
      loading: boolean;
      currentFile: File;
      downloadUrl: string;
      zoomFactor: number;
    } = {
      loading: false,
      currentFile: null,
      downloadUrl: null,
      zoomFactor: 1
    };

    // Create UI
    container.classList.add(...settings.containerClass);
    render(template(settings, state), container);

    // Load piwik tracker asynchronos to not impact the loading of the generator,
    // events before the load has finished are ignored
    let tracker: PiwikTracker | undefined;
    if (settings.tracking && !navigator.doNotTrack) {
      createTracker(container, settings.piwikHost, settings.piwikPageId).then(
        t => (tracker = t)
      );
    }

    /**
     * Track an event if a tracker exists
     */
    function trackEvent(
      category: string,
      action: string,
      name?: string,
      value?: number
    ): void {
      if (tracker) {
        tracker.trackEvent(category, action, name, value);
      }
    }

    /**
     * Generates the profile picture
     */
    async function generateProfilePicture(file: File) {
      state.loading = true;
      render(template(settings, state), container);

      // Generate Image with choosen generator
      let url = "";
      if (settings.generator === Generator.CLIENT) {
        console.time("clientBuildImageUrl()");
        url = await clientBuildImageUrl(
          file,
          settings.imageWidth,
          settings.imageHeight,
          settings.borderWidth,
          state.zoomFactor,
          settings.templateUrl,
          settings.templateRenderFunction
        );
        console.timeEnd("clientBuildImageUrl()");
      } else {
        console.time("backendBuildImageUrl()");
        url = await backendBuildImageUrl(
          settings.currentLanguage,
          file
        );
        console.timeEnd("backendBuildImageUrl()");
      }

      // Update download link and image preview with generated image
      state.downloadUrl = url;
      state.loading = false;
      render(template(settings, state), container);

      trackEvent(
        "profile-picture-generator",
        "generate",
        settings.currentLanguage
      );
    }

    /**
     * Changes the language of the component, returns true if successfull, false otherwise
     */
    function changeLanguage(lang: string): boolean {
      if (!settings.languages || !settings.languages[lang]) return false;
      // Load new settings
      settings = {
        ...defaultSettings,
        ...(defaultSettings.languages ? defaultSettings.languages[lang] : {}),
        ...originalSettings,
        ...(originalSettings.languages ? originalSettings.languages[lang] : {}),
        currentLanguage: lang
      };

      // Update texts
      render(template(settings, state), container);

      // Update image
      if (state.currentFile) {
        generateProfilePicture(state.currentFile);
      }

      return true;
    }

    /**
     * Changes the zoom factor for the image, returns true if successfull, false otherwise
     */
    function changeZoomFactor(zoomLevelStr: string): boolean {
      const zoomLevel = Number(zoomLevelStr);
      if (zoomLevel < 0 || 100 < zoomLevel) return false;
      // non-linear mapping from 0..100 to 0.5..2 with 50 at 1
      const zoomFactor = 0.5 * Math.pow(2, zoomLevel/50);

      // update state
      state.zoomFactor = zoomFactor;

      // Update image
      if (state.currentFile) {
        generateProfilePicture(state.currentFile);
      }

      return true;
    }

    // Load initial langauge
    changeLanguage(settings.initialLanguage);

    // Automaticly select best matching user language
    // IE11 only supports navigator.language
    if (navigator.languages) {
      for (const lang of navigator.languages) {
        if (changeLanguage(lang)) break;
      }
    } else if (navigator.language) {
      changeLanguage(navigator.language);
    }

    // Add event listener
    container.addEventListener("change-language", (e: CustomEvent<string>) =>
      changeLanguage(e.detail)
    );

    container.addEventListener("change-zoom-factor", (e: CustomEvent<string>) =>
      changeZoomFactor(e.detail)
    );

    container.addEventListener("upload-avatar", (e: CustomEvent<File>) => {
      state.currentFile = e.detail;
      generateProfilePicture(e.detail);
    });

    container.addEventListener("download", () =>
      trackEvent(
        "profile-picture-generator",
        "download",
        settings.currentLanguage
      )
    );
  };
