//  ScrollPort - A viewport to display side-scrolling tiles
//
//    Copyright (C) 2006 by David Kaufman <david@gigawatt.com>
//
//    This module is free software; you can redistribute it and/or 
//    modify it under the terms of the GNU General Public License as 
//    published by the Free Software Foundation http://www.fsf.org
//    either version 1, or (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful, 
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See 
//    either the GNU General Public License or the Artistic License
//    for more details.  
//
//  Version 0.03 
//
//  Changes:
//
//  - Added leftControl and rightControl options (element id's)
//
//  - Added updateControls(), enableLeft(), disableLeft(), 
//    enableRight() and disableRight(), atBeginning(), atEnd(), 
//    previousIsFirst() and nextIsLast() functions to support
//    disabling leftControl and rightControl appropriately

ScrollPort = Class.create();

ScrollPort.prototype = {

  initialize: function(element, options) {
    this.notReady = true;
    var options = Object.extend({
      tileWidth:  100,
      tileHeight: 100,
      transition: Effect.Transitions.sinoidal,
      duration:   0.75,
      fps:        25.0,
      debug:      false
    }, arguments[1] || {});
    this.options = options;

    this.element = $(element);
    if(! this.element) {
      if (this.options.debug) alert('scrollPort: element not found: "' + element + '"');
      return;
    }
    this.setViewportStyle();
    
    if (this.options.leftControl) 
      this.leftControl  = $(this.options.leftControl);
    if (this.options.rightControl) 
      this.rightControl = $(this.options.rightControl);

    this.activeTiles = new Array();
    if (this.options.tilesPage) {
      if (this.options.debug) alert('using tilesPage: ' + this.options.tilesPage);
      new Ajax.Request(
        this.options.tilesPage, {
          method:       'get',
          asynchronous: true,
          onSuccess:    this.onLoadRemote.bind(this)
        }
      );
      return;
    }
    this.start();
  },

  getMore: function(moreURL) {
    if (this.options.debug) alert('getting more from: ' + moreURL);
    new Ajax.Request(
      moreURL, {
        method:       'get',
        asynchronous: true,
        onSuccess:    this.onGetMore.bind(this)
      }
    );
    return;
  },

  onGetMore: function(request) {
    if (this.options.debug) alert('got request: ' + request);
    if (request) {
      var tempElement = Builder.node('div');
      tempElement.innerHTML = request.responseText;
      Element.cleanWhitespace(tempElement)
      for (var i=0; i<tempElement.childNodes.length; i++){
        var newElement = Builder.node('div', {style: 'float: left'});
        newElement.innerHTML = tempElement.childNodes[i].innerHTML;
        this.element.appendChild(newElement);
        Element.forceRerendering(this.element);
      }
      this.activeTiles = this.element.childNodes;
      this.setActiveTileStyle();
      this.updateControls();
      this.gotMore=1;
    }
    else {
      if (this.options.debug) alert('onGetMore() got no request'); 
      return;
    }
  },

  onLoadRemote: function(request) {
    if (this.options.debug) alert('loadRemote request: ' + request);
    if (request) {
      var tempElement = Builder.node('div');
      tempElement.innerHTML = request.responseText;
      Element.cleanWhitespace(tempElement)
      for (var i=0; i<tempElement.childNodes.length; i++){
        var newElement = Builder.node('div', {style: 'float: left'});
        newElement.innerHTML = tempElement.childNodes[i].innerHTML;
        this.element.appendChild(newElement);
      }
      this.start();
    }
    else {
      if (this.options.debug) alert('onLoadRemote() received no request'); 
      return;
    }
  },

  start: function() {
    if (this.options.debug) alert('we be start-ing this: ' + this.element);
    Element.cleanWhitespace(this.element);
    if (this.element.firstChild) 
      Element.cleanWhitespace(this.element.firstChild);
    if (this.options.debug) alert('got here with nodes: ' + this.element.childNodes.length);
    this.activeTiles = this.element.childNodes;

    if (! this.activeTiles.length) {
      if (this.options.debug) alert('scrollPort: no tiles in "' 
        + ( this.options.tilesPage || element) + '"');
      return;
    }
    else {
      if (this.options.debug) alert('activeTiles: ' + this.activeTiles.length);
    }
    this.setActiveTileStyle();
    
    this.tileIndex = 0;

    this.updateControls();

    this.notReady = false;

  },

  setViewportStyle: function() {
    if (this.options.debug) alert('setting ViewportStyles...');
    Element.setStyle(
      this.element, { 
        width:  this.options.tileWidth + 'px',
        height: this.options.tileHeight + 'px',
        border: 'none'
      }
    );
    Element.makeClipping(this.element);
  },

  setActiveTileStyle: function() {
    if (this.options.debug) alert('setting activeTileStyles: ' + this.activeTiles.length);
    for (i=0; i < this.activeTiles.length; i++) {
      Element.setStyle(
        this.activeTiles[i], { 
          width:  this.options.tileWidth + 'px',
          height: this.options.tileHeight + 'px'
        }
      );
      this.activeTiles[i].makeClipping();
    } 
  },

  currentTile: function() { return this.activeTiles[this.tileIndex] },

  nextTile: function() { 
    return this.activeTiles[this.tileIndex + 1];
  },

  enableLeft: function () {
    if (this.leftControl) {
      this.leftControl.setOpacity(1);
    }
  },

  disableLeft: function () {
    if (this.leftControl) {
      this.leftControl.setOpacity(.25);
    }
  },

  enableRight: function () {
    if (this.rightControl) {
      this.rightControl.setOpacity(1);
    }
  },

  disableRight: function () {
    //return;
    if (this.rightControl) {
      this.rightControl.setOpacity(.25);
    }
  },

  updateControls: function() {
    if (this.atBeginning()) this.disableLeft();  else this.enableLeft();
    if (this.atEnd())       this.disableRight(); else this.enableRight();
  },

  atBeginning: function() {
    return this.tileIndex == 0 ? 1 : 0;
  },

  previousIsFirst: function() {
    return this.tileIndex == 1 ? 1 : 0;
  },

  atEnd: function() {
    return this.tileIndex == this.activeTiles.length - 1 ? 1 : 0;
  },

  nextIsLast: function() {
    return this.tileIndex + 1 == this.activeTiles.length - 1 ? 1 : 0;
  },

  syncOpts: function() { 
    return {
      sync       : true,
      transition : this.options.transition,
      duration   : this.options.duration,
      fps        : this.options.fps
    };
  },

  PanRight: function() {
    if (this.options.debug) alert('Panning Right...');
    if (this.notReady) return(this.options.debug ? alert('notReady') : void 0);
    if (this.options.debug) alert("I'm Ready...");
    if (this.atEnd()) {
      this.isMoving = 0;
      this.keepMoving = 0;
      return;
    }
    if (this.isMoving) {
      this.keepMoving = 1;
      return;
    }
    else {
      this.keepMoving = 0;
    }
  
    if (this.activeTiles.length - this.tileIndex < 3 ) 
      if (!this.gotMore)
        this.getMore('scrollport-data-page2.html');

    new Effect.Parallel(
      [
        new Effect.LeftRollOut(this.currentTile(), this.syncOpts()),
        new Effect.RightStretchIn(this.nextTile(), this.options.tileWidth, this.syncOpts())
      ],
      Object.extend({
        transition : this.options.transition,
        duration   : this.options.duration,
        fps        : this.options.fps,
        beforeStart: function() {
          this.isMoving=1;
          this.enableLeft();
          if (this.nextIsLast()) this.disableRight();
        }.bind(this),
        afterFinish: function(effect) {
          this.tileIndex++;
          this.isMoving=0;
          if (this.keepMoving) this.PanRight();
        }.bind(this)
      }, arguments[1] || {})
    );
  },
  
  PanLeft: function() {
    if (this.notReady) return(this.options.debug ? alert('notReady') : void 0);
    if (this.atBeginning()) {
      this.isMoving = 0;
      this.keepMoving = 0;
      return;
    }
    if (this.isMoving) {
      this.keepMoving = 1; 
      return;
    }
    else {
      this.keepMoving=0;
    }
    
    this.tileIndex--;
    new Effect.Parallel(
      [
        new Effect.LeftRollIn(this.currentTile(), this.syncOpts()),
        new Effect.RightSqueezeOut(this.nextTile(), this.options.tileWidth, this.syncOpts())
      ],
      Object.extend({
        transition : this.options.transition,
        duration   : this.options.duration,
        fps        : this.options.fps,
        beforeStart: function() {
          this.isMoving=1;
          this.enableRight();
          if (this.atBeginning()) this.disableLeft();
        }.bind(this),
        afterFinish: function(effect) {
          this.isMoving=0;
          if (this.keepMoving) this.PanLeft();
        }.bind(this)
      }, arguments[1] || {})
    );
  }

};

Effect.LeftRollOut = Class.create();
Object.extend(
  Object.extend(Effect.LeftRollOut.prototype, Effect.Base.prototype), {
    initialize: function(element) {
      this.element = $(element);
      var options = Object.extend({
        tileWidth: parseFloat(Element.getStyle(this.element, 'width'))
      }, arguments[1] || {});
      this.start(options);
    },
    update: function(position) {
      var negativeWidth = this.options.tileWidth * -1;
      var hiddenPortion = position * negativeWidth;
      Element.setStyle(
        this.element, { marginLeft: hiddenPortion + 'px'  }
      );
    }
  }
);

Effect.LeftRollIn = Class.create();
Object.extend(
  Object.extend(Effect.LeftRollIn.prototype, Effect.Base.prototype), {
    initialize: function(element) {
      this.element = $(element);
      var options = Object.extend({
        tileWidth: parseFloat(Element.getStyle(this.element, 'width'))
      }, arguments[1] || {});
      this.start(options);
    },
    update: function(position) {
      var maxOffset = this.options.tileWidth * -1;
      var newMargin = (1 - position) * maxOffset;
      Element.setStyle(
        this.element, { marginLeft: newMargin + 'px'  }
      );
    }
  }
);

Effect.RightStretchIn = function(element, width) {
  element = $(element);
  return new Effect.Scale(element, 100, 
    Object.extend({ 
      scaleFrom: 0, // percent
      scaleTo: 100,
      scaleY: false,
      scaleContent: false,
      scaleMode: {originalWidth: width} // pixels
    }, arguments[2] || {})
  );
}

Effect.RightSqueezeOut = function(element, width) {
  element = $(element);
  return new Effect.Scale(element, 0, 
    Object.extend({ 
      scaleFrom: 100, // percent
      scaleTo: 0,
      scaleY: false,
      scaleContent: false,
      scaleMode: {originalWidth: width} // pixels
    }, arguments[2] || {})
  );
}


