/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

import {Inject, Injectable, StaticProvider} from '@angular/core';

import {Options} from '../common_options';
import {Metric} from '../metric';
import {WebDriverAdapter} from '../web_driver_adapter';

@Injectable()
export class UserMetric extends Metric {
  static PROVIDERS =
      <StaticProvider[]>[{provide: UserMetric, deps: [Options.USER_METRICS, WebDriverAdapter]}];

  constructor(
      @Inject(Options.USER_METRICS) private _userMetrics: {[key: string]: string},
      private _wdAdapter: WebDriverAdapter) {
    super();
  }

  /**
   * Starts measuring
   */
  beginMeasure(): Promise<any> {
    return Promise.resolve(true);
  }

  /**
   * Ends measuring.
   */
  endMeasure(restart: boolean): Promise<{[key: string]: any}> {
    let resolve: (result: any) => void;
    let reject: (error: any) => void;
    const promise = new Promise<{[key: string]: any;}>((res, rej) => {
      resolve = res;
      reject = rej;
    });
    const adapter = this._wdAdapter;
    const names = Object.keys(this._userMetrics);

    function getAndClearValues() {
      Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
          .then((values: any[]) => {
            if (values.every(v => typeof v === 'number')) {
              Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
                  .then((_: any[]) => {
                    const map: {[k: string]: any} = {};
                    for (let i = 0, n = names.length; i < n; i++) {
                      map[names[i]] = values[i];
                    }
                    resolve(map);
                  }, reject);
            } else {
              <any>setTimeout(getAndClearValues, 100);
            }
          }, reject);
    }
    getAndClearValues();
    return promise;
  }

  /**
   * Describes the metrics provided by this metric implementation.
   * (e.g. units, ...)
   */
  describe(): {[key: string]: any} {
    return this._userMetrics;
  }
}
