Element.addMethods({
  recordId: function(element) {
    return element.id.split('_').last()
  }
})


///////////////////////////////////////

var PUB_ZOOM_LEVEL = 13
var LOCATION_ZOOM_LEVEL = 7
var DEFAULT_ZOOM_LEVEL = 10
var SEARCH_MAX_ZOOM_LEVEL = 14
function Map(container, options) {
  var map, markerManager, pubMarkers, locationMarkers
  var dublinArea = new GLatLngBounds(new GLatLng(53, -7), new GLatLng(54, -4))
  
  initialize()
  return map
  
  function initialize() {
    options = options || {}
    container = $(container)

    map = new GMap2(container)
    map.setCenter(new GLatLng(53.35, -6.25), DEFAULT_ZOOM_LEVEL)
    map.enableScrollWheelZoom()
    map.disableDoubleClickZoom()
    map.addControl(new GSmallMapControl())
   
    markerManager = new MarkerManager(map)
        
    if (options.pubs) loadPubs()
    if (options.locations) loadLocations()
    
    markerManager.refresh()
    
    GEvent.addListener(map, 'click', onMapClick)
    Event.observe(window, 'unload', GUnload)
  }
  
  function loadPubs() {
    createPubMarkers()
    markerManager.addMarkers(pubMarkers, options.locations ? PUB_ZOOM_LEVEL : LOCATION_ZOOM_LEVEL)
    if (!options.locations) fitMarkersOnMap()
  }
  
  function loadLocations() {
    createLocationMarkers()
    markerManager.addMarkers(locationMarkers, LOCATION_ZOOM_LEVEL)
  }
  
  function createPubMarkers() {
    var filteredSites = options.pubs.select(isInDublinArea)
    pubMarkers = filteredSites.map(PubMarker.create)
  }
    
  function createLocationMarkers() {
    locationMarkers = options.locations.map(LocationMarker.create.curry(map))    
  }

  function fitMarkersOnMap() {
    if (pubMarkers.length == 0) return
    var newBounds = new GLatLngBounds(pubMarkers.first().getLatLng(), pubMarkers.first().getLatLng())
    pubMarkers.each(function(marker) { newBounds.extend(marker.getLatLng()) })
    var newZoomLevel = map.getBoundsZoomLevel(newBounds)
    newZoomLevel = Math.min(newZoomLevel, SEARCH_MAX_ZOOM_LEVEL)
    map.setCenter(newBounds.getCenter(), newZoomLevel)
  }
  
  function onMapClick(overlay, point) {
    if (overlay instanceof GMarker)
      overlay.onClick()
  }
  
  function isInDublinArea(site) {
    return (
      site.latitude && site.longitude &&
      dublinArea.containsLatLng(new GLatLng(site.latitude, site.longitude))
    )
  }
}


///////////////////////////////////////

function PubSelectionMap(container, pub_id, options) {
  var map = new Map('myMap', options)

	GEvent.addListener(map, 'dblclick', function(overlay, point) {
		var newPub = new GMarker(point)
		map.clearOverlays()
		map.addOverlay(newPub)

		new Ajax.Request("/admin/pubs/" + pub_id + "/geocode", {
			parameters: {'lat': point.lat(), 'long': point.lng()},
			method: 'put',
			onSuccess: function(xhr) {
			  $('location').replace(xhr.responseText)
			  new Effect.Highlight($('location'))
			}
		})
	})	
}

function LocationSelectionMap(container, location_id, options) {
  var map = new Map('myMap', options)
  map.setZoom(12)

	GEvent.addListener(map, 'dblclick', function(overlay, point) {
		var newLocation = new GMarker(point, {icon: LocationMarker.getIcon()})
		map.clearOverlays()
		map.addOverlay(newLocation)

		new Ajax.Request("/admin/locations/" + location_id + "/geocode", {
			parameters: {'lat': point.lat(), 'long': point.lng()},
			method: 'put',
			onSuccess: function(xhr) {
			  $('location').replace(xhr.responseText)
			  new Effect.Highlight($('location'))
			}
		})
	})	
}


///////////////////////////////////////

function PubMarker(pub) {
  GMarker.call(this,
    new GLatLng(pub.latitude, pub.longitude),
    {title: pub.name, icon: PubMarker.getIcon()}
  )
  GEvent.bind(this, 'dblclick', this, this.goToPub)
  this.site = pub
}

PubMarker.prototype = Object.extend(new GMarker(new GLatLng(0, 0)), {
  showTooltip: function() {
    var tooltip = new Element('div', {style: "width: 180px"}).
      insert(new Element('h5', {style: "font-weight: bold"}).update(this.site.name)).
      insert(new Element('p').
        insert(this.site.description).
        insert(this.site.imageUrl && new Element('img', {'class': 'pListing_img', src: this.site.imageUrl})).
        insert(new Element('br')).
        insert(this.site.url && new Element('a', {href: this.site.url}).update('more...'))
      )

    this.openInfoWindow(tooltip)
  },
  goToPub: function() {
    window.location.replace(this.site.url)
  },
  onClick: function() {
    this.showTooltip()
  }
})

Object.extend(PubMarker, {
  create: function(pub) {
    return new PubMarker(pub)
  },
  getIcon: function() {
    if (this.icon) return this.icon
    this.icon = new GIcon(G_DEFAULT_ICON)
    this.icon.image = "/images/pub-icon.png"
    return this.icon
  }  
})


function LocationMarker(map, location) {
  GMarker.call(this,
    new GLatLng(location.latitude, location.longitude),
    {title: location.description, icon: LocationMarker.getIcon()}
  )
  GEvent.bind(this, 'dblclick', this, this.zoom)
  this.location = location
  this.map = map
}

LocationMarker.prototype = Object.extend(new GMarker(new GLatLng(0, 0)), {
  showTooltip: function() {
    var tooltip = new Element('div', {style: "width: 180px; height: 20px"}).
      insert(new Element('h5', {style: "font-weight: bold"}).
        insert("#{name}: (#{count})".interpolate(this.location))
      )    
    
    this.openInfoWindow(tooltip)
  },
  zoom: function() {
    var newZoomLevel = this.map.getZoom() < PUB_ZOOM_LEVEL ? PUB_ZOOM_LEVEL : DEFAULT_ZOOM_LEVEL
    this.map.setCenter(this.getLatLng(), newZoomLevel)
  },
  onClick: function() { }
})

Object.extend(LocationMarker, {
  create: function(map, location) {
    return new LocationMarker(map, location)
  },
  getIcon: function() {
    if (this.icon) return this.icon
    this.icon = new GIcon(G_DEFAULT_ICON)
    this.icon.image = "/images/location-icon.png"
    return this.icon
  }  
})

///////////////////////////////////////

function PubItemList(options) {
  var list, listBody, editedRows
  var collectionUrl, itemUrl, editItemUrl
  var pubId
  
  initialize()
  
  function initialize() {
    list = $(options.listId)
    listBody = list.down('tbody')
    editedRows = {}
    collectionUrl = options.collectionUrl
    itemUrl = options.itemUrl
    editItemUrl = options.itemUrl + "/edit"
    pubId = options.pubId
    list.observe('click', onListClick)
  }
  
  function onNewItemSubmit(e) {
    create()
    e.stop()    
  }
  
  function onListClick(e) {
    if (!e.target.match("td.controls *")) return
    doButtonCommand(e.target)
  }
  
  function doButtonCommand(button) {
    var row = button.up('tr')
    switch (button.name) {
      case 'add': create(); break
      case 'edit': edit(row); break
      case 'delete': destroy(row); break
      case 'save': update(row); break
      case 'cancel': cancelEdit(row); break
    }
  }
  
  function create() {
    var url = collectionUrl.interpolate({pubId: options.pubId})    
    var inputs = findInputs(newRow())
    new Ajax.Request(url, { parameters: inputs.parameters, method: 'post',
      onSuccess: function(xhr) {
        var response = xhr.responseJSON
        listBody.insert(response.createdItem)
        listBody.down('tr:last-child').highlight()
        newRow().replace(response.newItem)
      },
      onFailure: function(xhr) {
        newRow().replace(xhr.responseText)
      }
    })
  }
  
  function destroy(row) {
    if (!confirm("Are you sure you want to delete this item?")) return
    var id = row.recordId()
    var url = itemUrl.interpolate({'id': id})
  	new Ajax.Request(url, {	method: 'delete',
  		onSuccess: function() { row.remove() }
  	})
  }
  
  function edit(row) {
    var id = row.recordId()
    var url = editItemUrl.interpolate({'id': id})
    editedRows[id] = row
    new Ajax.Request(url, {	method: 'get',
  		onSuccess: function(xhr) { row.replace(xhr.responseText) }
  	})        
  }
  
  function update(row) {
    var id = row.recordId()
    var url = itemUrl.interpolate({'id': id})
    var inputs = findInputs(row)
    new Ajax.Request(url, { parameters: inputs.parameters, method: 'put',
      onSuccess: function(xhr) { 
        row.replace(xhr.responseText)
        delete editedRows[id]
      },
      onFailure: function(xhr) {
        row.replace(xhr.responseText)
      }
    })
  }
  
  function cancelEdit(row) {
    var id = row.recordId()
    var oldRow = editedRows[id]
    row.replace(oldRow)
    delete editedRows[id]  
  }
  
  function newRow() {
    return list.down('tfoot tr.new-item')
  }
  
  function findInputs(row) {
    var elements = row.select('input', 'select')
    var parameters = Form.serializeElements(elements)
    return {'elements': elements, 'parameters': parameters}    
  }
}

function WebsiteList(options) {
  Object.extend(options, {
    collectionUrl: "/admin/pubs/#{pubId}/websites",
    itemUrl: "/admin/websites/#{id}" 
  })
  new PubItemList(options)
}

function IMAddressList(options) {
  Object.extend(options, {
    collectionUrl: "/admin/pubs/#{pubId}/im_addresses",
    itemUrl: "/admin/im_addresses/#{id}"
  })
  new PubItemList(options)
}

function EmailList(options) {
  Object.extend(options, {
    collectionUrl: "/admin/pubs/#{pubId}/emails",
    itemUrl: "/admin/emails/#{id}"
  })
  new PubItemList(options)
}

function PhoneList(options) {
  Object.extend(options, {
    collectionUrl: "/admin/pubs/#{pubId}/phones",
    itemUrl: "/admin/phones/#{id}"
  })
  new PubItemList(options)
}