(function($) {
    var options = {
        touch : {
            pan : 'xy',
            scale : 'xy',
            autoWidth : false,
            autoHeight : false
        }
    };

    function init(plot) {
        var isPanning = false;
        var isZooming = false;
        var lastTouchPosition = {
            x : -1,
            y : -1
        };
        var lastTouchDistance = 0;
        var relativeOffset = {
            x : 0,
            y : 0
        };
        var relativeScale = 1.0;
        var scaleOrigin = {
            x : 50,
            y : 50
        };

        function pan(delta) {
            var placeholder = plot.getPlaceholder();
            var options = plot.getOptions();

            relativeOffset.x -= delta.x;
            relativeOffset.y -= delta.y;

            switch (options.touch.pan.toLowerCase()) {
                case 'x':
                    placeholder.children('div.flot-touch-container').css('-webkit-transform', 'translateX(' + relativeOffset.x + 'px)');
                    break;
                case 'y':
                    placeholder.children('div.flot-touch-container').css('-webkit-transform', 'translateY(' + relativeOffset.y + 'px)');
                    break;
                default:
                    placeholder.children('div.flot-touch-container').css('-webkit-transform', 'translate(' + relativeOffset.x + 'px,' + relativeOffset.y + 'px)');
                    break;
            }
        }

        function scale(delta) {
            var placeholder = plot.getPlaceholder();
            var options = plot.getOptions();
            var container = placeholder.children('div.flot-touch-container');

            relativeScale *= 1 + (delta / 100);

            switch (options.touch.scale.toLowerCase()) {
                case 'x':
                    container.css('-webkit-transform', 'scaleX(' + relativeScale + ')');
                    break;
                case 'y':
                    container.css('-webkit-transform', 'scaleY(' + relativeScale + ')');
                    break;
                default:
                    container.css('-webkit-transform', 'scale(' + relativeScale + ')');
                    break;
            }
        }

        function processOptions(plot, options) {
            if (!options.touch) {
                // only do something when touch options are set
                return;
            }

            var placeholder = plot.getPlaceholder();

            if (options.touch.autoWidth) {
                placeholder.css('width', '100%');
            }

            if (options.touch.autoHeight) {
                var placeholderParent = placeholder.parent();
                var height = 0;

                placeholderParent.siblings().each(function() {
                    height -= $(this).outerHeight();
                });

                height -= parseInt(placeholderParent.css('padding-top'), 10);
                height -= parseInt(placeholderParent.css('padding-bottom'), 10);
                height += window.innerHeight;

                placeholder.css('height', (height <= 0) ? 100 : height + 'px');
            }

            plot.hooks.bindEvents.push(bindEvents);
            plot.hooks.shutdown.push(shutdown);
        }

        function bindEvents(plot, eventHolder) {
            var placeholder = plot.getPlaceholder();
            var container = $('<div class="flot-touch-container" style="background:#fff;"/>');

            placeholder.css({
                'background' : '#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////mpqaPjL2kgAAABdJREFUeNpiYIQCBhgYIIEBth4mABBgAEUQAIEfdL0YAAAAAElFTkSuQmCC) repeat',
                'overflow' : 'hidden'
            }).children('canvas').wrapAll(container);

            placeholder.bind('touchstart', function(evt) {
                var touches = evt.originalEvent.touches;
                var container = placeholder.children('div.flot-touch-container');

                if (touches.length === 1) {
                    isPanning = true;
                    lastTouchPosition = {
                        x : touches[0].pageX,
                        y : touches[0].pageY
                    };
                    lastTouchDistance = 0;
                }
                else if (touches.length === 2) {
                    isZooming = true;
                    lastTouchPosition = {
                        x : (touches[0].pageX + touches[1].pageX) / 2,
                        y : (touches[0].pageY + touches[1].pageY) / 2
                    };
                    lastTouchDistance = Math.sqrt(Math.pow(touches[1].pageX - touches[0].pageX, 2) + Math.pow(touches[1].pageY - touches[0].pageY, 2));
                }

                var offset = placeholder.offset();
                var rect = {
                    x : offset.left,
                    y : offset.top,
                    width : placeholder.width(),
                    height : placeholder.height()
                };

                var normalizedTouchPosition = {
                    x : lastTouchPosition.x,
                    y : lastTouchPosition.y
                };

                if (normalizedTouchPosition.x < rect.x) {
                    normalizedTouchPosition.x = rect.x;
                }
                else if (normalizedTouchPosition.x > rect.x + rect.width) {
                    normalizedTouchPosition.x = rect.x + rect.width;
                }

                if (normalizedTouchPosition.y < rect.y) {
                    normalizedTouchPosition.y = rect.y;
                }
                else if (normalizedTouchPosition.y > rect.y + rect.height) {
                    normalizedTouchPosition.y = rect.y + rect.height;
                }

                scaleOrigin = {
                    x : Math.round((normalizedTouchPosition.x / rect.width) * 100),
                    y : Math.round((normalizedTouchPosition.y / rect.height) * 100)
                };

                container.css('-webkit-transform-origin', scaleOrigin.x + '% ' + scaleOrigin.y + '%');

                // Return false to prevent touch scrolling.
                return false;
            });
            // /placeholder.bind

            placeholder.bind('touchmove', function(evt) {
                var touches = evt.originalEvent.touches;
                var position, distance, delta;

                if (isPanning && touches.length === 1) {
                    position = {
                        x : touches[0].pageX,
                        y : touches[0].pageY
                    };
                    delta = {
                        x : lastTouchPosition.x - position.x,
                        y : lastTouchPosition.y - position.y
                    };

                    // Transform via the delta
                    pan(delta);

                    lastTouchPosition = position;
                    lastTouchDistance = 0;
                }
                else if (isZooming && touches.length === 2) {
                    distance = Math.sqrt(Math.pow(touches[1].pageX - touches[0].pageX, 2) + Math.pow(touches[1].pageY - touches[0].pageY, 2));
                    position = {
                        x : (touches[0].pageX + touches[1].pageX) / 2,
                        y : (touches[0].pageY + touches[1].pageY) / 2
                    };
                    delta = distance - lastTouchDistance;

                    // Scale via the delta
                    scale(delta);

                    lastTouchPosition = position;
                    lastTouchDistance = distance;
                }
            });
            // /placeholder.bind

            placeholder.bind('touchend', function(evt) {
                var placeholder = plot.getPlaceholder();
                var options = plot.getOptions();
                var container = placeholder.children('div.flot-touch-container');

                // Apply the pan.
                if (relativeOffset.x !== 0 || relativeOffset.y !== 0) {
                    $.each(plot.getAxes(), function(index, axis) {
                        if (axis.direction === options.touch.pan.toLowerCase() || options.touch.pan.toLowerCase() == 'xy') {
                            var min = axis.c2p(axis.p2c(axis.min) - relativeOffset[axis.direction]);
                            var max = axis.c2p(axis.p2c(axis.max) - relativeOffset[axis.direction]);

                            axis.options.min = min;
                            axis.options.max = max;
                        }
                    });
                }

                // Apply the scale.
                if (relativeScale !== 1.0) {
                    var width = plot.width();
                    var height = plot.height();
                    var scaleOriginPixel = {
                        x : Math.round((scaleOrigin.x / 100) * width),
                        y : Math.round((scaleOrigin.y / 100) * height)
                    };
                    var range = {
                        x : {
                            min : scaleOriginPixel.x - (scaleOrigin.x / 100) * width / relativeScale,
                            max : scaleOriginPixel.x + (1 - (scaleOrigin.x / 100)) * width / relativeScale
                        },
                        y : {
                            min : scaleOriginPixel.y - (scaleOrigin.y / 100) * height / relativeScale,
                            max : scaleOriginPixel.y + (1 - (scaleOrigin.y / 100)) * height / relativeScale
                        }
                    };

                    $.each(plot.getAxes(), function(index, axis) {
                        if (axis.direction === options.touch.scale.toLowerCase() || options.touch.scale.toLowerCase() == 'xy') {
                            var min = axis.c2p(range[axis.direction].min);
                            var max = axis.c2p(range[axis.direction].max);

                            if (min > max) {
                                var temp = min;
                                min = max;
                                max = temp;
                            }

                            axis.options.min = min;
                            axis.options.max = max;
                        }
                    });
                }

                plot.setupGrid();
                plot.draw();

                isPanning = false;
                isZooming = false;
                lastTouchPosition = {
                    x : -1,
                    y : -1
                };
                lastTouchDistance = 0;
                relativeOffset = {
                    x : 0,
                    y : 0
                };
                relativeScale = 1.0;
                scaleOrigin = {
                    x : 50,
                    y : 50
                };

                container.css({
                    '-webkit-transform' : 'translate(' + relativeOffset.x + 'px,' + relativeOffset.y + 'px) scale(' + relativeScale + ')',
                    '-webkit-transform-origin' : scaleOrigin.x + '% ' + scaleOrigin.y + '%'
                });
            });
            // /placeholder.bind
        } // /bindEvents

        function shutdown(plot, eventHolder) {
            var placeholder = plot.getPlaceholder();

            placeholder.unbind('touchstart').unbind('touchmove').unbind('touchend');
        }

        plot.hooks.processOptions.push(processOptions);
    } // /init

    $.plot.plugins.push({
        init : init,
        options : options,
        name : 'touch',
        version : '1.0'
    });
})(jQuery);
