import {TemplateRegistryEntry, Loader} from 'aurelia-loader';
import {FEATURE} from 'aurelia-pal';

/**
* An implementation of the TemplateLoader interface implemented using HTML Imports.
* Suitable for use when integrating with Polymer.
*/
export class HTMLImportTemplateLoader {
  /**
   * Creates an instance of HTMLImportTemplateLoader.
   */
  constructor() {
    this.needsBundleCheck = true;
    this.onBundleReady = null;
  }

  /**
  * Loads a template.
  * @param loader The loader that is requesting the template load.
  * @param entry The TemplateRegistryEntry to load and populate with a template.
  * @return A promise which resolves when the TemplateRegistryEntry is loaded with a template.
  */
  loadTemplate(loader: Loader, entry: TemplateRegistryEntry): Promise<any> {
    return this._tryFindTemplateInBundle(loader, entry).then(found => {
      return found ? entry : this._importDocument(entry).then(doc => this._findTemplate(doc, entry));
    });
  }

  _tryFindTemplateInBundle(loader, entry) {
    if (this.bundle) {
      return this._tryGetTemplateFromBundle(entry);
    } else if (this.onBundleReady) {
      return this.onBundleReady.then(() => this._tryGetTemplateFromBundle(entry));
    } else if (this.needsBundleCheck) {
      return this._loadBundle(loader, entry);
    }

    return Promise.resolve(false);
  }

  _loadBundle(loader, entry) {
    let bundleLink = document.querySelector('link[aurelia-view-bundle]');
    this.needsBundleCheck = false;

    if (bundleLink) {
      this.onBundleReady = this._importBundle(bundleLink).then(doc => {
        this._normalizeTemplateIds(loader, doc);
        this.bundle = doc;
        this.onBundleReady = null;
      });

      return this.onBundleReady.then(() => this._tryGetTemplateFromBundle(entry));
    }

    return Promise.resolve(false);
  }

  _importDocument(entry) {
    return new Promise((resolve, reject) => {
      let frag = document.createDocumentFragment();
      let link = document.createElement('link');

      link.rel = 'import';
      link.href = entry.address;
      frag.appendChild(link);

      this._importElements(frag, link, () => resolve(link.import));
    });
  }

  _findTemplate(doc, entry) {
    let template = doc.getElementsByTagName('template')[0];

    if (!template) {
      throw new Error(`There was no template element found in '${entry.address}'.`);
    }

    entry.template = FEATURE.ensureHTMLTemplateElement(template);
  }

  _tryGetTemplateFromBundle(entry) {
    let found = this.bundle.getElementById(entry.address);

    if (found) {
      entry.template = FEATURE.ensureHTMLTemplateElement(found);
      return Promise.resolve(true);
    }

    return Promise.resolve(false);
  }

  _importBundle(link) {
    return new Promise((resolve, reject) => {
      if (link.import) {
        resolve(link.import);
      } else {
        this._importElements(null, link, () => resolve(link.import));
      }
    });
  }

  _normalizeTemplateIds(loader, doc) {
    let templates = doc.getElementsByTagName('template');
    let i = templates.length;

    while (i--) {
      let current = templates[i];
      let id = current.getAttribute('id');

      if (id !== null && id !== undefined) {
        let beforeNormalize = id + '!template-registry-entry';
        let afterNormalize = loader.normalizeSync(beforeNormalize);
        current.setAttribute('id', afterNormalize.replace('!template-registry-entry', ''));
      }
    }
  }

  _importElements(frag, link, callback) {
    if (frag) {
      document.head.appendChild(frag);
    }

    if (window.Polymer && Polymer.whenReady ) {
      Polymer.whenReady(callback);
    } else {
      link.addEventListener('load', callback);
    }
  }
}

/**
 * Configuires the HTMLImportTemplateLoader as the default loader for Aurelia.
 * @param config The FrameworkConfiguration instance.
 */
export function configure(config: Object): Promise<void> {
  config.aurelia.loader.useTemplateLoader(new HTMLImportTemplateLoader());

  // Check for native or polyfilled HTML imports support.
  if (!('import' in document.createElement('link')) && !('HTMLImports' in window)) {
    let name = config.aurelia.loader.normalizeSync('aurelia-html-import-template-loader');
    let importsName = config.aurelia.loader.normalizeSync('webcomponentsjs/HTMLImports.min', name);
    return config.aurelia.loader.loadModule(importsName);
  }
}

@@ -1579,24 +1579,31 @@
 %3E %7B%0A        
+return 
 this._normal
@@ -1631,16 +1631,38 @@
 , doc);%0A
+      %7D).then(() =%3E %7B%0A
         
@@ -3097,16 +3097,34 @@
 .length;
+%0A    let all = %5B%5D;
 %0A%0A    wh
@@ -3270,236 +3270,103 @@
     
-let beforeNormalize = id + '!template-registry-entry';%0A        let afterNormalize = loader.normalizeSync(beforeNormalize);%0A        current.setAttribute('id', afterNormalize.replace('!template-registry-entry', ''));%0A      %7D%0A    %7D
+all.push(normalizeTemplateId(loader, id, current));%0A      %7D%0A    %7D%0A%0A    return Promise.all(all);
 %0A  %7D
@@ -3627,16 +3627,291 @@
 %0A  %7D%0A%7D%0A%0A
+function normalizeTemplateId(loader, id, current) %7B%0A  let beforeNormalize = id + '!template-registry-entry';%0A%0A  return loader.normalize(beforeNormalize).then(afterNormalize =%3E %7B%0A    current.setAttribute('id', afterNormalize.replace('!template-registry-entry', ''));%0A  %7D);%0A%7D%0A%0A
 /**%0A * C
@@ -4329,18 +4329,14 @@
     
-let name =
+return
  con
@@ -4363,20 +4363,16 @@
 ormalize
-Sync
 ('aureli
@@ -4406,31 +4406,36 @@
 er')
-;%0A    let importsName =
+.then(name =%3E %7B%0A      return
  con
@@ -4466,12 +4466,8 @@
 lize
-Sync
 ('we
@@ -4505,30 +4505,46 @@
  name);%0A    
-return
+%7D).then(importsName =%3E
  config.aure
@@ -4573,20 +4573,21 @@
 importsName)
+)
 ;%0A  %7D%0A%7D%0A

switch to normalize api from normalizeSync