//
// Superclass to be used by all of the main routes below.
//
App.BaseRoute = Ember.Route.extend({
  rootKey: '',
  condensedView: false,

  // Don't record characters in browser history
  // for the "search" query item (filter)
  queryParams: {
    filter: {
      replace: true
    }
  },

  getParentAndGrandparent: function(key) {
    var parentKey = this.rootKey,
        grandParentKey = this.rootKey,
        parts = key.split('/');

    if (parts.length > 0) {
      parts.pop();
      parentKey = parts.join("/") + "/";
    }

    if (parts.length > 1) {
      parts.pop();
      grandParentKey = parts.join("/") + "/";
    }

    return {
      parent: parentKey,
      grandParent: grandParentKey,
      isRoot: parentKey === '/'
    };
  },

  removeDuplicateKeys: function(keys, matcher) {
    // Loop over the keys
    keys.forEach(function(item, index) {
      if (item.get('Key') == matcher) {
      // If we are in a nested folder and the folder
      // name matches our position, remove it
        keys.splice(index, 1);
      }
    });
    return keys;
  },

  actions: {
    // Used to link to keys that are not objects,
    // like parents and grandParents
    linkToKey: function(key) {
      if (key == "/") {
        this.transitionTo('kv.show', "");
      }
      else if (key.slice(-1) === '/' || key === this.rootKey) {
        this.transitionTo('kv.show', key);
      } else {
        this.transitionTo('kv.edit', key);
      }
    }
  }
});

//
// The route for choosing datacenters, typically the first route loaded.
//
App.IndexRoute = App.BaseRoute.extend({
  // Retrieve the list of datacenters
  model: function(params) {
    return Ember.$.getJSON(consulHost + '/v1/catalog/datacenters').then(function(data) {
      return data;
    });
  },

  afterModel: function(model, transition) {
    // If we only have one datacenter, jump
    // straight to it and bypass the global
    // view
    if (model.get('length') === 1) {
      this.transitionTo('services', model[0]);
    }
  }
});

// The parent route for all resources. This keeps the top bar
// functioning, as well as the per-dc requests.
App.DcRoute = App.BaseRoute.extend({
  model: function(params) {
    var token = App.get('settings.token');

    // Return a promise hash to retrieve the
    // dcs and nodes used in the header
    return Ember.RSVP.hash({
      dc: params.dc,
      dcs: Ember.$.getJSON(consulHost + '/v1/catalog/datacenters'),
      nodes: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/nodes', params.dc, token)).then(function(data) {
        var objs = [];

        // Merge the nodes into a list and create objects out of them
        data.map(function(obj){
          objs.push(App.Node.create(obj));
        });

        return objs;
      }),
      coordinates: Ember.$.getJSON(formatUrl(consulHost + '/v1/coordinate/nodes', params.dc, token)).then(function(data) {
        return data;
      })
    });
  },

  setupController: function(controller, models) {
    controller.set('content', models.dc);
    controller.set('nodes', models.nodes);
    controller.set('dcs', models.dcs);
    controller.set('coordinates', models.coordinates);
    controller.set('isDropdownVisible', false);
  },
});

App.KvIndexRoute = App.BaseRoute.extend({
  beforeModel: function() {
    this.transitionTo('kv.show', this.rootKey);
  }
});

App.KvShowRoute = App.BaseRoute.extend({
  model: function(params) {
    var key = params.key;
    var dc = this.modelFor('dc').dc;
    var token = App.get('settings.token');

    // Return a promise has with the ?keys for that namespace
    // and the original key requested in params
    return Ember.RSVP.hash({
      key: key,
      keys: Ember.$.getJSON(formatUrl(consulHost + '/v1/kv/' + key + '?keys&seperator=/', dc, token)).then(function(data) {
        var objs = [];
        data.map(function(obj){
          objs.push(App.Key.create({Key: obj}));
        });
        return objs;
      })
    });
  },

  setupController: function(controller, models) {
    var key = models.key;
    var parentKeys = this.getParentAndGrandparent(key);
    models.keys = this.removeDuplicateKeys(models.keys, models.key);

    controller.set('content', models.keys);
    controller.set('parentKey', parentKeys.parent);
    controller.set('grandParentKey', parentKeys.grandParent);
    controller.set('isRoot', parentKeys.isRoot);
    controller.set('newKey', App.Key.create());
    controller.set('rootKey', this.rootKey);
  }
});

App.KvEditRoute = App.BaseRoute.extend({
  model: function(params) {
    var key = params.key;
    var dc = this.modelFor('dc').dc;
    var parentKeys = this.getParentAndGrandparent(key);
    var token = App.get('settings.token');

    // Return a promise hash to get the data for both columns
    return Ember.RSVP.hash({
      dc: dc,
      token: token,
      key: Ember.$.getJSON(formatUrl(consulHost + '/v1/kv/' + key, dc, token)).then(function(data) {
        // Convert the returned data to a Key
        return App.Key.create().setProperties(data[0]);
      }),
      keys: keysPromise = Ember.$.getJSON(formatUrl(consulHost + '/v1/kv/' + parentKeys.parent + '?keys&seperator=/', dc, token)).then(function(data) {
        var objs = [];
        data.map(function(obj){
         objs.push(App.Key.create({Key: obj}));
        });
        return objs;
      }),
    });
  },

  // Load the session on the key, if there is one
  afterModel: function(models) {
    if (models.key.get('isLocked')) {
      return Ember.$.getJSON(formatUrl(consulHost + '/v1/session/info/' + models.key.Session, models.dc, models.token)).then(function(data) {
        models.session = data[0];
        return models;
      });
    } else {
      return models;
    }
  },

  setupController: function(controller, models) {
    var key = models.key;
    var parentKeys = this.getParentAndGrandparent(key.get('Key'));
    models.keys = this.removeDuplicateKeys(models.keys, parentKeys.parent);

    controller.set('content', models.key);
    controller.set('parentKey', parentKeys.parent);
    controller.set('grandParentKey', parentKeys.grandParent);
    controller.set('isRoot', parentKeys.isRoot);
    controller.set('siblings', models.keys);
    controller.set('rootKey', this.rootKey);
    controller.set('session', models.session);
  }
});

App.ServicesRoute = App.BaseRoute.extend({
  model: function(params) {
    var dc = this.modelFor('dc').dc;
    var token = App.get('settings.token');

    // Return a promise to retrieve all of the services
    return Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/services', dc, token)).then(function(data) {
      var objs = [];
      data.map(function(obj){
       objs.push(App.Service.create(obj));
      });
      return objs;
    });
  },
  setupController: function(controller, model) {
    controller.set('services', model);
  }
});


App.ServicesShowRoute = App.BaseRoute.extend({
  model: function(params) {
    var dc = this.modelFor('dc').dc;
    var token = App.get('settings.token');

    // Here we just use the built-in health endpoint, as it gives us everything
    // we need.
    return Ember.$.getJSON(formatUrl(consulHost + '/v1/health/service/' + params.name, dc, token)).then(function(data) {
      var objs = [];
      data.map(function(obj){
       objs.push(App.Node.create(obj));
      });
      return objs;
    });
  },
  setupController: function(controller, model) {
    var tags = [];
    model.map(function(obj){
      if (obj.Service.Tags !== null) {
        tags = tags.concat(obj.Service.Tags);
      }
    });

    tags = tags.filter(function(n){ return n !== undefined; });
    tags = tags.uniq().join(', ');

    controller.set('content', model);
    controller.set('tags', tags);
  }
});

function distance(a, b) {
    a = a.Coord;
    b = b.Coord;
    var sum = 0;
    for (var i = 0; i < a.Vec.length; i++) {
        var diff = a.Vec[i] - b.Vec[i];
        sum += diff * diff;
    }
    var rtt = Math.sqrt(sum) + a.Height + b.Height;

    var adjusted = rtt + a.Adjustment + b.Adjustment;
    if (adjusted > 0.0) {
        rtt = adjusted;
    }

    return Math.round(rtt * 100000.0) / 100.0;
}

App.NodesShowRoute = App.BaseRoute.extend({
  model: function(params) {
    var dc = this.modelFor('dc');
    var token = App.get('settings.token');

    var min = 999999999;
    var max = -999999999;
    var sum = 0;
    var distances = [];
    dc.coordinates.forEach(function (node) {
      if (params.name == node.Node) {
        var segment = node.Segment;
        dc.coordinates.forEach(function (other) {
          if (node.Node != other.Node && other.Segment == segment) {
            var dist = distance(node, other);
            distances.push({ node: other.Node, distance: dist, segment: segment });
            sum += dist;
            if (dist < min) {
              min = dist;
            }
            if (dist > max) {
              max = dist;
            }
          }
        });
        distances.sort(function (a, b) {
          return a.distance - b.distance;
        });
      }
    });
    var n = distances.length;
    var halfN = Math.floor(n / 2);
    var median;

    if (n > 0) {
      if (n % 2) {
        // odd
        median = distances[halfN].distance;
      } else {
        median = (distances[halfN - 1].distance + distances[halfN].distance) / 2;
      }
    } else {
      median = 0;
      min = 0;
      max = 0;
    }

    // Return a promise hash of the node
    return Ember.RSVP.hash({
      dc: dc.dc,
      token: token,
      tomography: {
        distances: distances,
        n: distances.length,
        min: parseInt(min * 100) / 100,
        median: parseInt(median * 100) / 100,
        max: parseInt(max * 100) / 100
      },
      node: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/node/' + params.name, dc.dc, token)).then(function(data) {
        return App.Node.create(data);
      })
    });
  },

  // Load the sessions for the node
  afterModel: function(models) {
    return Ember.$.getJSON(formatUrl(consulHost + '/v1/session/node/' + models.node.Node, models.dc, models.token)).then(function(data) {
      models.sessions = data;
      return models;
    });
  },

  setupController: function(controller, models) {
      controller.set('content', models.node);
      controller.set('sessions', models.sessions);
      controller.set('tomography', models.tomography);
  }
});

App.NodesRoute = App.BaseRoute.extend({
  model: function(params) {
    var dc = this.modelFor('dc').dc;
    var token = App.get('settings.token');

    // Return a promise containing the nodes
    return Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/nodes', dc, token)).then(function(data) {
      var objs = [];
      data.map(function(obj){
       objs.push(App.Node.create(obj));
      });
      return objs;
    });
  },
  setupController: function(controller, model) {
    controller.set('nodes', model);
  }
});


App.AclsRoute = App.BaseRoute.extend({
  model: function(params) {
    var dc = this.modelFor('dc').dc;
    var token = App.get('settings.token');
    // Return a promise containing the ACLS
    return Ember.$.getJSON(formatUrl(consulHost + '/v1/acl/list', dc, token)).then(function(data) {
      var objs = [];
      data.map(function(obj){
        if (obj.ID === "anonymous") {
          objs.unshift(App.Acl.create(obj));
        } else {
          objs.push(App.Acl.create(obj));
        }
      });
      return objs;
    });
  },

  actions: {
    error: function(error, transition) {
      // If consul returns 401, ACLs are disabled
      if (error && error.status === 401) {
        this.transitionTo('dc.aclsdisabled');
      // If consul returns 403, they key isn't authorized for that
      // action.
      } else if (error && error.status === 403) {
        this.transitionTo('dc.unauthorized');
      }
      return true;
    }
  },

  setupController: function(controller, model) {
      controller.set('acls', model);
      controller.set('newAcl', App.Acl.create());
  }
});

App.AclsShowRoute = App.BaseRoute.extend({
  model: function(params) {
    var dc = this.modelFor('dc').dc;
    var token = App.get('settings.token');

    // Return a promise hash of the ACLs
    return Ember.RSVP.hash({
      dc: dc,
      acl: Ember.$.getJSON(formatUrl(consulHost + '/v1/acl/info/'+ params.id, dc, token)).then(function(data) {
        return App.Acl.create(data[0]);
      })
    });
  },

  setupController: function(controller, models) {
      controller.set('content', models.acl);
  }
});

App.SettingsRoute = App.BaseRoute.extend({
  model: function(params) {
    return App.get('settings');
  }
});


// Adds any global parameters we need to set to a url/path
function formatUrl(url, dc, token) {
  if (token == null) {
    token = "";
  }
  if (url.indexOf("?") > 0) {
    // If our url has existing params
    url = url + "&dc=" + dc;
    url = url + "&token=" + token;
  } else {
    // Our url doesn't have params
    url = url + "?dc=" + dc;
    url = url + "&token=" + token;
  }
  return url;
}