/*
 * Copyright  (c) 2009 Bambit Technologies, Inc
 * jQuery PanZoom Plugin
 * http://www.bambitinc.com/Projects/jQuery-PanZoom/
 *
 * Licensed under the MIT license
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*  ------------------------ 	HISTORY      ------------------------
* Version 0.1 -- 2009.08.19
*	Base implementation
*/

; (function($) {
    jQuery.log = function(message) {
        if (window.console) {
            console.debug(message);
        }
    };
    function Zoom(surface, zoom) {
        var settings = surface.data('settings');
        var newZoom = settings.currentZoom + zoom;
        if (newZoom < 0 || newZoom >= settings.zoomLevels) {
            $.log("Can't zoom to " + newZoom+' (Min/Max: 0/'+settings.zoomLevels+')');
            return;
        }
        var scale = Math.pow(settings.zoomStep, zoom);
        var cl = surface.position().left - settings.containerWidth / 2;
        var ct = surface.position().top - settings.containerHeight / 2;
        cl = Math.round(settings.containerWidth / 2 + cl / scale);
        ct = Math.round(settings.containerHeight / 2 + ct / scale);
        $.log('Zooming to ' + newZoom);
        settings.currentZoom = newZoom;
        settings.fullWidth = settings.width / Math.pow(settings.zoomStep, settings.currentZoom);
        settings.fullHeight = settings.height / Math.pow(settings.zoomStep, settings.currentZoom);
        settings.totalColumns = Math.ceil(settings.fullWidth / settings.cellWidth);
        settings.totalRows = Math.ceil(settings.fullHeight / settings.cellHeight);
        surface.css('left', cl);
        surface.css('top', ct);
        AdjustCells(surface, true);
    }

    function AdjustCells(surface, force) {
        var cp = surface.position();
        UpdateStatus(surface, 'Position: (' + cp.left + ',' + cp.top + ')');
        var settings = surface.data('settings');
        var cells = surface.data('cells');
        var minl = -cp.left - settings.cellWidth;
        var mint = -cp.top - settings.cellHeight;
        var maxl = -cp.left + settings.containerWidth + settings.cellWidth;
        var maxt = -cp.top + settings.containerHeight + settings.cellHeight;
        var len = cells.length;
        var numChanged = 0;
        for (var i = 0; i < len; i++) {
            var changed = false || force;
            var cellPos = $(cells[i].img).position();
            var nl = cellPos.left;
            var nt = cellPos.top;
            var o;
            var safety;
            if (cellPos.left < minl) {
                safety = 0;
                do {
                    o = nl;
                    nl = nl + settings.numberColumns * settings.cellWidth;
                    safety++;
                } while (nl < minl && safety < 100);
                changed = true;

            } else if (cellPos.left > maxl) {
                safety = 0;
                do {
                    o = nl;
                    nl = nl - settings.numberColumns * settings.cellWidth;
                    safety++;
                } while (nl > maxl && safety < 100);
                changed = true;

            } if (cellPos.top < mint) {
                safety = 0;
                do {
                    o = nt;
                    nt = nt + settings.numberRows * settings.cellHeight;
                    safety++;
                } while (nt < mint && safety < 100);
                changed = true;
            } else if (cellPos.top > maxt) {
                safety = 0;
                do {
                    o = nt;
                    nt = nt - settings.numberRows * settings.cellHeight;
                    safety++;
                } while (nt > maxt && safety < 100);
                changed = true;
            }
            if (nl >= 0 && nt >= 0) {
                // NOTE: I call round here as it occasionally slips off the integer
                var expX = Math.round(nl / settings.cellWidth);
                var expY = Math.round(nt / settings.cellHeight);
                if (expX != cells[i].x || expY != cells[i].y) {
                    cells[i].x = expX;
                    cells[i].y = expY;
                    changed = true;
                }
            } else {
                cells[i].x = cells[i].y = -1;
            }

            if (changed === true) {
                SetCell(cells[i], settings);
                $(cells[i].img).css('left', nl);
                $(cells[i].img).css('top', nt);
                numChanged++;
            }
        }
        $.log("There were " + numChanged + " cells changed");
    }

    function SetCell(cell, settings) {
        var zoomLevel = settings.currentZoom;
        if (
				cell.x < 0 ||
				cell.y < 0 ||
				cell.x >= settings.totalColumns ||
				cell.y >= settings.totalRows
			) {
            cell.img.src = 'empty.png';
        } else {
            cell.img.src = settings.imgRootPath + zoomLevel + '/' + cell.x + 'x' + cell.y + '.jpg';
        }
    }


    function UpdateStatus(surface, status) {
        var settings = surface.data('settings');
        if (settings.status !== null) {
            settings.status.html('<span style="pzStatusUpdate">' + status + '</span>');
        }
    }

    function GetSurface(obj) {
        return $(obj).children(".pzSurface");
    }

    $.fn.Zoom = function(level) {
        return this.each(function() {
            var surface = GetSurface(this);
            Zoom(surface, level);
        });
    }

    jQuery.fn.panZoom = function(options) {

        return this.each(function() {
            var settings = jQuery.extend({
                imgRootPath: '',
                zoomLevels: 1,
                width: 1024,
                height: 1024,
                cellWidth: 256,
                cellHeight: 256,
                currentZoom: 0,
                zoomStep: 2,
                zoomOnMouse: false,
                zoomInControl: null,
                zoomOutControl: null,
                status: null
            }, options);
            $(this).css("overflow", "hidden");
            // Create the  divs
            settings.numberColumns = Math.ceil($(this).width() / settings.cellWidth) + 2;
            settings.numberRows = Math.ceil($(this).height() / settings.cellHeight) + 2;
            //	Create the cells
            var cells = [];
            $(this).append('<div class="pzSurface" style="position:relative"></div>');
            $(this).append('<div class="pzGrip"></div>');
            //	grab the surface object
            var surface = GetSurface(this); // $(this).children(".pzSurface");
            var grip = $(this).children(".pzGrip");
            grip.css('width', '1024px');
            grip.css('height', '1024px');
            settings.containerHeight = $(this).height();
            settings.containerWidth = $(this).width();
            settings.fullWidth = settings.width / Math.pow(settings.zoomStep, settings.currentZoom);
            settings.fullHeight = settings.height / Math.pow(settings.zoomStep, settings.currentZoom);
            settings.totalColumns = Math.ceil(settings.fullWidth / settings.cellWidth);
            settings.totalRows = Math.ceil(settings.fullHeight / settings.cellHeight);
            for (var y = 0; y < settings.numberRows; y++) {
                for (var x = 0; x < settings.numberColumns; x++) {
                    var left = (x - 1) * settings.cellWidth;
                    var top = (y - 1) * settings.cellHeight;
                    surface.append('<img style="position:absolute; left: ' + left + 'px; top:' + top + 'px;width:' + settings.cellWidth + ';height:' + settings.cellHeight + ';" />\n');
                    var children = surface.children('img');
                    var img = children[children.length - 1];
                    var cell = { 'x': x, 'y': y, 'left': left, 'top': top, 'img': img };
                    SetCell(cell, settings);
                    cells.push(cell);
                }
            }
            surface.data('cells', cells);
            surface.data('settings', settings);
            if (settings.zoomOnMouse === true) {
                $.log('Zooming on mouse');
                $(this).mousewheel(function(event, delta) {
                    var surface = $(this).children(".pzSurface");
                    if (delta < 0) {
                        Zoom($(surface), 1);
                    } else if (delta > 0) {
                        Zoom($(surface), -1);
                    }
                });
            }
            if (settings.zoomInControl !== null) {
                $(settings.zoomInControl).data('surface', surface);
                $(settings.zoomInControl).click(function() {
                    Zoom($(this).data('surface'), 1);
                });
            }
            if (settings.zoomOutControl !== null) {
                $(settings.zoomOutControl).data('surface', surface);
                $(settings.zoomOutControl).click(function() {
                    Zoom($(this).data('surface'), -1);
                });
            }
            if (settings.status !== null) {
                settings.status = $(settings.status);
            }
            $(grip).draggable(
				{
				    revert: true,
				    revertDuration: 1,
				    //helper:'clone'
				    start: function(event, ui) {
				        var surface = $(this).siblings('.pzSurface')[0];
				        $(surface).data('startPosition', $(surface).position());

				    },
				    stop: function(event, ui) {
				        var surface = $(this).siblings('.pzSurface')[0];
				        AdjustCells($(surface));
				    },
				    drag: function(event, ui) {
				        //	Get the surface
				        var surface = $(this).siblings('.pzSurface')[0];
				        var cp = $(surface).data('startPosition');
				        var nl = cp.left + ui.position.left;
				        var nt = cp.top + ui.position.top;
				        $(surface).css('left', nl + 'px');
				        $(surface).css('top', nt + 'px');


				    }
				}
			);

        });
    };
})(jQuery);

