var ConfiguredViewArea = Class.create({
  CLASSDEF: {
      name: 'ConfiguredViewArea'
  },
  
  initialize: function CPVA_initialize(configuredView, productArea) {
    this.id = productArea.id;
    this.configuredView = configuredView;
    this.configuredProduct = configuredView.configuredProduct;
    this.productArea = productArea;
    this.productView = configuredView.productView;
    
    this.selected = false;
    this.renderVersion = 0;
    this.reRender = false; //track changes on this object that need rerendering (make all child objects rerender)
    this.reRenderChild = false; //track if children have made changes requiring rerendering
    
    this.allItems = {}; //used to lookup where an item is (which process)
    this.itemCount = 0;
    this.visibleItemCount = 0;
    
    this.zIndex = configuredView.getNextZIndex();
    
    
    this.initProcesses();
    
    
    
    this.initialised = false;
    this.canvasPlaced = false;
    this.allowedProcesses = null;
    
    
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT && !this.configuredView.isTempView) {
      this.canvas = document.createElement("DIV");
      this.canvas.id = "canvas_" + d.getNextId();
      this.canvas.style.position = "absolute";
      this.canvas.style.border="dotted 1px white";
      this.canvas.style.overflow ="hidden";
      this.canvas.style.display = "none";
      this.canvas.style.zIndex = this.zIndex;
      this.eventMouseDown =  this.mouseDown.bindAsEventListener(this);
      Event.observe(this.canvas, "mousedown", this.eventMouseDown);
      
      if(this.productArea.maskUrl != null) {
        this.usingOverlay = true;
        this.overlay = document.createElement("IMG");
        this.overlay.style.position = "absolute";
        this.overlay.style.display = "none";
        this.overlay.style.zIndex = this.zIndex + 1;
        this.overlay.src = d.pathPrefix + "/images/trans.gif";
        this.configuredView.desPanel.appendChild(this.overlay);
        this.overlay = $(this.overlay);
        this.overlayMouseDownEvent = this.overlayMouseDown.bindAsEventListener(this);
        Event.observe(this.overlay, "mousedown", this.overlayMouseDownEvent);
        this.currentOverlayUrl = null;
        this.canvas.style.borderWidth="0px";
      } else {
        this.usingOverlay = false;
      }
      
      this.configuredView.desPanel.appendChild(this.canvas);
      this.canvas = $(this.canvas);
      if(!this.usingOverlay) {
        this.canvas2 = document.createElement("DIV");
        this.canvas2.id = "canvas_" + d.getNextId();
        this.canvas2.style.position = "absolute";
        this.canvas2.style.border="solid 1px black";
        this.canvas2.style.overflow ="hidden";
        this.canvas2.style.display = "none";
        this.canvas2.style.zIndex = this.zIndex-1;
        this.configuredView.desPanel.appendChild(this.canvas2);
        this.canvas2 = $(this.canvas2);
      }
      //this.vCanvas = Raphael(this.canvas, 400, 400); TODO: change to VML/SVG rendering...
    }
  },
  
  toString: function() {
    return this.configuredView.toString() + ":" + this.productArea.name;
  },
  
  //load from server through hashmap
  loadItems: function CPVA_loadItems(itemOptions) {
    for(var i=0; i < itemOptions.length; i++) {
      var itemData = itemOptions[i];
      var itemConfig = itemData.c;
    
      itemConfig.rv = itemData.rv; //put the render version into the config map
      var item = null;
      
      var id = (itemData.id == null) ? this.configuredProduct.getNextItemId() : this.configuredProduct.registerServerItemId(itemData.id);
      var processId = itemData.p;
      
      var cProcess = this.processes[processId];
      if(cProcess != null) {//the process may have been removed ..
        if(parseInt(itemConfig.it, 10) == "0") {
          item = new ImageItem(id, cProcess, d.assets[parseInt(itemConfig.aid, 10)], itemConfig);
        } else if(parseInt(itemConfig.it, 10) == "1") {
          item = new TextItem(id, cProcess, new TextAsset(new Hash()), itemConfig);
        } else if(parseInt(itemConfig.it, 10) == "2") {
          item = new TeamItem(id, cProcess, new TeamNameAsset(new Hash()), itemConfig);
        }
        cProcess.addItem(item);
      }
    }  
  },
  
  isUsed: function CPVA_isUsed() {
    return (this.visibleItemCount > 0);
  },
  
  registerItem: function CPVA_registerItem(item) {
    this.allItems[item.id] = item;
    this.itemCount ++;
    if (item.itemVisible()) this.visibleItemCount ++;
  },
  
  deRegisterItem: function CPVA_deRegisterItem(item) {
    this.itemCount --;
    if (item.itemVisible()) this.visibleItemCount --;
    delete this.allItems[item.id];
  },
    
  //called once the items have been added 
  loadConfiguration: function CPVA_loadConfiguration(configuration) {
    this.backgroundColor = configuration.bgColor;
    this.layoutScale = 1;
    if(configuration.rv != null) {
      this.renderVersion = configuration.rv;
    }
  },
  
  //init the ConfiguredViewProcesses
  initProcesses: function CPVA_initProcesses() {
    if(d.mode != DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      this.managePane = $(document.createElement("DIV"));
      this.managePane.id = "mp_" + d.getNextId();
      this.managePane.hide();
      d.managePaneContainer.appendChild(this.managePane);
      //get the list of available processes
    }
    this.processes = {};
    for(var i=0; i < this.productArea.processes.list.size(); i++) {
      var process = this.productArea.processes.list[i];
      this.processes[process.id] = new ConfiguredViewProcess(this, process);
    }
    
  },
  
  //choose what process to select when the area has been selected
  selectStartProcess: function CPVA_selectStartProcess() {
    
    if(d.userSelectedProcessId == null) { //user has selected 'all'
      log("selectStartProcess: user has selected 'all'");
      if(this.productArea.processes.list.size() ==1) {  
        log('But there is only one process, so lets choose that one.') ;
        return hashFirstElement(this.processes);
      }
      return null;
    }
    if(this.getAllowedProcesses()[d.userSelectedProcessId]==true) { //user has selected a process we can use
      log("selectStartProcess: user has selected allowed process");
      return this.processes[d.userSelectedProcessId];
    } else {
      log("selectStartProcess: user has selected disallowed process");
    }
    if((d.defaultProcess != null)&&(this.getAllowedProcesses()[d.defaultProcess.id]==true)) { //this area can use the default process
      log("selectStartProcess: default process is allowed");
      return this.processes[d.defaultProcess.id];
    }
    
    //find the first process with an item...
    for(var k in this.processes) {
      if(this.processes[k].isUsed()) {
        log("selectStartProcess: found used process");
        return this.processes[k];
      }
    }
    if(this.productArea.processes.list.size() ==1) {  
      return hashFirstElement(this.processes);
    }  
    log("selectStartProcess: defaulting to 'all'");
    //final fallback: all
    return null;
  },
  
  //position the canvas and set the style
  //doItems: reposition the items to the correct scale...
  prepareCanvas: function CPVA_prepareCanvas(doItems) {
    this.positionCanvas(doItems);
    this.setCanvasStyle();
  },
  
  deSelect: function CPVA_deSelect() {
    this.selected = false;
    this.canvas.style.zIndex = this.zIndex;
    if(this.canvas2 != null) {
      this.canvas2.style.zIndex = this.zIndex-1;
    }
    if(this.overlay != null) {
      this.overlay.style.zIndex = this.zIndex + 1;
    }
    this.selectItem(null);
    this.setCanvasStyle(); //deselect the canvas areas
    this.hideCurrentProcessPanels(); //hide the management panels
    this.enableCanvasItems(false); //disable the canvas items...
  },
  
  select: function CPVA_select() {
    this.selected = true;
    this.canvas.style.zIndex = this.configuredView.nextZIndex + 2;
    if(this.canvas2 != null) {
      this.canvas2.style.zIndex = this.configuredView.nextZIndex + 1;
    }
    if(this.overlay != null) {
      this.overlay.style.zIndex = this.configuredView.nextZIndex + 3;
    }
  },
  
  //set 
  setCanvasStyle: function CPVA_setCanvasStyle() {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    
    this.canvas.show();
    if(this.canvas2!=null) this.canvas2.show();
    if(((this.usingOverlay)||(!d.showGrid))/*||(d.currentCanvasType==1&&!this.selected)*/) {
      this.canvas.style.borderWidth="0px";
      if(this.canvas2 != null) this.canvas2.hide(); 
    } else {
      this.canvas.style.borderWidth="1px";
      if(this.canvas2 != null) this.canvas2.show();
      if(this.selected) {
        this.canvas.style.borderColor="#FFFF00";
        if(this.canvas2 != null) this.canvas2.style.borderColor="#FF0000";
      } else {
        this.canvas.style.borderColor="#FFFFFF";
        if(this.canvas2 != null) this.canvas2.style.borderColor="#000000";
      }
    }
  },
  
  //turn the management panels on/off based on selected process
  showManagementPanels: function CPVA_showManagementPanels() {
    var allowedProcesses = this.getAllowedProcesses();
    var uniqueProcs = 0;
    for(var k in allowedProcesses) {
      if(this.processes[k].isUsed()) uniqueProcs+=1;
    }
    var showHeadings = (uniqueProcs > 1);
    for(var k in allowedProcesses) {
      this.processes[k].showPanel(showHeadings, true);
    }
    this.managePane.show();
    this.checkNoOptions();
  },
  
  hideCurrentProcessPanels: function CPVA_hideCurrentProcessPanels() {
    log("hideCurrentProcessPanels()");
    for(var k in this.processes) {
      this.processes[k].hidePanel();
    }
  },
  
  updateAllowedProcesses: function CPVA_updateAllowedProcesses() {
    this.allowedProcesses = {};
    for(var k in this.processes) {
      this.allowedProcesses[k] = true;
    }
    for(var k in this.processes) {
      var cProcess = this.processes[k];
      if((cProcess.isUsed()) && (this.allowedProcesses[cProcess.id])) {
        this.allowedProcesses = cProcess.productProcess.productTypeProcess.getAllowedProcesses(this.allowedProcesses);
      }
    }
    return this.allowedProcesses;
  },
  
  //get the allowed item types for all allowed processes on this area
  getAllAllowedItemTypes: function CPVA_getAllAllowedItemTypes() {
    var allowedProcs = this.getAllowedProcesses();
    var allowed = {
      image: { count:0, single:null},
      text: { count:0, single:null},
      teamname: { count:0, single:null}
    };
    for(var i=0; i < this.productArea.processes.list.length; i++) {
      var pvap = this.productArea.processes.list[i];
      if(allowedProcs[pvap.id]) { //can be used on this area
        var proc = pvap.process;
        if(proc.allowImages) {
          allowed.image.count ++;
          allowed.image.single = pvap.id;
        }
        if(proc.allowText) {
          allowed.text.count ++;
          allowed.text.single = pvap.id;
        }
        if(proc.allowTeamNames) {
          allowed.teamname.count ++;
          allowed.teamname.single = pvap.id;
        }
      }
    }
    return allowed;
  },
  
  //get allowed processes based on what processes are currently used
  getAllowedProcesses: function CPVA_getAllowedProcesses() {
    if(this.allowedProcesses==null) {
      this.updateAllowedProcesses();
    }
    return this.allowedProcesses;
  },
  
  addNewItem: function CPVA_addNewItem(processId, asset, initData) {
    if(processId == null) { //find a matching process...
      for(var i=0; i < asset.processes.length;i++) {
        if(this.processes[asset.processes[i]]!=null) {
          processId = asset.processes[i];
          break;
        }
      }
      if(processId == null) {
        alert(ml("Unable to add image to product as it does not support the decoration process used by the image"));
        return;
      }
    }
    
    
    var process = this.processes[processId];
    var itemType = asset.getItemType();
    var newItem = null;
    if(itemType==0) { //normal image
      newItem = new ImageItem(this.configuredProduct.getNextItemId(), process, asset, initData);
    } else if(itemType==1) { //text
      newItem = new TextItem(this.configuredProduct.getNextItemId(), process, asset, initData);
      d.track("add-new-text");
    } else if(itemType==2) { //team name
      newItem = new TeamItem(this.configuredProduct.getNextItemId(), process, asset, initData);
      d.track("add-new-teamname");
    }
    return this.insertNewItem(newItem);
  },
  
  insertNewItem: function CPVA_insertNewItem(newItem) {
    var wasUsing = newItem.cViewProcess.isUsed();
    newItem.cViewProcess.addNewItem(newItem);
    this.showManagementPanels();
    if(!wasUsing) {
      //we are using a process for the first time.. lets update what process we can now use..
      this.updateAllowedProcesses();
    }
    this.setReRender(true);
    d.checkCopyPasteState();
    return newItem;
  },
  
  pasteItems: function CPVA_pasteItems(items) {
    for(var i=0; i < items.length; i++) {
      var newItem = items[i];
      var process = this.processes[processId];
      
    }
  },
  
  mouseDown: function CPVA_mouseDown(event) {
    log("mouseDown:" + this.toString());
    if(!this.selected) {
      if(event == null) {
        log("area isnt selected..event is null..not passing through....");
        return false;
      }
      var pointer = this.configuredView.getLayoutMousePosition(event);
      log("area isnt selected..passing through....");
      this.configuredView.proxyMouseDown(pointer[0],pointer[1], event);
      return false;
    } else {
      if(event == null) {
        log("area is selected..event is null..not selectAreaFromMouse....");
        return false;
      }
      var pointer = this.configuredView.getLayoutMousePosition(event);
      //click on area that did not hit an item...
      
      this.configuredView.selectAreaFromMouse(pointer[0],pointer[1],event);
    }
  },
  
  overlayMouseDown: function CPVA_overlayMouseDown(event) {
    log("overlayMouseDown:" + this.toString());
    if(!this.selected) {
      log("Area No Selected: Proxy through mouseDown");
      return this.mouseDown(event);
    }
    var pointer = this.configuredView.getLayoutMousePosition(event);
    //var dims = this.rel({l:pointer[0], t: pointer[1]});
    
    var i = this.hitTest(pointer[0], pointer[1]);
    if(i != null) {
      log("hit test found item");
      i.select();
      i.dragable.initDrag(event);
      Event.stop(event);
    } else {
      this.configuredView.selectAreaFromMouse(pointer[0],pointer[1],event);
    }
    return false;
  },
  
  removeNewItem: function CPVA_removeNewItem(item) {
    this.items[this.items.indexOf(item)] = null;
    this.items = this.items.compact();
    this.itemsById[item.id] = null;
    this.showManagementPanels();
    d.checkCopyPasteState();
  },
  
  tbTransformed: function CPVA_tbTransformed(options) {
    var item = this.allItems[options.id];
    if(item!=null) {
      item.tbTransformed(options);
    } else {
      log("tbTransformed: unable to get item " + options.id);
    }
  },
  
  setBgColor: function CPVA_setBgColor() {
    if(this.productArea.canSetBgColor) {
      if((this.bgColor == null) || (this.bgColor == "Transparent")) {
        this.canvas.style.backgroundColor = "";
      } else {
        this.canvas.style.backgroundColor = "#" + this.bgColor;
      }
    } else {
      this.canvas.style.backgroundColor = "";
    }
  },
  
  getOverlayURL: function CPVA_getOverlayURL(size) {
    if(this.productArea.maskUrl != null) {
      var colorId = this.configuredProduct.getSelectedColorId();
      var url = this.productArea.maskUrl.replace(d.layoutViewUrlCID, colorId);
      return url.replace(d.layoutViewUrlS, size); 
    } else {
      return null;
    }
  },
  
  updateOverlay: function CPVA_updateOverlay() {
    if(this.overlay!= null) {
      log("setting overlay", true);
      var url = this.getOverlayURL("Z"); //generate the url as if we are zoomed in...
      if(url != this.currentOverlayUrl) { 
        if(d.currentCanvasType == 0) { //we are not currently zoomed in... lets try the unzoomed version... 
          url = this.getOverlayURL(1);
          if(url != this.currentOverlayUrl) {
            setTransparentImage(d.designPane, this.overlay, url); //get the unzoomed version
          }
        } else {
          setTransparentImage(d.designPane, this.overlay, url); //get the zoomed version
        }
        this.currentOverlayUrl = url;
      }
    }
  },
  
  canZoom: function() {
    return (this.productArea.reScale > 1.2);
  },
  
  positionCanvas: function CPVA_positionCanvas(doItems) {
    if(d.mode==DESIGNER_MODE_VIEW_CUSTOM_PRODUCT) {
      return;
    }
    //log("positionCanvas. top=" + (this.productArea.t * d.zoom));
  
    
      
    var delta = 0;
    if(((!d.showGrid)&&(this.overlay== null))/*||(!this.selected && d.currentCanvasType==1)*/) {
      delta = 1;
    }
    
    this.canvas.style.top = (this.productArea.t * d.zoom + delta) + "px";
    this.canvas.style.left = (this.productArea.l * d.zoom + delta) + "px";
    this.canvas.style.width = (this.productArea.w * d.zoom - delta) + "px";
    this.canvas.style.height = (this.productArea.h * d.zoom - delta) + "px";
    if(this.canvas2 != null) {
      this.canvas2.style.top = (this.productArea.t * d.zoom + delta) + "px";
      this.canvas2.style.left = (this.productArea.l * d.zoom + delta) + "px";
      this.canvas2.style.width = (this.productArea.w * d.zoom - delta) + "px";
      this.canvas2.style.height = (this.productArea.h * d.zoom - delta) + "px";
    }
    
    if(this.overlay!= null) {
      this.overlay.style.top = (this.productArea.t * d.zoom) + "px";
      this.overlay.style.left = (this.productArea.l * d.zoom) + "px";
      this.overlay.style.width = (this.productArea.w * d.zoom)  + "px";
      this.overlay.style.height = (this.productArea.h * d.zoom) + "px";
      this.overlay.style.display = "";
      this.canvas.style.borderWidth="0px";
    } else {
      /*if(!this.selected && d.currentCanvasType==1) { //hide outlines of unselected areas when zoomed in
        this.canvas.style.borderWidth="0px";
        if(this.canvas2 != null) this.canvas2.hide();
      } else {*/
        if(d.showGrid) {
          this.canvas.style.borderWidth="1px";
          if(this.canvas2 != null) this.canvas2.show();
        } else {
          this.canvas.style.borderWidth="0px";
          if(this.canvas2 != null) this.canvas2.hide();
        }
      //}
    }
      

    
    this.canvasX=null; //will reset the calibration
        
    if((doItems)&&(this.initialised)) {
      this.recalibrate();
    }
  },
  
  canvasSize: function CPVA_canvasSize() {
    //if(d.currentCanvasType==0) { //LAYOUT
      return {w: this.productArea.w * d.zoom, h:this.productArea.h * d.zoom};
    //} else { //DESIGN
    //  return {w: this.productArea.dW, h:this.productArea.dH};
    //}
  },
  
  abs: function CPVA_abs(dims) {
    this.checkCalibration();
    if(dims.l != null) {
      dims.l += this.canvasX;
    }
    if(dims.t != null) {
      dims.t += this.canvasY;
    }
    return dims;
  },   
  
  rel: function CPVA_rel(dims) {
    //this.checkCalibration();
    if(dims.l != null) {
      dims.l -= this.productArea.l;
    }
    if(dims.t != null) {
      dims.t -= this.productArea.t;
    }
    return dims;
  },
  
  checkCalibration: function CPVA_checkCalibration() {
    if(this.canvasX==null) {
      var pos = Position.cumulativeOffset(this.canvas);
      this.canvasX = pos[0];
      this.canvasY = pos[1];
    }
  },
  
  recalibrate: function CPVA_recalibrate() {
    this.canvasX=null; //will reset the calibration
        
    if(this.initialised) {
      for(var k in this.allItems) {
        this.allItems[k].quickSetPosition();
      }
      if((this.selectedItem)&&(this.selected)) {
        this.selectedItem.autoRepositionHandles();
      }
    }
  }, 
  
  checkInitialised: function CPVA_checkInitialised() {
    if(!this.initialised) {
      log("checkInitialised");
      log(this);
      this.updateOverlay();
      for(k in this.processes) {
        var process = this.processes[k];
        process.initItems();
      }
      this.initialised = true;
    }
  },
  
  //select the area from the interface...
  show: function CPVA_show(allowTransition) {
    this.checkInitialised();
    this.configuredView.selectArea(this, allowTransition);
    
    this.enableCanvasItems(true);
    this.checkNoOptions();
    this.canvas.show();
    if(this.canvas2 != null) {
      this.canvas2.show();
    }

    //this.canvasX=null; //will reset the calibration // this.configuredView.selectArea will do this
    //this.recalibrate(); //make sure all the items use the correct scale
    
    this.setBgColor();
    

    var bgcDiv = $("canvas_bg_color");
    if(this.productArea.canSetBgColor) {
      var button = $("bg_color_button");
      if((this.bgColor==null)||(this.bgColor=="Transparent")) {
        button.style.backgroundColor = "";
        button.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
      } else {
        button.style.backgroundColor = "#" + this.bgColor;
        button.style.backgroundImage = "";
      }
      bgcDiv.style.display="";
    } else {
      bgcDiv.style.display="none";
    }
    this.showManagementPanels();
  },
  
  
  //enable/disable canvas items based on area selection
  enableCanvasItems: function CPVA_enableCanvasItems(enabled) {
    for(var k in this.allItems) {
      this.allItems[k].setEnabled(enabled);
    }
  },
  
  hide: function CPVA_hide() {
    this.canvas.hide();
    if(this.canvas2 != null) {
      this.canvas2.hide();
    }
    this.managePane.hide();
    if(this.selectedItem) {
      this.selectedItem.deSelect();
    }
    if(this.overlay!= null) {
      this.overlay.hide();
    }
  },
  
  selectItem: function CPVA_selectItem(item) {
    if(this.selectedItem) {
      this.selectedItem.deSelect();
    } else {
      log("Area.selectItem: no existing selection");
    }
    this.selectedItem = item;
    d.checkCopyPasteState();
  },
  
  findAndSelectItem: function CPVA_findAndSelectItem() {
    var proc = null;

    for(var k in this.processes) {
      if(this.processes[k].isUsed()) {
        proc = this.processes[k];
      }
    }

    if((proc == null) || (!proc.hasVisibleItems())) {
      return false;
    }
    log("findAndSelectItem");
    proc.visibleItems.list[0].select();
    return true;
  },
  
  checkNoOptions: function CPVA_checkNoOptions() {
    log("checkNoOptions.itemCount:" + this.visibleItemCount);
    if(this.visibleItemCount == 0) {
      $("no_items").show();
      $("has_items").hide();
    } else {
      $("no_items").hide();
      $("has_items").show();
    }
  },
  
  serialize: function CPVA_serialize(queryComponents, prefix) {
    this.checkInitialised();
    if(prefix==null) {
      prefix="a[" + this.id + "]";
    }
    if(queryComponents==null) {
      queryComponents = new Array();
    }
    queryComponents.push(encodeURIComponent(prefix + "[rv]") + "=" + encodeURIComponent(this.renderVersion));
    if((this.bgColor == null)||(this.bgColor == "Transparent")) {
      queryComponents.push(encodeURIComponent(prefix + "[bg]") + "=" + encodeURIComponent("Transparent"));
    } else {
      
      queryComponents.push(encodeURIComponent(prefix + "[bg]") + "=" + encodeURIComponent(this.bgColor));
    }
    
    for(var k in this.allItems) {
      var item = this.allItems[k];
      var p = prefix + "[i][" + item.id + "]";
      item.serialize(queryComponents, p);
    }
    return queryComponents.join('&');
  },
  
  remove: function CPVA_remove() {
    for(var k in this.processes) {
      this.processes[k].remove();
    }

    if(this.canvas != null) {
      Event.stopObserving(this.canvas, "mousedown", this.eventMouseDown);
    }
    if(this.overlay != null) {
      Event.stopObserving(this.overlay, "mousedown", this.overlayMouseDownEvent);
    }
    
    this.canvas.parentNode.removeChild(this.canvas);
    if(this.canvas2 != null) {
      this.canvas2.parentNode.removeChild(this.canvas2);
    }
  },
  

  
  hitTest: function CPVA_hitTest(x,y) {
    log("hit test:" + x + "." + y);
    log(this.productArea);
    
    var dims = this.rel({l:x, t: y});
    
    x = dims.l;
    y = dims.t;
    
    log("hit test rel:" + x + "." + y);
    
    if(x < 0 || x > this.productArea.w  || y < 0 || y > this.productArea.h) {
      log("Hit Test Fail: Not even in area");
      return null;
    }
    
    
    
    //var dims = d.fromCanvasDims({l:x, t:y});
    //x = dims.l;
    //y = dims.t;
    //log(dims);
    var bestItem = null;
    var bestZIndex = -1;
    for(var k in this.allItems) {
      if(this.allItems[k].hitTest(x,y)) {
        if(this.allItems[k].selected) {
          return this.allItems[k];
        } else if(this.allItems[k].zIndex > bestZIndex) {
          bestItem = this.allItems[k];
          bestZIndex = bestItem.zIndex;
        }
      }
    }
    return bestItem;
  },
  
  // move all the elements from the src layout manager, at the same time rescaling them to fit in the current canvas
  moveElements: function CPVA_moveElements(src, testOnly, makeCopy, itemsToCopy) {
    if(testOnly) {
      return this.testIfCanMove(src);
    }
    //the items store dims in "real" co-ords... convert them to design canvas co-ords, rescale, the convert back to new "real" co-ords
    log("Moving Elements from " + src.productArea.getName() + " to " + this.productArea.getName());
    var sDims = src.getMinCanvasSize();
    
    log(sDims);
    
    var x1 = sDims.left; //left gap
    var x2 = src.productArea.dW - (sDims.left + sDims.width); //right gap
    var horizontalGapScale = (x1 + x2==0 || x1==0 && Math.abs(x2) <= 1) ? 0.5 : parseFloat(x1) / parseFloat(x1 + x2);
    
    var y1 = sDims.top; //top gap
    var y2 = src.productArea.dH - (sDims.top + sDims.height); //bottom gap
    var verticalGapScale = (y1 + y2==0) ? 0.5 : parseFloat(y1) / parseFloat(y1 + y2);
    var selfCopy = (src == this); //are we copying over ourselves?
    
    
    
    var xScale = parseFloat(sDims.width) / parseFloat(this.productArea.dW);
    var yScale = parseFloat(sDims.height) / parseFloat(this.productArea.dH);
    
    var rScale = (xScale > yScale) ? xScale : yScale;
    
    if(rScale < 1) { //we dont want to make it any bigger....
      rScale= 1.0;
    }
    
    log("y1=" + y1 + " y2=" + y2 + " selfCopy=" + selfCopy);
    
    log("xScale=" + xScale + " yScale=" + yScale + " rScale=" + rScale + " horizontalGapScale=" + horizontalGapScale + " verticalGapScale=" + verticalGapScale);
    
    //we want to use rScale on the items.. lets check we can....
    
    var allowedProcesses = this.getAllowedProcesses();
    var needAllowUpdate = false;
    
    var addedItems = [];
    
    var srcArray = []; //make a copy of the src becuase copy() will add it to src.allItems causing an infiniate loop
    if(itemsToCopy == null) {
      for(var k in src.allItems) {
        srcArray.push(src.allItems[k]);
      }
    } else {
      srcArray = itemsToCopy;
    }
    
    for(var i = 0; i < srcArray.length; i++) {
      needAllowUpdate = false;
      var item = srcArray[i];
      if(allowedProcesses[item.cViewProcess.id]) {
        var cProcess = this.processes[item.cViewProcess.id];
        if(!cProcess.isUsed()) {
          needAllowUpdate = true;
        }
        var itemToAdd = (makeCopy==true) ? item.copy() : item;
        addedItems.push(itemToAdd);
        if(!selfCopy) cProcess.addItem(itemToAdd);
        //check the largest scale we can use...
        rScale = itemToAdd.checkScale(rScale, cProcess.productProcess);
        if(needAllowUpdate) {
          //we have used a process for the first time.. this could exclude other processes...
          allowedProcesses = this.updateAllowedProcesses();
          log(allowedProcesses);
        }
      } else {
        log("Cannot use item for process " + item.cViewProcess.id);
        //store unusable items in case we change products later..
      }
    }
    
    
    //refactor the width/height using the new scale...
    var finalWidth = parseInt(sDims.width / rScale, 10);
    var finalHeight = parseInt(sDims.height / rScale, 10);
    //var newLeft = parseInt(((this.productArea.dW - finalWidth)/2) * horizontalGapScale, 10);
    //var newTop = parseInt(((this.productArea.dH - finalHeight)/2) * verticalGapScale, 10);
    
    var offsetLeft = parseInt(((this.productArea.dW - finalWidth)) * horizontalGapScale, 10); //sDims.left - newLeft;
    var offsetTop = parseInt(((this.productArea.dH - finalHeight)) * verticalGapScale, 10); //sDims.top - newTop;
    log("after checking scale rScale=" + rScale + " this.productArea.dH=" + this.productArea.dH + " finalWidth=" + finalWidth + " finalHeight=" + finalHeight + " offsetLeft=" + offsetLeft + " offsetTop=" + offsetTop);
    
    log(this);
                         
    //sort addedItems by zindex
    if(makeCopy == true) {
      addedItems = addedItems.sortBy( function(i) { return i.zIndex;});
      
    }
    for(var i=0; i < addedItems.length; i++) {
      var item = addedItems[i];
      var cProcess = this.processes[item.cViewProcess.id];
      if(!selfCopy) item.cViewProcess.removeItem(item); //we need to remove from src process's item list otherwise cleanup of src will destroy this...
      if(makeCopy == true) {
        item.zIndex = null;
      }
      item.moveToDesigner(cProcess, rScale, sDims.left, sDims.top, offsetLeft, offsetTop);
      item.setPosition(true);
    }
    this.initialised = src.initialised;
    
    sDims = this.getMinCanvasSize();
    log(sDims);
  },
  
  
  copyTo: function CPVA_copyTo(destArea, copyState) {
    var existingCount = hashSize(destArea.allItems);
    var thisCount = copyState.length;
    destArea.moveElements(this, false, true, copyState);
    this.setReRender(true);
    var newCount = hashSize(destArea.allItems);
    return [newCount-existingCount, thisCount];
  },
  
  getCopyState: function CPVA_getCopyState() {
    var srcArray = []; //make a copy of the src becuase copy() will add it to src.allItems causing an infiniate loop
    for(var k in this.allItems) {
      srcArray.push(this.allItems[k]);
    }
    return srcArray;
  },
  
  //called after the items have all moved to another product and everything is setup again
  validate: function CPVA_validate() {
    for(var k in this.allItems) {
      var item = this.allItems[k];
      item.validate();
    }
  },
  
  getMinCanvasSize: function CPVA_getMinCanvasSize() {
    var top = -1;
    var bottom = -1;
    var left = -1;
    var right = -1;
    for(var k in this.allItems) {
      var item = this.allItems[k];
      var dims = item.getCanvasDims();
      if(dims.t < top || top == -1) {
        top = dims.t;
      }
      if(dims.t + dims.h > bottom || bottom == -1) {
        bottom = dims.t + dims.h;
      }
      if(dims.l < left || left == -1) {
        left = dims.l;
      }
      if(dims.l + dims.w > right || right == -1) {
        right = dims.l + dims.w;
      }
    }
    
    //we now have the max dims...
    return {top : top, left: left, width: right - left, height: bottom - top};
  },
  
  testIfCanMove: function CPVA_testIfCanMove(src) {
    var allowedProcesses = this.getAllowedProcesses();
    var all_ok = true;
    var any_ok = false;
    for(var k in src.allItems) {
      var item = src.allItems[k];
      if(allowedProcesses[item.cViewProcess.id]) {
        any_ok = true ;
      } else {
        all_ok = false;
      }
    }
    //we move to the higher number so 'any match' overrides 'no match' or 'all matches'
    if(all_ok) {
      return 0;
    } else if(any_ok) {
      return 2;
    } else {
      return 1;
    }
  },
  
  selectBackgroundColor: function CPVA_selectBackgroundColor(processId) {
    var self = this;
    var button = $("bg_color_button");
    pwColorPicker.selectColor(processId, button, self.bgColor, 
    //showColorPicker(button, 
      function(c) { 
        if(c=="Transparent") {
          self.bgColor = "Transparent"; 
          button.style.backgroundColor = "";
          button.style.backgroundImage = "url(/ppr/images/trans-display-small.gif)";
          self.canvas.style.backgroundColor = "";
        } else {
          self.bgColor = c.substr(1, 6);
          button.style.backgroundColor = c;
          button.style.backgroundImage = "";
          self.canvas.style.backgroundColor = c;
        }
        self.setReRender();
      }, {allow_transparency:true});
    
  },
  
  setChildReRender: function CPVA_setChildReRender(queueUpdate) {
    this.reRenderChild = true;
    this.renderVersion ++;
    this.configuredView.setChildReRender(queueUpdate);
  },
  
  setReRender: function CPVA_setReRender(queueUpdate) {
    this.reRender = true;
    this.renderVersion ++;
    this.configuredView.setChildReRender(queueUpdate);
  },
  
  clearReRender: function CPVA_clearReRender() {
    this.reRenderChild = false;
    this.reRender = false;
    for(var k in this.allItems) {
      this.allItems[k].clearReRender();
    }
  },
  
  //calcuate for price to do the printing this object stores..
  //colorType: the type of color (white/light/dark)
  //priceData: object to store the price breakdown in (priceData.extras)
  //returns total this views costs to decorate
  calculatePrice: function CPVA_calculatePrice(colorType, fullPriceData, priceData, percentMarkup) {
    var areaTotal = 0;
    var areaPriceData = {
      type: 2,
      area: this,
      extras: []
    };
    for(var i=0; i < processes.list.size(); i++) {
      var process = processes.list[i];
      var cProcess = this.processes[process.id];
      if(cProcess != null && cProcess.isUsed()) {
        areaTotal += cProcess.calculatePrice(colorType,fullPriceData, areaPriceData, percentMarkup);
      }
    }
    if(areaTotal > 0) {
      areaPriceData.total = areaTotal * percentMarkup;
      priceData.extras.push(areaPriceData);
    }
    return areaTotal;
  },
  
  setErrors: function CPVA_setErrors(errors) {
    if(errors==null) errors = {};
    if(this.lastErrors != null) {
      //clear all errors from last errors not in these errors before setting these errors...
      for(var k in this.lastErrors) {
        if(errors[k] == null) {
          var error = this.lastErrors[k];
          var item = this.allItems[error.cause];
          if(item != null) { //it may have been deleted...
            item.removeAlert(0, error.type);
          } else {
            this.configuredProduct.setAlertIcons(); //it may need to be recalced...
          }
        }
      }
    }
    for(var k in errors) {
      var error = errors[k];
      var item = this.allItems[error.cause];
      if(item != null) {
        var message = error.message == null ? ml(error.type) : error.message;
        if(this.lastErrors!=null && this.lastErrors[k] != null) { //same error...
          item.updateAlertMessage(0, error.type, "", message);
        } else {
          item.addAlert(0, error.type, "alert_" + error.type, "", message, true);
        }
      }
    }
    this.lastErrors = errors;
  }
      
});
