// TODO Make all styles controllable using CSS
// Allow irregular area shapes by using different subclasses of map area (RectangularArea and PolygonArea)
// Use image maps to do polygon
// Cache map data so it doesn't have to be retrieved every time

var Map = Class.create();
Map.prototype = {
  initialize: function(divId, ajaxUrl, initialMapId, initialMapImage, indicatorImage, selectImage, selectImageClicked, width, height, scaleFactor, mapIdField, useImageMaps, onMapLoad) {
    this.containerId = divId;
    this.imageDivId = divId + 'ImageDiv';
    this.imageId = divId + 'Image';
    this.parentLinksId = divId + 'ParentLinks';
    this.mapNameId = divId + 'MapName';
    this.indicatorId = divId + 'Indicator';
    this.ajaxUrl = ajaxUrl;
    this.selectImage = selectImage;
    this.selectImageClicked = selectImageClicked;
    this.areas = new Array();
    this.requestInProgress = false;
    this.width = width;
    this.height = height;
    this.scaleFactor = scaleFactor;
    this.mapIdField = mapIdField;
    this.useImageMaps = useImageMaps;
    this.onMapLoad = onMapLoad;
    
    if (this.useImageMaps) {
      this.imageMapId = this.imageDivId + 'ImageMap' + initialMapId;
    }

    this.setupMapDiv(initialMapImage, indicatorImage);

    this.retrieveMap(initialMapId);
  },
  setupMapDiv: function(initialMapImage, indicatorImage) {
    // Create title (contains parent links, map name and indicator image)
    var title = document.createElement('div');
    
    var parentLinks = document.createElement('span');
    parentLinks.id = this.parentLinksId;
    title.appendChild(parentLinks);
    
    var mapName = document.createElement('span');
    mapName.id = this.mapNameId;
    title.appendChild(mapName);

    $(this.containerId).appendChild(title);
    
    // Create image area - a div containing an image
    var imageDiv = document.createElement('div');
    imageDiv.id = this.imageDivId;
    imageDiv.style.position = 'relative';
    $(this.containerId).appendChild(imageDiv);
    
    var mapImage = document.createElement("img");
    mapImage.id = this.imageId;
    if (this.width) {
      mapImage.width = this.width + 'px';
      $(this.imageDivId).style.width = this.width + 'px';
    }
    if (this.height) {
      mapImage.height = this.height + "px";
      $(this.imageDivId).style.height = this.height + 'px';
    }
    mapImage.src = initialMapImage;
    setOpacity(mapImage, 0);
    $(this.imageDivId).appendChild(mapImage);
    
    var indicatorImg = document.createElement('img');
    indicatorImg.style.display = 'none';
    indicatorImg.src = indicatorImage;
    indicatorImg.id = this.indicatorId;
    indicatorImg.style.position = 'absolute';
    var map = this;
    indicatorImg.onload = function (evt) {
      this.style.top = (map.height / 2) - (this.height / 2) + 'px';
      this.style.left = (map.width / 2) - (this.width / 2) + 'px';
    }

    $(this.imageDivId).appendChild(indicatorImg);    
  },
  drawBoxes: function() {
    var divId = this.imageDivId;
    this.areas.each(function(area, index) {
      area.drawBox(divId);
    });
  },
  removeAreaBoxes: function() {
    this.areas.each(function(area, index) {
      area.removeBox();
    });
  },
  addArea: function(area) {
    this.areas.push(area);
  },
  clearAreas: function(area) {
    this.areas = new Array();
  },
  retrieveMap: function(mapId) {
    var imageId = this.imageId;
    var map = this
    Element.show(this.indicatorId)
    if (!this.requestInProgress) {
      this.removeAreaBoxes();
      this.requestInProgress = true;
      setOpacity(imageId, 50);
      new Ajax.Request(this.ajaxUrl + '&' + 'mapId=' + mapId, {
        method:'get',    
        onSuccess: function(transport, json) {
          if (!json) {
            responseText = transport.responseText;
            json = eval(responseText)
          }
          map.update(json);
          map.requestInProgress = false;
        },
        onFailure: function(transport, json) {
          alert('Map request failed: status ' + transport.status + ' Response text: ' + transport.responseText);
          Element.hide(map.indicatorId)
          map.requestInProgress = false;
          Effect.Appear(map.imageId);
        },
        onException: function(request, exception) {
          //dump(exception);
          //alert('Exception raised, see debug output for details');
        }
      });
    }
  },
  update: function(mapData) {
    if (mapData) {
      var map = this;

      if (this.onMapLoad) {
        eval(this.onMapLoad)(mapData);
      }
      
      var previousAreaCount = this.areas.length;
      this.clearAreas();
      
      if (this.useImageMaps) {
        this.imageMapId = this.imageDivId + 'ImageMap' + mapData.id;
        mapData.areas.each(function (area, index) {
          map.addArea(new PolyMapArea(map, index, area.name, area.type, area.points, map.scaleFactor, area.linkedMap, area.link));
        });
        // IE Fix: IE tries to highlight the image map that was last clicked
        // If the current image map has less areas than the previous one
        // and the clicked area is > the current number of areas IE will crash
        // Fix is to create phantom areas so that this never happens
        var currentAreaCount = mapData.areas.length;
        if (previousAreaCount > currentAreaCount) {
          for (i = 0; i < previousAreaCount - currentAreaCount; i++) {
            map.addArea(new PolyMapArea(map, currentAreaCount + i, '', 'rect', '0,0,0,0', map.scaleFactor, null, '#'));
          }
        }
      } else {
        mapData.areas.each(function (area, index) {
          map.addArea(new BoxMapArea(map, index, area.id, area.name, area.isSelectArea, area.x, area.y, area.width, area.height, map.scaleFactor, area.linkedMap));
        });
      }

      $(this.mapNameId).innerHTML = mapData.name;
      
      if (mapData.parents) {
        $(this.parentLinksId).innerHTML = '';
        mapData.parents.each(function(parent) {
          parentLink = document.createElement('a');
          parentLink.onclick = function(event) {map.retrieveMap(parent.id); return false;};
          parentLink.href = '?mapId=' + parent.id;
          parentLink.innerHTML = parent.name + ':';
          $(map.parentLinksId).appendChild(parentLink);
        });
      } else {
        $(this.parentLinksId).innerHTML = '';
      }
      
      mapIdField = $(this.mapIdField)
      if (mapIdField) {
        mapIdField.value = mapData.id;
      }
      var img = document.createElement('img');
      var imageId = this.imageId;
      
      img.onload = function (evt) {
        $(imageId).src=this.src;
        if (map.height) {
          $(imageId).height=map.height;
        } else {
          $(imageId).height=this.height;
        }
        if (map.width) {
          $(imageId).width=map.width;
        } else {
          $(imageId).width=this.width;
        }
        $(imageId).border = 0;

        if (map.useImageMaps) {
          $(imageId).useMap = '#' + map.imageMapId;
        }
        Effect.Appear(imageId, {
          afterFinish:function(obj) {
            Element.hide(map.indicatorId);
            map.drawBoxes();
          }
        });        
      };
      img.src = mapData.url;
    }
  },
  clearSelectedArea: function(areaId) {
    var area = this.areas.find(function(area, index) { return area.id == areaId; });
    if (area) {
      area.clearBox();
    }
  }
};

var PolyMapArea = Class.create();
PolyMapArea.prototype = {
  initialize: function(map, index, name, type, points, scaleFactor, linkedMapId, link) {
    this.map = map;
    this.index = index;
    this.name = name;
    this.type = type;
    this.points = points;
    this.linkedMapId = linkedMapId;
    this.link = link;
  },
  removeBox: function() {
    var existingImageMap = $(this.map.imageMapId)
    if (existingImageMap) {
      existingImageMap.parentNode.removeChild(existingImageMap);
    }
  },
  drawBox: function(divId) {
    var existingImageMap = $(this.map.imageMapId)
    if (!existingImageMap) {
      var imageMap = document.createElement('map');
      imageMap.name = this.map.imageMapId;
      imageMap.id = this.map.imageMapId;
      $(this.map.imageDivId).appendChild(imageMap);
      existingImageMap = imageMap;
    }
    
    var area = document.createElement('area');
    area.shape = this.type;
    area.coords = this.points;
    area.title = this.name;
    area.alt = this.name;

    if (this.link) {
      area.href = this.link;
      map = this.map;
    } else {
      area.href = '#';
      var linkedMapId = this.linkedMapId;
      var map = this.map;

      area.onclick = function(event) {
        map.retrieveMap(linkedMapId);
        return false;
      };
    }

    $(existingImageMap).appendChild(area);
  }
};

var BoxMapArea = Class.create();
BoxMapArea.prototype = {
  initialize: function(map, index, id, name, isSelectArea, x, y, width, height, scaleFactor, linkedMapId) {
    this.map = map;
    this.index = index;
    this.id = id;
    this.name = name;
    this.isSelectArea = isSelectArea;
    this.x = x * scaleFactor;
    this.y = y * scaleFactor;
    this.width = width * scaleFactor;
    this.height = height * scaleFactor;
    this.linkedMapId = linkedMapId;
    this.selected = false;
  },
  removeBox: function() {
    var box = $('box'+this.index);
    box.parentNode.removeChild(box);  
  },
  clearBox: function() {
    var box = $('box'+this.index);
    box.src = this.map.selectImage;
    this.selected = false;
  },
  drawBox: function(divId) {
    if (this.isSelectArea) {
      var box = document.createElement("img");
      box.id = "box" + this.index;
      box.locationId = this.id;
      box.src = this.map.selectImage;
      with (box.style) {
        position = 'absolute';
        top = (this.y - 7) + 'px';
        left = (this.x - 7) + 'px';
      }
      var map = this.map;
      var area = this;
      box.onclick = function (evt) {
        if (typeof(selectAreaClicked) == 'function') {
          if (selectAreaClicked(this.locationId, area.selected)) {
            if (area.selected) {
              this.src = map.selectImage;
              area.selected = false;
            } else {
              this.src = map.selectImageClicked;
              area.selected = true;
            }
          }
        }
      }
      box.onmouseover = function(evt) {
        this.style.cursor='pointer';
      }
      
      $(divId).appendChild(box);
    } else {
      var box = document.createElement("div");
      box.id = "box" + this.index;
      with (box.style) {
        position = 'absolute';
        top = this.y + 'px';
        left = this.x + 'px';
        width = this.width + 'px';
        height = this.height + 'px';
        backgroundColor = '#fff';
        border = '2px solid #000';
        cursor = 'pointer';
      }
      Element.hide(box);
      $(divId).appendChild(box);
      setOpacity("box" + this.index, 50);
      Element.show(box);

      var linkedMapId = this.linkedMapId;
      var map = this.map;

      box.onclick = function(event) {
        map.retrieveMap(linkedMapId);
        return false;
      };
    }
  }
};

function setOpacity(id, opacity) {
  var object = $(id).style;
  object.opacity = (opacity / 100);
  object.MozOpacity = (opacity / 100);
  object.KhtmlOpacity = (opacity / 100);
  object.filter = "alpha(opacity=" + opacity + ")";
}