﻿using System;
using System.Windows.Browser;
using System.Windows.Input;
using System.Windows;
using System.Diagnostics;

namespace SampleWheel
{
    // This helper class came from Peter Blois.

    public class MouseWheelEventArgs : EventArgs
    {
        private double delta;
        private bool handled = false;
        private MouseEventArgs _lastMouseEventArgs = null;

        public MouseWheelEventArgs(double delta)
        {
            this.delta = delta;
        }

        public double Delta
        {
            get { return this.delta; }
        }

        public bool Handled
        {
            get { return this.handled; }
            set { this.handled = value; }
        }

        public void SetLastMouseEventArgs(MouseEventArgs value)
        {
            _lastMouseEventArgs = value;
        }

        public Point GetPosition(UIElement uiElement)
        {
            Debug.Assert(_lastMouseEventArgs != null, "Should only get called when _lastMouseEventArgs is set to something");
            return _lastMouseEventArgs.GetPosition(uiElement);
        }
    }

    public class MouseWheelHelper
    {

        public event EventHandler<MouseWheelEventArgs> Moved;
        private static Worker worker;
        private bool isMouseOver = false;
        private MouseEventArgs _latestMouseEventArgs = null;

        public MouseWheelHelper(FrameworkElement element, string controlID)
        {

            if (MouseWheelHelper.worker == null)
                MouseWheelHelper.worker = new Worker(controlID);

            MouseWheelHelper.worker.Moved += this.HandleMouseWheel;

            element.MouseEnter += this.HandleMouseEnter;
            element.MouseLeave += this.HandleMouseLeave;
            element.MouseMove += this.HandleMouseMove;
        }

        private void HandleMouseWheel(object sender, MouseWheelEventArgs args)
        {
            if (this.isMouseOver)
            {
                args.SetLastMouseEventArgs(_latestMouseEventArgs);
                this.Moved(this, args);
            }
        }

        private void HandleMouseEnter(object sender, MouseEventArgs e)
        {
            _latestMouseEventArgs = e;
            this.isMouseOver = true;
        }

        private void HandleMouseLeave(object sender, EventArgs e)
        {
            _latestMouseEventArgs = null;
            this.isMouseOver = false;
        }

        private void HandleMouseMove(object sender, MouseEventArgs e)
        {
            _latestMouseEventArgs = e;
            this.isMouseOver = true;
        }

        [Scriptable]
        private class Worker
        {

            public event EventHandler<MouseWheelEventArgs> Moved;

            public Worker(string controlName)
            {
                WebApplication.Current.RegisterScriptableObject("MouseWheelHandler", this);

                HtmlElement scriptBlock = HtmlPage.Document.CreateElement("script");
                scriptBlock.SetProperty("type", @"text/javascript");
                string javascript = this.MouseWheelScriptText.Replace("SilverlightControlName", controlName);
                scriptBlock.SetProperty("text", javascript);
                HtmlElementCollection bodyElements = HtmlPage.Document.GetElementsByTagName("body");


                if (bodyElements.Count > 0)
                {
                    bodyElements[0].AppendChild(scriptBlock);
                }
            }

            [Scriptable]
            public int HandleMouseWheel(float delta)
            {
                int handled = 0;
                if (this.Moved != null)
                {
                    MouseWheelEventArgs args = new MouseWheelEventArgs(delta);
                    this.Moved(this, args);
                    handled = args.Handled ? 1 : 0;
                }
                return handled;
            }

            // I couldn't convert this code to managed at this time, so instead I'm
            // keeping it browser JS and injecting it, then having it call back to the
            // managed code.

            // This code originated from: http://adomas.org/javascript-mouse-wheel/
            // with some additional tweaks for Safari.
            private string MouseWheelScriptText = @"
handleMouseWheel = function(event) {
    var delta = 0;
    if (!event) // For IE.
        event = window.event;
        
    if (event.wheelDelta) { //IE/Opera.
        delta = event.wheelDelta/120;
        // In Opera 9, delta differs in sign as compared to IE.
        if (window.opera)
        {
            delta = -delta;
        }
            
    }
    else if (event.detail) { // Mozilla case.
        // In Mozilla, sign of delta is different than in IE.
        // Also, delta is multiple of 3.
        delta = -event.detail/3;
        //Sign is only reversed in windows FF
        if(navigator.userAgent.indexOf('Macintosh') != -1)
            delta=-delta;
    }

    // If delta is nonzero, handle it.
    // Basically, delta is now positive if wheel was scrolled up,
    // and negative, if wheel was scrolled down.
	var consume = 0
    if (delta) {
		consume = document.getElementById('SilverlightControlName').content.MouseWheelHandler.HandleMouseWheel(delta);
	}

    // Prevent default actions caused by mouse wheel.
    // That might be ugly, but we handle scrolls somehow
    // anyway, so don't bother here..
	if (consume == 1) {
		if (event.preventDefault)
			event.preventDefault();
	        
		event.returnValue = false;
	}
}

var wheelHandler = handleMouseWheel;
if (window.addEventListener) {
	// DOMMouseScroll is for mozilla.
	window.addEventListener('DOMMouseScroll', wheelHandler, false);
}
window.onmousewheel = document.onmousewheel = wheelHandler;
";
        }

    }
}
