import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter';
import {PromiseWrapper} from 'angular2/src/core/facade/async';
import {ListWrapper, Map, MapWrapper} from 'angular2/src/core/facade/collection';
import {DateWrapper, Type, print} from 'angular2/src/core/facade/lang';

import {
  Parser,
  Lexer,
  DynamicChangeDetection
} from 'angular2/src/core/change_detection/change_detection';

import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';

import {Component, Directive, View, ViewMetadata} from 'angular2/metadata';
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';

import {reflector} from 'angular2/src/core/reflection/reflection';
import {ReflectionCapabilities} from 'angular2/src/core/reflection/reflection_capabilities';
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';

import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {
  ViewLoader,
  DefaultDomCompiler,
  SharedStylesHost,
  TemplateCloner
} from 'angular2/src/core/render/render';
import {
  DomElementSchemaRegistry
} from 'angular2/src/core/render/dom/schema/dom_element_schema_registry';

export function main() {
  BrowserDomAdapter.makeCurrent();
  var count = getIntParameter('elements');

  reflector.reflectionCapabilities = new ReflectionCapabilities();
  var reader = new DirectiveResolver();
  var pipeResolver = new PipeResolver();
  var cache = new CompilerCache();
  var viewResolver = new MultipleViewResolver(
      count, [BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]);
  var urlResolver = new UrlResolver();
  var appRootUrl = new AppRootUrl("");
  var renderCompiler = new DefaultDomCompiler(
      new DomElementSchemaRegistry(), new TemplateCloner(-1), new Parser(new Lexer()),
      new ViewLoader(null, null, null), new SharedStylesHost(), 'a');
  var compiler = new Compiler(reader, pipeResolver, [], cache, viewResolver,
                              new ComponentUrlMapper(), urlResolver, renderCompiler,
                              new ProtoViewFactory(new DynamicChangeDetection()), appRootUrl);

  function measureWrapper(func, desc) {
    return function() {
      var begin = DateWrapper.now();
      print(`[${desc}] Begin...`);
      var onSuccess = function(_) {
        var elapsedMs = DateWrapper.toMillis(DateWrapper.now()) - DateWrapper.toMillis(begin);
        print(`[${desc}] ...done, took ${elapsedMs} ms`);
      };
      PromiseWrapper.then(func(), onSuccess, null);
    };
  }

  function compileNoBindings() {
    cache.clear();
    return compiler.compileInHost(BenchmarkComponentNoBindings);
  }

  function compileWithBindings() {
    cache.clear();
    return compiler.compileInHost(BenchmarkComponentWithBindings);
  }

  bindAction('#compileNoBindings', measureWrapper(compileNoBindings, 'No Bindings'));
  bindAction('#compileWithBindings', measureWrapper(compileWithBindings, 'With Bindings'));
}

@Directive({selector: '[dir0]', properties: ['prop: attr0']})
class Dir0 {
}

@Directive({selector: '[dir1]', properties: ['prop: attr1']})
class Dir1 {
  constructor(dir0: Dir0) {}
}

@Directive({selector: '[dir2]', properties: ['prop: attr2']})
class Dir2 {
  constructor(dir1: Dir1) {}
}

@Directive({selector: '[dir3]', properties: ['prop: attr3']})
class Dir3 {
  constructor(dir2: Dir2) {}
}

@Directive({selector: '[dir4]', properties: ['prop: attr4']})
class Dir4 {
  constructor(dir3: Dir3) {}
}

class MultipleViewResolver extends ViewResolver {
  _multiple: number;
  _cache: Map<any, any>;

  constructor(multiple: number, components: Type[]) {
    super();
    this._multiple = multiple;
    this._cache = new Map();
    ListWrapper.forEach(components, (c) => this._warmUp(c));
  }

  _warmUp(component: Type) {
    var view = super.resolve(component);
    var multiplier = ListWrapper.createFixedSize(this._multiple);
    for (var i = 0; i < this._multiple; ++i) {
      multiplier[i] = view.template;
    }
    this._cache.set(component, ListWrapper.join(multiplier, ''));
  }

  resolve(component: Type): ViewMetadata {
    var view = super.resolve(component);
    return new ViewMetadata(
        {template:<string>this._cache.get(component), directives: view.directives});
  }
}

@Component({selector: 'cmp-nobind'})
@View({
  directives: [Dir0, Dir1, Dir2, Dir3, Dir4],
  template: `
<div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
  <div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
    <div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
      <div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
        <div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
        </div>
      </div>
    </div>
  </div>
</div>`
})
class BenchmarkComponentNoBindings {
}

@Component({selector: 'cmp-withbind'})
@View({
  directives: [Dir0, Dir1, Dir2, Dir3, Dir4],
  template: `
<div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
  {{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
  <div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
    {{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
    <div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
      {{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
      <div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
        {{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
        <div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
          {{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
        </div>
      </div>
    </div>
  </div>
</div>`
})
class BenchmarkComponentWithBindings {
}
