import isObject from 'lodash/lang/isObject';
import isEqual from 'lodash/lang/isEqual';
import cloneDeep from 'lodash/lang/cloneDeep';

import transform from 'lodash/object/transform';

import Url from '../../components/url';

import Scope from './scope';
import Injector from './injector';
import Diggest from './diggest';

var contexts = new WeakMap();
var queue = new WeakMap();

var local = {
  context: Symbol('context'),
  state: Symbol('state')
};


export default class AngularUrl extends Url {

  /**
   * Associate controller's name with controller's context (URL scope)
   * @param namespace
   * @param context
   */
  static attach(namespace, context) {
    if (!Url.has(namespace)) {
      throw new Error(`Can not attach context. Url struct for "${namespace}" does not defined`);
    }

    var url = Url.get(namespace);
    url.attach(context);

    contexts.set(context, url);

    if (queue.has(context)) {
      var resolve = queue.get(context);
      resolve(url);

      queue.delete(context);
    }
  }

  static isAttached(context) {
    return contexts.has(context);
  }

  static create(context) {
    if (!isObject(context)) {
      throw new Error(`Wrong arguments for AngularUrl.get`);
    }

    return new Promise((resolve, reject) => {
      if (AngularUrl.isAttached(context)) {
        var url = contexts.get(context);
        resolve(url);

      } else {
        queue.set(context, resolve);
      }
    });
  }

  constructor(pattern, struct) {
    super(pattern, struct);

    this[local.state] = {};
  }

  parse() {
    var $location = Injector.get('$location');
    return this.decode($location.$$url);
  }

  attach(context) {
    this[local.context] = context;
  }

  silent(params = {}) {
    let isChanged = this.isEqual(params);
    if (!isChanged) {
      let url = this.stringify(params);
      let $location = Injector.get('$location');
      let $rootScope = Injector.get('$rootScope');

      var unsubscribe = $rootScope.$on('$routeUpdate', (event) => {
        event.silent = true;
        unsubscribe();
      });

      $location.url(url)
    }

    return isChanged;
  }

  go(params = {}) {
    let isChanged = this.isEqual(params);
    if (!isChanged) {
      let url = this.stringify(params);
      let $location = Injector.get('$location');

      $location.url(url)
    }

    return isChanged;
  }


  isEqual(params = {}) {
    if (!isObject(params)) {
      throw new Error('params should be an object');
    }

    var existingParams = this.parse();

    return isEqual(existingParams, params);
  }

  redirect(params = {}) {
    if (!isObject(params)) {
      throw new Error('params should be an object');
    }

    var url = this.stringify(params);
    var $window = Injector.get('$window');

    $window.location.href = url;
  }

  stringify(params) {
    var url = super.stringify(params);

    var $browser = Injector.get('$browser');
    var base = $browser.baseHref();

    return base ? url.replace(/^\//, '') : url;
  }

  watch(callback) {
    var context = this[local.context];
    this[local.state] = this.parse();

    Scope.get(context).then($scope => {
      $scope.$on('$routeUpdate', (event) => {
        if (!event.silent) {
          var params = this.parse();

          var diff = transform(params, (result, n, key) => {
            if (!isEqual(n, this[local.state][key])) {
              result[key] = n;
            }
          });

          this[local.state] = cloneDeep(params);

          callback(params, diff);
        }
      });
    });
  }
}

@@ -3177,39 +3177,8 @@
 %3E %7B%0A
-        if (!event.silent) %7B%0A  
     
@@ -3213,26 +3213,24 @@
 );%0A%0A        
- 
- 
 var diff = t
@@ -3274,26 +3274,24 @@
 %7B%0A          
-  
 if (!isEqual
@@ -3317,26 +3317,24 @@
 e%5D%5Bkey%5D)) %7B%0A
-  
             
@@ -3356,30 +3356,26 @@
 ;%0A          
-  %7D%0A  
+%7D%0A
         %7D);%0A
@@ -3383,18 +3383,16 @@
         
- 
- 
 this%5Bloc
@@ -3431,18 +3431,16 @@
         
-  
 callback
@@ -3452,18 +3452,50 @@
 ms, diff
-);
+, %7B%0A          silent: event.silent
 %0A       
@@ -3492,24 +3492,26 @@
 nt%0A        %7D
+);
 %0A      %7D);%0A 

update silent url method