function Picturebox(){
    this.pictures = [];
}

Picturebox.newFromDiv = function(div){
    picturebox = new Picturebox();
    picturebox.div = div;
    
    var picture;
    var children = div.getElementsByTagName('div');
    for (var i = 0, l = children.length; i < l; ++i){
        picture = PictureboxPicture.newFromDiv(children[i]);
        picturebox.addPicture(picture);
    }
    
    picturebox.setupDiv();
    
    return picturebox;
};

Picturebox.prototype = {
    div: null,
    scrollDiv: null,
    pictures: null,
    currentPage: null,
    nextDiv: null,
    prevDiv: null,
    pagerDiv: null,
    listeners: null,
    
    setupDiv: function(){
      if (this.div.scrollWidth > this.div.clientWidth){
      
        this.scrollDiv = this.div.appendChild(document.createElement('div'));
        this.scrollDiv.style.position = 'absolute';
        this.scrollDiv.style.top = '0px';
        this.scrollDiv.style.left = '20px';
        this.scrollDiv.style.right = '20px';
        this.scrollDiv.style.bottom = '10px';
        this.scrollDiv.style.overflow = 'hidden';
        
        for (var i = 0, l = this.pictures.length; i < l; ++i){
          this.scrollDiv.appendChild(this.pictures[i].div);
        }
          
        this.nextDiv = this.div.appendChild(document.createElement('div'));
        this.prevDiv = this.div.appendChild(document.createElement('div'));
        this.pagerDiv = this.div.appendChild(document.createElement('div'));
        
        this.nextDiv.className = 'picturebox-next';
        this.prevDiv.className = 'picturebox-prev';
        this.pagerDiv.className = 'picturebox-pager';
        this.pagerDots = [];
        
        this.listeners = {
          nextDiv: {
            mouseover: this.handleNextMouseOver.scope(this),
            mouseout: this.handleNextMouseOut.scope(this),
            mousedown: this.handleNextMouseDown.scope(this),
            mouseup: this.handleNextMouseUp.scope(this)
          },
          prevDiv: {
            mouseover: this.handlePrevMouseOver.scope(this),
            mouseout: this.handlePrevMouseOut.scope(this),
            mousedown: this.handlePrevMouseDown.scope(this),
            mouseup: this.handlePrevMouseUp.scope(this)
          },
          pagerDiv: {
            mouseover: this.handlePagerMouseOver.scope(this),
            mouseout: this.handlePagerMouseOut.scope(this),
            mousedown: this.handlePagerMouseDown.scope(this),
            mouseup: this.handlePagerMouseUp.scope(this)
          }
        };
        
        for (var prop in this.listeners){
          for (var type in this.listeners[prop]){
            this[prop].addEventListener(type, this.listeners[prop][type]);
          }
        }
        
        var w = this.scrollDiv.clientWidth;
        
        this.pages = [];
        var page = [];
        this.pages.push(page);
        var offset = 0;
        for (var i = 0, l = this.pictures.length; i < l; ++i){
          if (this.pictures[i].div.offsetLeft + this.pictures[i].div.offsetWidth > offset + w){
            var first = page[0].div;
            var last = page[page.length - 1].div;
            first.style.marginLeft = Math.round((w - (last.offsetLeft + last.offsetWidth - first.offsetLeft)) / 2)+ 'px';
            offset = this.pictures[i].div.offsetLeft - 10;
            page = [];
            this.pages.push(page);
          }
          page.push(this.pictures[i]);
        }
        
        var first = page[0].div;
        var last = page[page.length - 1].div;
        first.style.marginLeft = Math.round((w - (last.offsetLeft + last.offsetWidth - first.offsetLeft)) / 2)+ 'px';
        last.style.marginRight = first.style.marginLeft;
        
        this.showPage(0);
      }
    },
    
    showPage: function(i){
      this.currentPage = i;
      var page = this.pages[i];
      
      for (var i = 0, l = this.pictures.length; i < l; ++i){
        this.pictures[i].div.style.visibility = 'visible';
      }
    
      var s0 = this.scrollDiv.scrollLeft;
      var s = page[0].div.offsetLeft - parseInt(page[0].div.style.marginLeft);
    
      var t = 200;
      var dt = 40;
      
      var steps = t / dt;
      var step = 0;
      var t0 = new Date();
      
      var self = this;
      
      var interval = setInterval(function(){
          var _t = new Date();
          var _dt = _t - t0;
          t0 = _t;
          var stepsTaken = _dt / dt;
          step += stepsTaken;
          
          if (step > steps){
              clearInterval(interval);
              
              self.scrollDiv.scrollLeft = s;
      
              for (var i = 0, l = page[0].index; i < l; ++i){
                self.pictures[i].div.style.visibility = 'hidden';
              }
              for (var i = 0, l = page.length; i < l; ++i){
                page[i].div.style.visibility = 'visible';
              }
              for (var i = page[page.length -1].index + 1, l = self.pictures.length; i < l; ++i){
                self.pictures[i].div.style.visibility = 'hidden';
              }
          }else{
              var ratio = step / steps;
              self.scrollDiv.scrollLeft = s0 + (s - s0) * ratio;
          } 
      
      }, dt);
      
      for (var i = 0, l = this.pages.length; i < l; ++i){
        if (!this.pagerDots[i]){
          this.pagerDots.push(this.pagerDiv.appendChild(document.createElement('div')));
        }
        if (i == this.currentPage){
          this.pagerDots[i].className = 'picturebox-pager-dot selected';
        }else{
          this.pagerDots[i].className = 'picturebox-pager-dot';
        }
      }
      
    },
    
    addPicture: function(picture){
        picture.picturebox = this;
        picture.index = this.pictures.length;
        this.pictures.push(picture);
    },
    
    presentPicture: function(picture){
        if (picture.picturebox != this){
            throw new Error('Trying to present a picture from a different picturebox');
        }
        
        pictureWindow = PictureboxWindow.sharedWindow();
        pictureWindow.setPicture(picture);
        pictureWindow.show();
    },
    
    setNextHighlighted: function(highlighted){
      this.nextHighlighted = highlighted;
    },
    
    setNextPressed: function(pressed){
      this.nextPressed = pressed;
    },
    
    handleNextMouseOver: function(){
      this.setNextHighlighted(true);
    },
    
    handleNextMouseOut: function(){
      this.setNextHighlighted(false);
      if (this.nextPressed){
        this.setNextPressed(false);
      }
    },
    
    handleNextMouseDown: function(){
      this.setNextPressed(true);
    },
    
    handleNextMouseUp: function(){
      if (this.nextPressed){
        this.showPage((this.currentPage + 1) % this.pages.length);
        this.setNextPressed(false);
      }
    },
    
    setPrevHighlighted: function(highlighted){
      this.prevHighlighted = highlighted;
    },
    
    setPrevPressed: function(pressed){
      this.prevPressed = pressed;
    },
    
    handlePrevMouseOver: function(){
      this.setPrevHighlighted(true);
    },
    
    handlePrevMouseOut: function(){
      this.setPrevHighlighted(false);
      if (this.prevPressed){
        this.setPrevPressed(false);
      }
    },
    
    handlePrevMouseDown: function(){
      this.setPrevPressed(true);
    },
    
    handlePrevMouseUp: function(){
      if (this.prevPressed){
        this.showPage((this.currentPage + this.pages.length - 1) % this.pages.length);
        this.setPrevPressed(false);
      }
    },
    
    setPagerHighlighted: function(highlighted){
      this.pagerHighlighted = highlighted;
    },
    
    setPagerPressed: function(pressed){
      this.pagerPressed = pressed;
    },
    
    handlePagerMouseOver: function(){
      this.setPagerHighlighted(true);
    },
    
    handlePagerMouseOut: function(){
      this.setPagerHighlighted(false);
      if (this.pagerPressed){
        this.setPagerPressed(false);
      }
    },
    
    handlePagerMouseDown: function(){
      this.setPagerPressed(true);
    },
    
    handlePagerMouseUp: function(){
      if (this.pagerPressed){
        this.showPage((this.currentPage + 1) % this.pages.length);
        this.setPagerPressed(false);
      }
    }
};

function PictureboxPicture(){
    this.highlighted = false;
    this.pressed = false;
    this.visible = true;
    
    this.listeners = {
        mouseover: this.handleMouseOver.scope(this),
        mouseout: this.handleMouseOut.scope(this),
        mousedown: this.handleMouseDown.scope(this),
        mouseup: this.handleMouseUp.scope(this)
    };
}

PictureboxPicture.newFromDiv = function(div){
    var picture = new PictureboxPicture();
    picture.div = div;
    picture.setupEventListeners();
    return picture;
};

PictureboxPicture.prototype = {
    picturebox: null,
    index: null,
    div: null,
    highlighted: null,
    pressed: null,
    visible: null,
    listeners: null,
    label: null,
    
    setupEventListeners: function(){
        this.div.style.cursor = 'pointer';
        this.label = this.div.title;
        
        for (var type in this.listeners){
            this.div.addEventListener(type, this.listeners[type], false);
        }
    },
    
    setHighlighted: function(highlighted){
        this.highlighted = highlighted;
    },
    
    setPressed: function(pressed){
        this.pressed = true;
    },
    
    handleMouseOver: function(e){
        this.setHighlighted(true);
    },
    
    handleMouseOut: function(e){
        this.setHighlighted(false);
        if (this.pressed){
            this.setPressed(false);
        }
    },
    
    handleMouseDown: function(e){
        this.setPressed(true);
    },
    
    handleMouseUp: function(e){
        if (this.pressed){
            this._activate();
        }
        this.setPressed(false);
    },
    
    setVisible: function(visible){
        this.visible = visible;
        if (this.visible){
            this.div.style.visibility = 'visible';
        }else{
            this.div.style.visibility = 'hidden';
        }
    },
    
    _activate: function(){
        this.present();
    },
    
    present: function(){
        this.picturebox.presentPicture(this);
    }
};

function PictureboxWindow(){
    this.div = document.body.appendChild(document.createElement('div'));
    this.div.className = 'PictureboxWindow';
    this.div.style.position = 'absolute';
    this.setHidden(true);
    this.pictureDiv = this.div.appendChild(document.createElement('div'));
    this.pictureDiv.className = 'PictureboxWindow-Picture';
    this.pictureImg = this.pictureDiv.appendChild(document.createElement('img'));
    this.closeDiv = this.div.appendChild(document.createElement('div'));
    this.closeDiv.className = 'PictureboxWindow-Close';
    this.labelDiv = this.div.appendChild(document.createElement('div'));
    this.labelDiv.className = 'PictureboxWindow-Label';
    this.labelTextNode = this.labelDiv.appendChild(document.createTextNode(''));
    
    this.closePressed = false;
    this.closeHighlighted = false;
    this.picturePressed = false;
    
    this.listeners = {
        closeDiv: {
            mouseover: this.handleCloseMouseOver.scope(this),
            mouseout: this.handleCloseMouseOut.scope(this),
            mousedown: this.handleCloseMouseDown.scope(this),
            mouseup: this.handleCloseMouseUp.scope(this)
        },
        pictureDiv: {
            mouseout: this.handlePictureMouseOut.scope(this),
            mousedown: this.handlePictureMouseDown.scope(this),
            mouseup: this.handlePictureMouseUp.scope(this)
        },
        pictureImg: {
            load: this.handlePictureLoad.scope(this)
        }
    };
    
    for (var div in this.listeners){
        for (var type in this.listeners[div]){
            this[div].addEventListener(type, this.listeners[div][type]);
        }
    }
    
}

PictureboxWindow._sharedWindow = null;

PictureboxWindow.sharedWindow = function(){
    if (!PictureboxWindow._sharedWindow){
        PictureboxWindow._sharedWindow = new PictureboxWindow();
    }
    return PictureboxWindow._sharedWindow;
};

PictureboxWindow.prototype = {
    div: null,
    pictureDiv: null,
    pictureImg: null,
    labelDiv: null,
    labelTextNode: null,
    hidden: null,
    picture: null,
    
    loadingTimeout: null,
    
    closePressed: null,
    closeHighlighted: null,
    picturePressed: null,
    
    setHidden: function(hidden){
        this.hidden = hidden;
        if (this.hidden){
            this.div.style.display = 'none';
        }else{
            this.div.style.display = '';
        }
    },
    
    setPicture: function(picture){
        this.picture = picture;
    },
    
    show: function(){
        if (this.picture){
        
            var src = this.picture.div.style.backgroundImage.replace(/^url\(['"]?(.+)-thumbnail(.+)['"]?\)$/,'$1$2');
            
            if (src){
                if (this.hidden){
                    this.pictureDiv.style.width = (this.picture.div.clientWidth) + 'px';
                    this.pictureDiv.style.height = (this.picture.div.clientHeight) + 'px';
                    
                    this.div.style.visibility = 'hidden';
                    this.setHidden(false);
                    
                    var e = this.picture.div;
                    var y = 0;
                    do {
                        y += e.offsetTop;
                        e = e.offsetParent;
                    } while (e);
                    
                    var e = this.picture.div;
                    var x = 0;
                    do {
                        x += e.offsetLeft;
                        e = e.offsetParent;
                    } while (e);
                    
                    this.div.style.top = (y - this.pictureDiv.offsetTop - this.pictureDiv.clientTop + this.picture.div.clientTop) + 'px';
                    this.div.style.left = (x - this.pictureDiv.offsetLeft - this.pictureDiv.clientLeft + this.picture.div.clientLeft) + 'px'; // TODO: off by 1...but why???
                    
                    this.div.style.visibility = '';
                       
                }

                this.pictureImg.style.visibility = 'hidden';
                this.pictureImg.style.width = 'auto';
                this.pictureImg.style.height = 'auto';
                this.labelTextNode.nodeValue = '';
                
                if (src != this.pictureImg.src){
                    this.pictureImg.src = src;
                    
                    this.loadingTimeout = setTimeout(this.handleLoadingTimeout.scope(this),500);
                }else{
                    this.animatePictureChange();
                }
                
            }else{
                this.setHidden(true);
            }
        }else{
            this.setHidden(true);
        }
    },
    
    animatePictureChange: function(){
        var h = this.pictureImg.offsetHeight;
        var w = this.pictureImg.offsetWidth;
        
        var W = w + this.div.offsetWidth - this.pictureDiv.clientWidth;
        var H = h + this.div.offsetHeight - this.pictureDiv.clientHeight;
        
        if (W > document.body.clientWidth){
            var tmp = w - (W - document.body.clientWidth);
            W = document.body.clientWidth;
            var ratio = tmp / w;
            var w = tmp;
            var h = Math.round(h * ratio);
            var H = h + this.div.offsetHeight - this.pictureDiv.clientHeight;
        }
        
        if (H > document.body.clientHeight){
            var tmp = h - (H - document.body.clientHeight);
            H = document.body.clientHeight;
            var ratio = tmp / h;
            var h = tmp;
            var w = Math.round(w * ratio);
            var W = w + this.div.offsetWidth - this.pictureDiv.clientWidth;
        }
        
        var x = Math.round((document.body.clientWidth - W) / 2) + document.body.scrollLeft + document.documentElement.scrollLeft;
        var y = Math.round((document.body.clientHeight - H) / 2) + document.body.scrollTop + document.documentElement.scrollTop;
        
        var w0 = parseInt(this.pictureDiv.style.width);
        var h0 = parseInt(this.pictureDiv.style.height);
        var x0 = parseInt(this.div.style.left);
        var y0 = parseInt(this.div.style.top);
    
        this.pictureImg.style.width = '100%';
        this.pictureImg.style.height = 'auto';
        this.pictureImg.style.visibility = 'visible';
    
        var t = 200;
        var dt = 40;
        
        var steps = t / dt;
        var step = 0;
        var t0 = new Date();
        
        var self = this;
        
        var interval = setInterval(function(){
            var _t = new Date();
            var _dt = _t - t0;
            t0 = _t;
            var stepsTaken = _dt / dt;
            step += stepsTaken;
            
            if (step > steps){
                clearInterval(interval);
                self.pictureDiv.style.width = w + 'px';
                self.pictureDiv.style.height = h + 'px';
                self.div.style.left = x + 'px';
                self.div.style.top = y + 'px';
                if (self.picture.label !== null){
                  self.labelTextNode.nodeValue = self.picture.label;
                }
            }else{
                var ratio = step / steps;
                self.pictureDiv.style.width = (w0 + (w - w0) * ratio) + 'px';
                self.pictureDiv.style.height = (h0 + (h - h0) * ratio) + 'px';
                self.div.style.left = (x0 + (x - x0) * ratio) + 'px';
                self.div.style.top = (y0 + (y - y0) * ratio) + 'px';
            } 
        
        }, dt);
        
    },
    
    setClosePressed: function(pressed){
        this.closePressed = pressed;
    },
    
    setCloseHighlighted: function(highlighted){
        this.closeHighlighted = highlighted;
    },
    
    setPicturePressed: function(pressed){
        this.picturePressed = pressed;
    },
    
    handleCloseMouseOut: function(e){
        this.setCloseHighlighted(false);
        if (this.closePressed){
            this.setClosedPressed(false);
        }
    },
    
    handleCloseMouseOver: function(e){
        this.setCloseHighlighted(true);
    },
    
    handleCloseMouseDown: function(e){
        this.setClosePressed(true);
    },
    
    handleCloseMouseUp: function(e){
        if (this.closePressed){
            this.setClosePressed(false);
            this.setHidden(true);
        }
    },
    
    handlePictureMouseOut: function(e){
        if (this.picturePressed){
            this.setPicturePressed(false);
        }
    },
    
    handlePictureMouseDown: function(e){
        this.setPicturePressed(true);
    },
    
    handlePictureMouseUp: function(e){
        if (this.picturePressed){
            this.setPicturePressed(false);
            var picturebox = this.picture.picturebox;
            var index = (this.picture.index + 1) % picturebox.pictures.length;
            this.setPicture(picturebox.pictures[index]);
            this.show();
        }
    },
    
    handlePictureLoad: function(e){
        if (this.loadingTimeout){
            clearTimeout(this.loadingTimeout);
            this.loadingTimeout = null;
        }
        this.animatePictureChange();
    },
    
    handleLoadingTimeout: function(e){
        this.loadingTimeout = null;
    }
    
};

