import { h, render } from 'preact';
import User from './user';
import API from './api';
import Pages from './components/pages';
import { isMobileSafari, isFocus, isNativeIOS } from './utility';
import DebugBox from './components/debug-box';

const LOAD_DELAY = 500; // before pulling the curtain
const LOAD_TIMEOUT = 5000; // we can only wait so long.

/**
 * Preload these images before revealing contents.
 * TODO: right now we load all images, which is unnecessary.
 */
const PRELOAD = [
  '/img/mozilla.svg',
  '/img/robot-greetings.png',
  '/img/robot-listening.png',
  '/img/robot-thanks.png',
  '/img/robot-thinking.png',
  '/img/robot-thumbs-up.png',
  '/img/speech-bubble.png',
  '/img/strip-repeat.png',
  '/img/strip-top.png',
  '/img/wave-blue-large.png',
  '/img/wave-blue-mobile.png',
  '/img/wave-red-large.png',
  '/img/wave-red-mobile.png',
  '/img/circle.png',
];

/**
 * Main app controller, rensponsible for routing between page
 * controllers.
 */
export default class App {
  box: DebugBox;
  user: User;
  api: API;
  loaded: boolean;
  loadProgress: number;
  progressMeter: HTMLSpanElement;

  /**
   * App will handle routing to page controllers.
   */
  constructor() {
    // Disable the debug box for now.
    if (isNativeIOS()) {
      this.bootstrapIOS();
    }

    if (isFocus()) {
      document.body.classList.add('focus');
    }

    if (isMobileSafari()) {
      document.body.classList.add('mobile-safari');
    }

    this.user = new User();
    this.api = new API(this.user);
    this.loaded = false;

    // Force binding of handleNavigation to this instance.
    this.handleNavigation = this.handleNavigation.bind(this);

    // Render before loaded.
    this.renderCurrentPage();
  }

  private loadImages(progressCallback?: Function): Promise<void> {
    return new Promise<void>((res, rej) => {
      let loadedSoFar = 0;
      let onLoad = () => {
        ++loadedSoFar;
        if (loadedSoFar === PRELOAD.length) {
          res();
          return;
        }

        progressCallback(loadedSoFar / PRELOAD.length);
      };
      for (let i = 0; i < PRELOAD.length; i++) {
        let image = new Image();
        image.onload = onLoad;
        image.src = PRELOAD[i];
      }
    });
  }

  /**
   * LOAD ALL IMAGES BEFORE FIRST RENDER
   * */

  /**
   * Perform any native iOS specific operations.
   */
  private bootstrapIOS() {
    document.body.classList.add('ios');
    // this.renderDebugBox();
  }

  /**
   * Get the page name from the url.
   */
  private getPageName(href?: string): string {
    if (!href) {
      href = window.location.href;
    }
    let link = document.createElement('a');
    link.href = href;

    // Workaround for IE bug where pathname was not prefixed by '/'
    const pathname = link.pathname;
    if (pathname.indexOf('/') !== 0) {
      return '/' + pathname;
    }
    return pathname;
  }

  /**
   * Update the current page based on new url.
   */
  private handleNavigation(href: string) {
    let page = this.getPageName(href);
    window.history.pushState(null, '', page);
    this.renderCurrentPage();
  }

  private renderDebugBox() {
    render(<DebugBox />, document.body);
  }

  private renderCurrentPage() {
    if (!this.loaded) {
      render(
        <div id="spinner">
          <span
            ref={el => {
              if (this.progressMeter) {
                return;
              }

              this.progressMeter = el as HTMLSpanElement;
            }}
          />
        </div>,
        document.body,
        document.body.firstElementChild
      );
      return;
    }

    // Render the main controller, Pages.
    render(
      <Pages
        user={this.user}
        api={this.api}
        navigate={this.handleNavigation}
        currentPage={this.getPageName()}
      />,
      document.body,
      document.body.firstElementChild
    );
  }

  async init(): Promise<void> {
    // Always force page to be ready after a specified time.
    setTimeout(() => {
      this.loaded = true;
      document.body.classList.add('loaded');
    }, LOAD_TIMEOUT);

    await this.loadImages((progress: number) => {
      if (this.progressMeter) {
        // TODO: find something performant here. (ie not this)
        // let whatsLeft = 1 - progress;
        // this.progressMeter.style.cssText =
        //   `transform: scale(${whatsLeft});`;
      }
    });

    this.loaded = true;
    setTimeout(() => {
      document.body.classList.add('loaded');
    }, LOAD_DELAY);
  }

  /**
   * Entry point for the application.
   */
  run(): void {
    this.renderCurrentPage();
  }
}