var M = {
  get: function(key) {
    return function(target) {
      return target[key];
    }
  },
  set: function(key, value) {
    return function(target) {
      target[key] = value;
    }
  },
  is: function(key) {
    return function(target) {
      return !!target[key];
    }
  },
  isnt: function(key) {
    return function(target) {
      return !target[key];
    }
  },
  send: function(method) {
    return function(target) {
      return target[method]();
    }
  }
}

function createBorder(className) {
  var table = new Element("table", {className: className});

  var tbody = new Element("tbody");
  table.insert(tbody);
  
  var top = new Element("tr");
  tbody.insert(top);
  var tl = new Element("td", {className: className + " tl"});
  top.insert(tl);
  var tc = new Element("td", {className: className + " tc"});
  top.insert(tc);
  var tr = new Element("td", {className: className + " tr"});
  top.insert(tr);
  
  var middle = new Element("tr");
  tbody.insert(middle);
  var cl = new Element("td", {className: className + " cl"});
  middle.insert(cl);
  var content = new Element("td", {className: className + " content"});
  middle.insert(content);
  var cr = new Element("td", {className: className + " cr"});
  middle.insert(cr);
  
  var bottom = new Element("tr");
  tbody.insert(bottom);
  var bl = new Element("td", {className: className + " bl"});
  bottom.insert(bl);
  var bc = new Element("td", {className: className + " bc"});
  bottom.insert(bc);
  var br = new Element("td", {className: className + " br"});
  bottom.insert(br);
  return [table, content];
}

function windowHeight() {
  var updateSize = function() {
    var height = document.viewport.getHeight();
    $('layout_table').setStyle({height: height + 'px'});
    if (Prototype.Browser.IE || (Prototype.Browser.Gecko && $('directory_families')))
      $('content').setStyle({height: (height - 118) + 'px'});
    else if (Prototype.Browser.Gecko)
      $('content').setStyle({height: (height - 88) + 'px'});
  }
  Event.observe(window, 'resize', updateSize);
  updateSize();
}

function showMap(event) {
  closeMapButton.show();
  largeMapBackground.show();
  largeMap.setStyle({visibility: 'visible'});
}

function closeMap(event) {
  closeMapButton.hide();
  largeMapBackground.hide();
  largeMap.setStyle({visibility: 'hidden'});
}

function initMaps() {
  window.smallMap = $('small_map');
  if (window.smallMap.hasClassName('disabled'))
    return;
  window.largeMap = $('large_map');
  window.closeMapButton = new Element('div', {id: 'close_map'});
  window.largeMapBackground = new Element('div', {id: 'large_map_background'});
  closeMapButton.hide();
  largeMapBackground.hide();
  $(document.body).insert(closeMapButton);
  $(document.body).insert(largeMapBackground);
  closeMapButton.observe('click', closeMap);
  smallMap.observe('click', showMap);
  
  var updateSize = function() {
    largeMap.setStyle({height: (document.viewport.getHeight() - 55) + 'px', width: (document.viewport.getWidth() - 40) + 'px'});
  }
  Event.observe(window, 'resize', updateSize);
  updateSize();
}

var FamilyNameParser = Class.create({
  initialize: function(field) {
    this.field = $(field);
    this.field.observe('blur', this.parse.bind(this));
    $(this.field.form).observe('submit', this.parse.bind(this));
    this.parents = [];
  },
  parse: function() {
    var names = this.field.value.strip().match(/^(.+?)\s*(?:(?:&| and )\s*((?:\S+ )+?))?(?:(\S+)|\"(.+)\")$/);
    if (names == null) return;
    var last_name = names[3] || names[4];
    names = [names[1], names[2]].compact().invoke('strip').without("");
    this.parents = names.collect(function(name, i) {
      var parent = this.parents.detect(function(parent) {return parent.first == name}) || (this.parents[i] && !names.include(this.parents[i].first) ? this.parents[i] : new FamilyMember({table: family_members_table}));
      parent.update({first: name, last: last_name});
      return parent;
    }.bind(this));
    family_members_table.updateParents(this.parents);
  }
});

var ChildrenParser = Class.create({
  initialize: function(field) {
    this.field = $(field);
    this.field.observe('blur', function() {setTimeout(this.parse.bind(this), 1000)}.bind(this));
    $(this.field.form).observe('submit', this.parse.bind(this));
    this.children = [];
  },
  parse: function() {
    var names = this.field.value.split(',').invoke('strip').without("");
    this.children = names.collect(function(name, i) {
      var family_member = this.children.detect(function(child) {return child.first == name}) || (this.children[i] && !names.include(this.children[i].first) ? this.children[i] : new FamilyMember({table: family_members_table}));
      family_member.update({first: name, last: family_members_table.parents[0] && family_members_table.parents[0].last});
      return family_member;
    }.bind(this));
    family_members_table.updateChildren(this.children);
  }
})

var FamilyMember = Class.create({
  initialize: function(properties) {
    if (properties) this.update(properties);
  },
  update: function(properties) {
    Object.extend(this, properties);
  },
  addCell: function(tr, property, class_name) {
    class_name = class_name || property;
    var input = new Element('input', {type: 'text', value: this[property] || ''});
    input.observe('blur', function() {
      this[property] = input.value;
      this.table.updateJson();
    }.bind(this));
    this.table.form.observe('submit', function() {
      this[property] = input.value;
      this.table.updateJson();
    }.bind(this));
    tr.insert(new Element('td', {className: class_name}).insert(input));
  },
  toJSON: function() {
    return $H($w("id first last cell_phone email birthday parent").inject({}, function(hash, field) {
      hash[field] = this[field];
      return hash;
    }.bind(this))).toJSON();
  },
  toElement: function(options) {
    var tr = new Element('tr');
    if (options.edit) {
      this.addCell(tr, 'first');
      this.addCell(tr, 'last');
    } else {
      tr.insert(new Element('td', {className: 'family-member'}).update(this.first));
    }
    this.addCell(tr, 'cell_phone', 'phone');
    this.addCell(tr, 'email');
    this.addCell(tr, 'birthday', 'date');
    if (options.edit) {
      var a = new Element('a', {href: 'javascript:void(0)'}).update('<img src="/images/x.png"/>');
      a.observe('click', function() {
        family_members_table.remove(this)
      }.bind(this));
      var td = new Element('td', {className: 'buttons'}).insert(a);
      tr.insert(td);
    }
    return tr;
  }
})

var FamilyMembersTable = Class.create({
  initialize: function(container, options) {
    this.options = options || {};
    this.container = $(container);
    this.hidden_field = $('family_family_members_json');
    this.form = $(this.hidden_field.form);
    this.container.insert({after: this.hidden_field});
    this.children = this.parents = [];
    if (this.hidden_field.value != '')
      this.loadJson();
  },
  loadJson: function() {
    var family_members = eval(this.hidden_field.value);
    var create_family_member = function(hash) {
      var family_member = new FamilyMember({table: this});
      family_member.update(hash);
      return family_member;
    }.bind(this);
    this.parents = family_members.select(M.is('parent')).map(create_family_member);
    this.children = family_members.select(M.isnt('parent')).map(create_family_member);
    this.update();
  },
  remove: function(family_member) {
    if (family_member.parent)
      this.parents = this.parents.without(family_member);
    else
      this.children = this.children.without(family_member);
    this.update();
  },
  updateParents: function(parents) {
    this.parents = parents;
    this.parents.each(M.set('parent', true));
    this.children.each(function(child) {
      child.last = parents[0] && parents[0].last;
    });
    this.update();
  },
  updateChildren: function(children) {
    this.children = children;
    this.update();
  },
  updateJson: function() {
    this.hidden_field.value = this.parents.concat(this.children).toJSON();
  },
  update: function() {
    this.updateJson();
    var table = new Element("table", {className: 'family-members'});
    this.container.update(table);
    var tbody = new Element("tbody");
    table.insert(tbody);
    var family_members = this.parents.concat(this.children);
    if (family_members.length != 0) {
      tbody.insert("<tr><th>First Name</th><th>Last Name</th><th>Cell Phone</th><th>Email Address</th><th>Birthday</th><th class='buttons'></th></tr>");
    }
    family_members.each(function(family_member, i) {
      tbody.insert(family_member.toElement(this.options).writeAttribute('id', 'family_member_row_' + i).addClassName(i % 2 == 0 ? 'odd' : 'even').addClassName(i == family_members.length - 1 ? 'last' : ''));
    }.bind(this));
    if (this.parents.length < 2)
      table.insert({after: new Element('a', {href: 'javascript:void(0)', id: 'add_parent'}).update('<img src="/images/add_parent_button.png"/>').observe('click', this.addParent.bind(this))})
    table.insert({after: new Element('a', {href: 'javascript:void(0)', id: 'add_child'}).update('<img src="/images/add_child_button.png"/>').observe('click', this.addChild.bind(this))})
  },
  advancedMode: function() {
    this.options.edit = true;
    this.update();
    $('family_name').disable();
    $('family_children').disable();
    $('right_tray').update('');
  },
  addParent: function() {
    this.parents.push(new FamilyMember({table: this, parent: true, last: this.parents && this.parents[0].last}));
    this.update();
    this.focusFamilyMember(this.parents.length - 1);
  },
  addChild: function() {
    this.children.push(new FamilyMember({table: this, last: this.parents && this.parents[0].last}));
    this.update();
    this.focusFamilyMember(this.parents.length + this.children.length - 1);
  },
  focusFamilyMember: function(index) {
    $$('#family_member_row_' + index + ' .first input')[0].focus();
  }
});

var IphoneSlideshow = Class.create({
  initialize: function(element, width) {
    this.element = $(element);
    this.width = width;
    this.offset = (this.element.getWidth() - this.width) / 2
    this.element.setStyle({position: 'relative', overflow: 'hidden'});
    this.overlay = this.element.down('.overlay');
    this.overlay.setStyle({zIndex: 100});
    this.slides = this.element.select('.slide');
    Event.observe(window, 'load', function() { this.start(); }.bind(this));
  },
  start: function() {
    this.slides.invoke('setStyle', {left: (this.width + this.offset) + 'px', zIndex: 50});
    this.next(this.slides.first())
    this.slides.without(this.slides.first()).each(function(slide, i) {
      setTimeout(this.next.bind(this, slide), 2000 * (i+2));
    }.bind(this))
    setTimeout(this.start.bind(this), 2000 * (this.slides.length + 2));
  },
  next: function(next) {
    next.setStyle({left: (this.width + this.offset) + 'px'});
    new Fx.Style(next, 'left', {onComplete: function() {this.current = next;}.bind(this)}).custom(this.width + this.offset, this.offset);
    if (this.current) new Fx.Style(this.current, 'left').custom(this.offset, -(this.width - this.offset));
  }
});
