function Thumbnail() {
}

Thumbnail.prototype.setCurrentWindowWidth = function(clientWidth) {
  this.currentWindowWidth = (clientWidth < 0) ? 0 : clientWidth;
}

Thumbnail.prototype.setCurrentWindowHeight = function(clientHeight) {
  this.currentWindowHeight =  (clientHeight < 0) ? 0 : clientHeight;
}

Thumbnail.prototype.setBaseUrl = function(url) {
  if (url.charAt(url.length - 1) == "/") {
    this.baseUrl = url.substr(0, url.length - 1)
  }
  else {
    this.baseUrl = url;
  }
}

Thumbnail.prototype.setWidth = function(width) {
  this.width = Math.floor(width);
}

Thumbnail.prototype.setHeight = function(height) {
  this.height = Math.floor(height);
}

Thumbnail.setGigapanAndSnapshotDimensions = function(gigapanThumbnail,snapshotThumbnail, widthOfUsedScreenRealEstate) {
  solutionFound = false;
  minSnapshotHeight = 50;
  maxSnapshotHeight = 200;
  snapshotWidth = 300;
  snapshotHeight = 200;
  pigeonholeIncrement = 150;

  // now compute the maximum amount of the width we can use
  widthOfUsedScreenRealEstate = (widthOfUsedScreenRealEstate < 0) ? 0 : widthOfUsedScreenRealEstate;
  maxWidthOfSnapshotPlusGigapan = gigapanThumbnail.currentWindowWidth - widthOfUsedScreenRealEstate;

  // now pigeonhole the max width to some increment of the pigeonholeIncrement value;
  maxWidthOfSnapshotPlusGigapan = maxWidthOfSnapshotPlusGigapan - (maxWidthOfSnapshotPlusGigapan % pigeonholeIncrement);

  snapshotAspectRatio = snapshotWidth / snapshotHeight;


  // first try to make the pano as big as it would like to be, and just shrink the snapshot
  if( gigapanThumbnail.gigapan.height > 0 ) {
    panoAspectRatio = gigapanThumbnail.gigapan.width / gigapanThumbnail.gigapan.height;
  }
  else {
    panoAspectRatio = 1.5;
  }
  panoHeight = maxWidthOfSnapshotPlusGigapan / (panoAspectRatio + snapshotAspectRatio);
  panoWidth = panoHeight * panoAspectRatio;
  snapshotHeight = panoHeight;
  snapshotWidth = maxWidthOfSnapshotPlusGigapan - panoWidth;

  // see if this is a viable solution
  solutionFound = (minSnapshotHeight <= snapshotHeight) && (snapshotHeight <= maxSnapshotHeight);

  // if the snapshot's height is invalid, then we need to make some adjustments to the pano's size
  if (!solutionFound) {
    // if the solution is invalid because the snapshot height is too small, then just set the snapshot height to
    // the minimum allowed height.  Otherwise, we know it's invalid because it was too big, so just constrain it
    // to be the maximum height
    snapshotHeight = (snapshotHeight < minSnapshotHeight) ? minSnapshotHeight : maxSnapshotHeight;

    // now that the know the snapshot height, we can compute the rest
    snapshotWidth = snapshotHeight * snapshotAspectRatio;
    panoHeight = snapshotHeight;
    //panoWidth = maxWidthOfSnapshotPlusGigapan - snapshotWidth;
    panoWidth = panoAspectRatio * panoHeight;

    gigapanThumbnail.setWidth(panoWidth);
    gigapanThumbnail.setHeight(panoHeight);
    snapshotThumbnail.setWidth(snapshotWidth);
    snapshotThumbnail.setHeight(snapshotHeight);
  } else {
    gigapanThumbnail.setWidth(panoWidth);
    gigapanThumbnail.setHeight(panoHeight);
    snapshotThumbnail.setWidth(snapshotWidth);
    snapshotThumbnail.setHeight(snapshotHeight);
  }        
}

Thumbnail.setGigapanDimensions = function(gigapanThumbnail, widthOfUsedScreenRealEstate) {
  solutionFound = false;
  minSnapshotHeight = 50;
  maxSnapshotHeight = 200;
  snapshotWidth = 300;
  snapshotHeight = 200;
  pigeonholeIncrement = 150;

  // now compute the maximum amount of the width we can use
  widthOfUsedScreenRealEstate = (widthOfUsedScreenRealEstate < 0) ? 0 : widthOfUsedScreenRealEstate;
  maxWidthOfSnapshotPlusGigapan = gigapanThumbnail.currentWindowWidth - widthOfUsedScreenRealEstate;

  // now pigeonhole the max width to some increment of the pigeonholeIncrement value;
  maxWidthOfSnapshotPlusGigapan = maxWidthOfSnapshotPlusGigapan - (maxWidthOfSnapshotPlusGigapan % pigeonholeIncrement);

  snapshotAspectRatio = snapshotWidth / snapshotHeight;

  // first try to make the pano as big as it would like to be, and just shrink the snapshot
  if( gigapanThumbnail.gigapan.height > 0 ) {
    panoAspectRatio = gigapanThumbnail.gigapan.width / gigapanThumbnail.gigapan.height;
  }
  else {
    panoAspectRatio = 1.5;
  }
  panoHeight = maxWidthOfSnapshotPlusGigapan / (panoAspectRatio + snapshotAspectRatio);
  panoWidth = panoHeight * panoAspectRatio;
  snapshotHeight = panoHeight;
  snapshotWidth = maxWidthOfSnapshotPlusGigapan - panoWidth;

  // see if this is a viable solution
  solutionFound = (minSnapshotHeight <= snapshotHeight) && (snapshotHeight <= maxSnapshotHeight);
  // if the snapshot's height is invalid, then we need to make some adjustments to the pano's size
  if (!solutionFound) {
    // if the solution is invalid because the snapshot height is too small, then just set the snapshot height to
    // the minimum allowed height.  Otherwise, we know it's invalid because it was too big, so just constrain it
    // to be the maximum height
    snapshotHeight = (snapshotHeight < minSnapshotHeight) ? minSnapshotHeight : maxSnapshotHeight;
    // now that the know the snapshot height, we can compute the rest
    snapshotWidth = snapshotHeight * snapshotAspectRatio;
    panoHeight = snapshotHeight;
    //panoWidth = maxWidthOfSnapshotPlusGigapan - snapshotWidth;
    panoWidth = panoAspectRatio * panoHeight;
    gigapanThumbnail.setWidth(panoWidth);
    gigapanThumbnail.setHeight(panoHeight);
  } else {
    gigapanThumbnail.setWidth(panoWidth);
    gigapanThumbnail.setHeight(panoHeight);
  }        
}

GigapanThumbnail.prototype = new Thumbnail;
GigapanThumbnail.prototype.constructor = GigapanThumbnail;
function GigapanThumbnail(gigapan) {
  Thumbnail.call(this);
  this.gigapan = gigapan;
}
GigapanThumbnail.prototype.getUrl = function () {
  if (this.gigapan.is_private) {
    return  this.baseUrl + '/gigapans/' + this.gigapan.auth_key + '-' + this.width + 'x' + this.height;
  }
  else {
    return this.baseUrl + '/gigapans/' + this.gigapan.id + '-' + this.width + 'x' + this.height;
  }
}

GigapanThumbnail.prototype.setWidthAndHeight = function(width, maxHeight) {
  height = width * (this.gigapan.height / this.gigapan.width);
  if(height > maxHeight) {
    height = maxHeight;
    width = height * (this.gigapan.width / this.gigapan.height);
  }
  this.setWidth(width);
  this.setHeight(height);
  
}



SnapshotThumbnail.prototype = new Thumbnail;
SnapshotThumbnail.prototype.constructor = new SnapshotThumbnail;
function SnapshotThumbnail(snapshot) {
  Thumbnail.call(this);
  this.snapshot = snapshot;
}
SnapshotThumbnail.prototype.getUrl = function () {
  if (this.snapshot.no_snapshot) {
    return this.baseUrl + '/images/no_snapshot';
  } else {
    if (this.snapshot.is_private) {
      return this.baseUrl + '/gigapans/' + this.snapshot.auth_key + '/snapshot/' + this.snapshot.id + '-' + this.width + 'x' + this.height;
    }
    else {
      return this.baseUrl + '/snapshots/' + this.snapshot.id + '-' + this.width + 'x' + this.height;
    }
  }

}

SnapshotThumbnail.prototype.setWidthAndHeight = function(width, maxHeight, aspectRatio) {
  height = width / aspectRatio;
  this.setWidth(width);
  this.setHeight(height);
  
}



function suggestedGigapanWidth(windowWidth, modifier, constant) { 
  if (windowWidth >= 1600) {
    suggestedWidth = 1600 * modifier - constant;
  }
  else if (windowWidth >= 1500 && windowWidth < 1600) {
    suggestedWidth = 1500 * modifier - constant;
  }
  else if (windowWidth >= 1400 && windowWidth < 1500) {
    suggestedWidth = 1400 * modifier - constant;
  }
  else if (windowWidth >= 1300 && windowWidth < 1400) {
    suggestedWidth = 1300 * modifier - constant;
  }
  else if (windowWidth >= 1200 && windowWidth < 1300) {
    suggestedWidth = 1200 * modifier - constant;
  }
  else if (windowWidth >= 1100 && windowWidth < 1200) {
    suggestedWidth = 1100 * modifier - constant;
  }
  else if (windowWidth >= 1000 && windowWidth < 1100) {
    suggestedWidth = 1000 * modifier - constant;
  }
  else if (windowWidth >= 900 && windowWidth < 1000) {
    suggestedWidth = 900 * modifier - constant;
  }
  else if (windowWidth >= 800 && windowWidth < 900) {
    suggestedWidth = 800 * modifier - constant;
  }
  else {
    suggestedWidth = 700 * modifier - constant;
  }
  return suggestedWidth;
}