using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using Xunit;
using Xunit.Abstractions;

namespace Terminal.Gui.ViewTests {
	public class ScrollBarViewTests {
		readonly ITestOutputHelper output;

		public ScrollBarViewTests (ITestOutputHelper output)
		{
			this.output = output;
		}

		// This class enables test functions annotated with the [InitShutdown] attribute
		// to have a function called before the test function is called and after.
		// 
		// This is necessary because a) Application is a singleton and Init/Shutdown must be called
		// as a pair, and b) all unit test functions should be atomic.
		[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
		public class ScrollBarAutoInitShutdownAttribute : AutoInitShutdownAttribute {

			public override void Before (MethodInfo methodUnderTest)
			{
				base.Before (methodUnderTest);

				ScrollBarViewTests._hostView = new HostView () {
					Width = Dim.Fill (),
					Height = Dim.Fill (),
					Top = 0,
					Lines = 30,
					Left = 0,
					Cols = 100
				};

				Application.Top.Add (ScrollBarViewTests._hostView);
			}

			public override void After (MethodInfo methodUnderTest)
			{
				ScrollBarViewTests._hostView = null;
				base.After (methodUnderTest);
			}
		}

		public class HostView : View {
			public int Top { get; set; }
			public int Lines { get; set; }
			public int Left { get; set; }
			public int Cols { get; set; }
		}

		private static HostView _hostView;
		private ScrollBarView _scrollBar;
		private bool _added;

		private void AddHandlers ()
		{
			if (!_added) {
				_hostView.DrawContent += _hostView_DrawContent;
				_scrollBar.ChangedPosition += _scrollBar_ChangedPosition;
				_scrollBar.OtherScrollBarView.ChangedPosition += _scrollBar_OtherScrollBarView_ChangedPosition;
			}
			_added = true;
		}

		private void RemoveHandlers ()
		{
			if (_added) {
				_hostView.DrawContent -= _hostView_DrawContent;
				_scrollBar.ChangedPosition -= _scrollBar_ChangedPosition;
				_scrollBar.OtherScrollBarView.ChangedPosition -= _scrollBar_OtherScrollBarView_ChangedPosition;
			}
			_added = false;
		}

		private void _hostView_DrawContent (Rect obj)
		{
			_scrollBar.Size = _hostView.Lines;
			_scrollBar.Position = _hostView.Top;
			_scrollBar.OtherScrollBarView.Size = _hostView.Cols;
			_scrollBar.OtherScrollBarView.Position = _hostView.Left;
			_scrollBar.Refresh ();
		}

		private void _scrollBar_ChangedPosition ()
		{
			_hostView.Top = _scrollBar.Position;
			if (_hostView.Top != _scrollBar.Position) {
				_scrollBar.Position = _hostView.Top;
			}
			_hostView.SetNeedsDisplay ();
		}

		private void _scrollBar_OtherScrollBarView_ChangedPosition ()
		{
			_hostView.Left = _scrollBar.OtherScrollBarView.Position;
			if (_hostView.Left != _scrollBar.OtherScrollBarView.Position) {
				_scrollBar.OtherScrollBarView.Position = _hostView.Left;
			}
			_hostView.SetNeedsDisplay ();
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void Hosting_A_Null_View_To_A_ScrollBarView_Throws_ArgumentNullException ()
		{
			Assert.Throws<ArgumentNullException> ("The host parameter can't be null.",
				() => new ScrollBarView (null, true));
			Assert.Throws<ArgumentNullException> ("The host parameter can't be null.",
				() => new ScrollBarView (null, false));
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void Hosting_A_Null_SuperView_View_To_A_ScrollBarView_Throws_ArgumentNullException ()
		{
			Assert.Throws<ArgumentNullException> ("The host SuperView parameter can't be null.",
				() => new ScrollBarView (new View (), true));
			Assert.Throws<ArgumentNullException> ("The host SuperView parameter can't be null.",
				() => new ScrollBarView (new View (), false));
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void Hosting_Two_Vertical_ScrollBarView_Throws_ArgumentException ()
		{
			var top = new Toplevel ();
			var host = new View ();
			top.Add (host);
			var v = new ScrollBarView (host, true);
			var h = new ScrollBarView (host, true);

			Assert.Throws<ArgumentException> (() => v.OtherScrollBarView = h);
			Assert.Throws<ArgumentException> (() => h.OtherScrollBarView = v);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void Hosting_Two_Horizontal_ScrollBarView_Throws_ArgumentException ()
		{
			var top = new Toplevel ();
			var host = new View ();
			top.Add (host);
			var v = new ScrollBarView (host, false);
			var h = new ScrollBarView (host, false);

			Assert.Throws<ArgumentException> (() => v.OtherScrollBarView = h);
			Assert.Throws<ArgumentException> (() => h.OtherScrollBarView = v);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void Scrolling_With_Default_Constructor_Do_Not_Scroll ()
		{
			var sbv = new ScrollBarView {
				Position = 1
			};
			Assert.NotEqual (1, sbv.Position);
			Assert.Equal (0, sbv.Position);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void Hosting_A_View_To_A_ScrollBarView ()
		{
			RemoveHandlers ();

			_scrollBar = new ScrollBarView (_hostView, true);

			Assert.True (_scrollBar.IsVertical);
			Assert.False (_scrollBar.OtherScrollBarView.IsVertical);

			Assert.Equal (_scrollBar.Position, _hostView.Top);
			Assert.NotEqual (_scrollBar.Size, _hostView.Lines);
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
			Assert.NotEqual (_scrollBar.OtherScrollBarView.Size, _hostView.Cols);

			AddHandlers ();
			_hostView.SuperView.LayoutSubviews ();
			_hostView.Redraw (_hostView.Bounds);

			Assert.Equal (_scrollBar.Position, _hostView.Top);
			Assert.Equal (_scrollBar.Size, _hostView.Lines);
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
			Assert.Equal (_scrollBar.OtherScrollBarView.Size, _hostView.Cols);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void ChangedPosition_Update_The_Hosted_View ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			_scrollBar.Position = 2;
			Assert.Equal (_scrollBar.Position, _hostView.Top);

			_scrollBar.OtherScrollBarView.Position = 5;
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void ChangedPosition_Scrolling ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			for (int i = 0; i < _scrollBar.Size; i++) {
				_scrollBar.Position += 1;
				Assert.Equal (_scrollBar.Position, _hostView.Top);
			}
			for (int i = _scrollBar.Size - 1; i >= 0; i--) {
				_scrollBar.Position -= 1;
				Assert.Equal (_scrollBar.Position, _hostView.Top);
			}

			for (int i = 0; i < _scrollBar.OtherScrollBarView.Size; i++) {
				_scrollBar.OtherScrollBarView.Position += i;
				Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
			}
			for (int i = _scrollBar.OtherScrollBarView.Size - 1; i >= 0; i--) {
				_scrollBar.OtherScrollBarView.Position -= 1;
				Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
			}
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void ChangedPosition_Negative_Value ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			_scrollBar.Position = -20;
			Assert.Equal (0, _scrollBar.Position);
			Assert.Equal (_scrollBar.Position, _hostView.Top);

			_scrollBar.OtherScrollBarView.Position = -50;
			Assert.Equal (0, _scrollBar.OtherScrollBarView.Position);
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void DrawContent_Update_The_ScrollBarView_Position ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			_hostView.Top = 3;
			_hostView.Redraw (_hostView.Bounds);
			Assert.Equal (_scrollBar.Position, _hostView.Top);

			_hostView.Left = 6;
			_hostView.Redraw (_hostView.Bounds);
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void OtherScrollBarView_Not_Null ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			Assert.NotNull (_scrollBar.OtherScrollBarView);
			Assert.NotEqual (_scrollBar, _scrollBar.OtherScrollBarView);
			Assert.Equal (_scrollBar.OtherScrollBarView.OtherScrollBarView, _scrollBar);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void ShowScrollIndicator_Check ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			Assert.True (_scrollBar.ShowScrollIndicator);
			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void KeepContentAlwaysInViewport_True ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			Assert.Equal (80, _hostView.Bounds.Width);
			Assert.Equal (25, _hostView.Bounds.Height);
			Assert.Equal (79, _scrollBar.OtherScrollBarView.Bounds.Width);
			Assert.Equal (24, _scrollBar.Bounds.Height);
			Assert.Equal (30, _scrollBar.Size);
			Assert.Equal (100, _scrollBar.OtherScrollBarView.Size);
			Assert.True (_scrollBar.ShowScrollIndicator);
			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (_scrollBar.Visible);
			Assert.True (_scrollBar.OtherScrollBarView.Visible);

			_scrollBar.Position = 50;
			Assert.Equal (_scrollBar.Position, _scrollBar.Size - _scrollBar.Bounds.Height);
			Assert.Equal (_scrollBar.Position, _hostView.Top);
			Assert.Equal (6, _scrollBar.Position);
			Assert.Equal (6, _hostView.Top);
			Assert.True (_scrollBar.ShowScrollIndicator);
			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (_scrollBar.Visible);
			Assert.True (_scrollBar.OtherScrollBarView.Visible);

			_scrollBar.OtherScrollBarView.Position = 150;
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _scrollBar.OtherScrollBarView.Size - _scrollBar.OtherScrollBarView.Bounds.Width);
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
			Assert.Equal (21, _scrollBar.OtherScrollBarView.Position);
			Assert.Equal (21, _hostView.Left);
			Assert.True (_scrollBar.ShowScrollIndicator);
			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (_scrollBar.Visible);
			Assert.True (_scrollBar.OtherScrollBarView.Visible);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void KeepContentAlwaysInViewport_False ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			_scrollBar.KeepContentAlwaysInViewport = false;
			_scrollBar.Position = 50;
			Assert.Equal (_scrollBar.Position, _scrollBar.Size - 1);
			Assert.Equal (_scrollBar.Position, _hostView.Top);
			Assert.Equal (29, _scrollBar.Position);
			Assert.Equal (29, _hostView.Top);

			_scrollBar.OtherScrollBarView.Position = 150;
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _scrollBar.OtherScrollBarView.Size - 1);
			Assert.Equal (_scrollBar.OtherScrollBarView.Position, _hostView.Left);
			Assert.Equal (99, _scrollBar.OtherScrollBarView.Position);
			Assert.Equal (99, _hostView.Left);
		}

		[Fact]
		[ScrollBarAutoInitShutdown]
		public void AutoHideScrollBars_Check ()
		{
			Hosting_A_View_To_A_ScrollBarView ();

			AddHandlers ();

			_hostView.Redraw (_hostView.Bounds);
			Assert.True (_scrollBar.ShowScrollIndicator);
			Assert.True (_scrollBar.Visible);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.Width.ToString ());
			Assert.Equal (1, _scrollBar.Bounds.Width);
			Assert.Equal ("Dim.Combine(DimView(side=Height, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(1))",
				_scrollBar.Height.ToString ());
			Assert.Equal (24, _scrollBar.Bounds.Height);
			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (_scrollBar.OtherScrollBarView.Visible);
			Assert.Equal ("Dim.Combine(DimView(side=Width, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(1))",
				_scrollBar.OtherScrollBarView.Width.ToString ());
			Assert.Equal (79, _scrollBar.OtherScrollBarView.Bounds.Width);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
			Assert.Equal (1, _scrollBar.OtherScrollBarView.Bounds.Height);

			_hostView.Lines = 10;
			_hostView.Redraw (_hostView.Bounds);
			Assert.False (_scrollBar.ShowScrollIndicator);
			Assert.False (_scrollBar.Visible);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.Width.ToString ());
			Assert.Equal (1, _scrollBar.Bounds.Width);
			Assert.Equal ("Dim.Combine(DimView(side=Height, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(1))",
				_scrollBar.Height.ToString ());
			Assert.Equal (24, _scrollBar.Bounds.Height);
			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (_scrollBar.OtherScrollBarView.Visible);
			Assert.Equal ("Dim.Combine(DimView(side=Width, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(0))",
				_scrollBar.OtherScrollBarView.Width.ToString ());
			Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
			Assert.Equal (1, _scrollBar.OtherScrollBarView.Bounds.Height);

			_hostView.Cols = 60;
			_hostView.Redraw (_hostView.Bounds);
			Assert.False (_scrollBar.ShowScrollIndicator);
			Assert.False (_scrollBar.Visible);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.Width.ToString ());
			Assert.Equal (1, _scrollBar.Bounds.Width);
			Assert.Equal ("Dim.Combine(DimView(side=Height, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(1))",
				_scrollBar.Height.ToString ());
			Assert.Equal (24, _scrollBar.Bounds.Height);
			Assert.False (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.False (_scrollBar.OtherScrollBarView.Visible);
			Assert.Equal ("Dim.Combine(DimView(side=Width, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(0))",
				_scrollBar.OtherScrollBarView.Width.ToString ());
			Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
			Assert.Equal (1, _scrollBar.OtherScrollBarView.Bounds.Height);

			_hostView.Lines = 40;
			_hostView.Redraw (_hostView.Bounds);
			Assert.True (_scrollBar.ShowScrollIndicator);
			Assert.True (_scrollBar.Visible);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.Width.ToString ());
			Assert.Equal (1, _scrollBar.Bounds.Width);
			Assert.Equal ("Dim.Combine(DimView(side=Height, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(0))",
				_scrollBar.Height.ToString ());
			Assert.Equal (25, _scrollBar.Bounds.Height);
			Assert.False (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.False (_scrollBar.OtherScrollBarView.Visible);
			Assert.Equal ("Dim.Combine(DimView(side=Width, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(0))",
				_scrollBar.OtherScrollBarView.Width.ToString ());
			Assert.Equal (80, _scrollBar.OtherScrollBarView.Bounds.Width);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
			Assert.Equal (1, _scrollBar.OtherScrollBarView.Bounds.Height);

			_hostView.Cols = 120;
			_hostView.Redraw (_hostView.Bounds);
			Assert.True (_scrollBar.ShowScrollIndicator);
			Assert.True (_scrollBar.Visible);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.Width.ToString ());
			Assert.Equal (1, _scrollBar.Bounds.Width);
			Assert.Equal ("Dim.Combine(DimView(side=Height, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(1))",
				_scrollBar.Height.ToString ());
			Assert.Equal (24, _scrollBar.Bounds.Height);
			Assert.True (_scrollBar.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (_scrollBar.OtherScrollBarView.Visible);
			Assert.Equal ("Dim.Combine(DimView(side=Width, target=HostView()({X=0,Y=0,Width=80,Height=25}))-Dim.Absolute(1))",
				_scrollBar.OtherScrollBarView.Width.ToString ());
			Assert.Equal (79, _scrollBar.OtherScrollBarView.Bounds.Width);
			Assert.Equal ("Dim.Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
			Assert.Equal (1, _scrollBar.OtherScrollBarView.Bounds.Height);
		}

		[Fact]
		public void Constructor_ShowBothScrollIndicator_False_And_IsVertical_True_Refresh_Does_Not_Throws_An_Object_Null_Exception ()
		{
			var exception = Record.Exception (() => {
				Application.Init (new FakeDriver ());

				var top = Application.Top;

				var win = new Window () {
					X = 0,
					Y = 0,
					Width = Dim.Fill (),
					Height = Dim.Fill ()
				};

				List<string> source = new List<string> ();

				for (int i = 0; i < 50; i++) {
					source.Add ($"item {i}");
				}

				var listView = new ListView (source) {
					X = 0,
					Y = 0,
					Width = Dim.Fill (),
					Height = Dim.Fill ()
				};
				win.Add (listView);

				var newScrollBarView = new ScrollBarView (listView, true, false) {
					KeepContentAlwaysInViewport = true
				};
				win.Add (newScrollBarView);

				newScrollBarView.ChangedPosition += () => {
					listView.TopItem = newScrollBarView.Position;
					if (listView.TopItem != newScrollBarView.Position) {
						newScrollBarView.Position = listView.TopItem;
					}
					Assert.Equal (newScrollBarView.Position, listView.TopItem);
					listView.SetNeedsDisplay ();
				};

				listView.DrawContent += (e) => {
					newScrollBarView.Size = listView.Source.Count;
					Assert.Equal (newScrollBarView.Size, listView.Source.Count);
					newScrollBarView.Position = listView.TopItem;
					Assert.Equal (newScrollBarView.Position, listView.TopItem);
					newScrollBarView.Refresh ();
				};

				top.Ready += () => {
					newScrollBarView.Position = 45;
					Assert.Equal (newScrollBarView.Position, newScrollBarView.Size - listView.TopItem + (listView.TopItem - listView.Bounds.Height));
					Assert.Equal (newScrollBarView.Position, listView.TopItem);
					Assert.Equal (27, newScrollBarView.Position);
					Assert.Equal (27, listView.TopItem);
					Application.RequestStop ();
				};

				top.Add (win);

				Application.Run ();

				Application.Shutdown ();
			});

			Assert.Null (exception);
		}

		[Fact]
		public void Constructor_ShowBothScrollIndicator_False_And_IsVertical_False_Refresh_Does_Not_Throws_An_Object_Null_Exception ()
		{
			var exception = Record.Exception (() => {
				Application.Init (new FakeDriver ());

				var top = Application.Top;

				var win = new Window () {
					X = 0,
					Y = 0,
					Width = Dim.Fill (),
					Height = Dim.Fill ()
				};

				List<string> source = new List<string> ();

				for (int i = 0; i < 50; i++) {
					var text = $"item {i} - ";
					for (int j = 0; j < 160; j++) {
						var col = j.ToString ();
						text += col.Length == 1 ? col [0] : col [1];
					}
					source.Add (text);
				}

				var listView = new ListView (source) {
					X = 0,
					Y = 0,
					Width = Dim.Fill (),
					Height = Dim.Fill ()
				};
				win.Add (listView);

				var newScrollBarView = new ScrollBarView (listView, false, false) {
					KeepContentAlwaysInViewport = true
				};
				win.Add (newScrollBarView);

				newScrollBarView.ChangedPosition += () => {
					listView.LeftItem = newScrollBarView.Position;
					if (listView.LeftItem != newScrollBarView.Position) {
						newScrollBarView.Position = listView.LeftItem;
					}
					Assert.Equal (newScrollBarView.Position, listView.LeftItem);
					listView.SetNeedsDisplay ();
				};

				listView.DrawContent += (e) => {
					newScrollBarView.Size = listView.Maxlength;
					Assert.Equal (newScrollBarView.Size, listView.Maxlength);
					newScrollBarView.Position = listView.LeftItem;
					Assert.Equal (newScrollBarView.Position, listView.LeftItem);
					newScrollBarView.Refresh ();
				};

				top.Ready += () => {
					newScrollBarView.Position = 100;
					Assert.Equal (newScrollBarView.Position, newScrollBarView.Size - listView.LeftItem + (listView.LeftItem - listView.Bounds.Width));
					Assert.Equal (newScrollBarView.Position, listView.LeftItem);
					Assert.Equal (92, newScrollBarView.Position);
					Assert.Equal (92, listView.LeftItem);
					Application.RequestStop ();
				};

				top.Add (win);

				Application.Run ();

				Application.Shutdown ();
			});

			Assert.Null (exception);
		}

		[Fact]
		[AutoInitShutdown]
		public void Internal_Tests ()
		{
			var top = Application.Top;
			Assert.Equal (new Rect (0, 0, 80, 25), top.Bounds);
			var view = new View () { Width = Dim.Fill (), Height = Dim.Fill () };
			top.Add (view);
			var sbv = new ScrollBarView (view, true);
			top.Add (sbv);
			Assert.Equal (view, sbv.Host);
			sbv.Size = 40;
			sbv.Position = 0;
			sbv.OtherScrollBarView.Size = 100;
			sbv.OtherScrollBarView.Position = 0;
			// Host bounds is empty.
			Assert.False (sbv.CanScroll (10, out int max, sbv.IsVertical));
			Assert.Equal (0, max);
			Assert.False (sbv.OtherScrollBarView.CanScroll (10, out max, sbv.OtherScrollBarView.IsVertical));
			Assert.Equal (0, max);
			// They aren't visible so they aren't drawn.
			Assert.False (sbv.Visible);
			Assert.False (sbv.OtherScrollBarView.Visible);
			top.LayoutSubviews ();
			// Now the host bounds is not empty.
			Assert.True (sbv.CanScroll (10, out max, sbv.IsVertical));
			Assert.Equal (10, max);
			Assert.True (sbv.OtherScrollBarView.CanScroll (10, out max, sbv.OtherScrollBarView.IsVertical));
			Assert.Equal (10, max);
			Assert.True (sbv.CanScroll (50, out max, sbv.IsVertical));
			Assert.Equal (40, sbv.Size);
			Assert.Equal (15, max); // 15+25=40
			Assert.True (sbv.OtherScrollBarView.CanScroll (150, out max, sbv.OtherScrollBarView.IsVertical));
			Assert.Equal (100, sbv.OtherScrollBarView.Size);
			Assert.Equal (20, max); // 20+80=100
			Assert.False (sbv.Visible);
			Assert.False (sbv.OtherScrollBarView.Visible);
			sbv.KeepContentAlwaysInViewport = false;
			sbv.OtherScrollBarView.KeepContentAlwaysInViewport = false;
			Assert.True (sbv.CanScroll (50, out max, sbv.IsVertical));
			Assert.Equal (39, max);
			Assert.True (sbv.OtherScrollBarView.CanScroll (150, out max, sbv.OtherScrollBarView.IsVertical));
			Assert.Equal (99, max);
			Assert.True (sbv.Visible);
			Assert.True (sbv.OtherScrollBarView.Visible);
		}

		private class TextViewScrollBarView : ScrollBarView {
			private TextView _textView;

			public TextViewScrollBarView (TextView textView) : base (textView, true)
			{
				_textView = textView;

				ChangedPosition += TextViewScrollBarView_ChangedPosition;
				OtherScrollBarView.ChangedPosition += OtherScrollBarView_ChangedPosition;
				VisibleChanged += TextViewScrollBarView_VisibleChanged;
				OtherScrollBarView.VisibleChanged += OtherScrollBarView_VisibleChanged;
				_textView.DrawContent += TextView_DrawContent;
				Initialized += TextViewScrollBarView_Initialized;
				OtherScrollBarView.Initialized += OtherScrollBarView_Initialized;
			}

			private void OtherScrollBarView_Initialized (object sender, EventArgs e)
			{
				OtherScrollBarView_VisibleChanged ();
			}

			private void TextViewScrollBarView_Initialized (object sender, EventArgs e)
			{
				TextViewScrollBarView_VisibleChanged ();
			}

			private void TextView_DrawContent (Rect obj)
			{
				Size = _textView.Lines;
				Position = _textView.TopRow;
				if (OtherScrollBarView != null) {
					OtherScrollBarView.Size = _textView.Maxlength;
					OtherScrollBarView.Position = _textView.LeftColumn;
				}
				LayoutSubviews ();
				Refresh ();
			}

			private void OtherScrollBarView_VisibleChanged ()
			{
				if (OtherScrollBarView.Visible && _textView.BottomOffset == 0) {
					_textView.BottomOffset = 1;
				} else if (!OtherScrollBarView.Visible && _textView.BottomOffset == 1) {
					_textView.BottomOffset = 0;
				}
			}

			private void TextViewScrollBarView_VisibleChanged ()
			{
				if (Visible && _textView.RightOffset == 0) {
					_textView.RightOffset = 1;
				} else if (!Visible && _textView.RightOffset == 1) {
					_textView.RightOffset = 0;
				}
			}

			private void OtherScrollBarView_ChangedPosition ()
			{
				_textView.LeftColumn = OtherScrollBarView.Position;
				if (_textView.LeftColumn != OtherScrollBarView.Position) {
					OtherScrollBarView.Position = _textView.LeftColumn;
				}
				_textView.SetNeedsDisplay ();
			}

			private void TextViewScrollBarView_ChangedPosition ()
			{
				_textView.TopRow = Position;
				if (_textView.TopRow != Position) {
					Position = _textView.TopRow;
				}
				_textView.SetNeedsDisplay ();
			}
		}

		[Fact, AutoInitShutdown]
		public void Hosting_ShowBothScrollIndicator_Invisible ()
		{
			var textView = new TextView () {
				Width = Dim.Fill (),
				Height = Dim.Fill (),
				Text = "This is the help text for the Second Step.\n\nPress the button to see a message box.\n\nEnter name too."
			};
			var win = new Window ("Test") {
				Width = Dim.Fill (),
				Height = Dim.Fill ()
			};
			win.Add (textView);

			var scrollBar = new TextViewScrollBarView (textView);

			Application.Top.Add (win);

			Application.Begin (Application.Top);
			((FakeDriver)Application.Driver).SetBufferSize (45, 20);

			Assert.True (scrollBar.AutoHideScrollBars);
			Assert.Equal (5, textView.Lines);
			Assert.Equal (42, textView.Maxlength);
			Assert.Equal (0, textView.LeftColumn);
			Assert.Equal (0, scrollBar.Position);
			Assert.Equal (0, scrollBar.OtherScrollBarView.Position);
			var expected = @"
┌ Test ─────────────────────────────────────┐
│This is the help text for the Second Step. │
│                                           │
│Press the button to see a message box.     │
│                                           │
│Enter name too.                            │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
│                                           │
└───────────────────────────────────────────┘
";

			var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
			Assert.Equal (new Rect (0, 0, 45, 20), pos);

			textView.WordWrap = true;
			((FakeDriver)Application.Driver).SetBufferSize (26, 20);
			Application.Refresh ();

			Assert.True (textView.WordWrap);
			Assert.True (scrollBar.AutoHideScrollBars);
			Assert.Equal (7, textView.Lines);
			Assert.Equal (22, textView.Maxlength);
			Assert.Equal (0, textView.LeftColumn);
			Assert.Equal (0, scrollBar.Position);
			Assert.Equal (0, scrollBar.OtherScrollBarView.Position);
			expected = @"
┌ Test ──────────────────┐
│This is the help text   │
│for the Second Step.    │
│                        │
│Press the button to     │
│see a message box.      │
│                        │
│Enter name too.         │
│                        │
│                        │
│                        │
│                        │
│                        │
│                        │
│                        │
│                        │
│                        │
│                        │
│                        │
└────────────────────────┘
";

			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
			Assert.Equal (new Rect (0, 0, 26, 20), pos);

			((FakeDriver)Application.Driver).SetBufferSize (10, 10);
			Application.Refresh ();

			Assert.True (textView.WordWrap);
			Assert.True (scrollBar.AutoHideScrollBars);
			Assert.Equal (20, textView.Lines);
			Assert.Equal (7, textView.Maxlength);
			Assert.Equal (0, textView.LeftColumn);
			Assert.Equal (0, scrollBar.Position);
			Assert.Equal (0, scrollBar.OtherScrollBarView.Position);
			expected = @"
┌ Test ──┐
│This   ▲│
│is the ┬│
│help   ││
│text   ┴│
│for    ░│
│the    ░│
│Second ░│
│Step.  ▼│
└────────┘
";

			pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
			Assert.Equal (new Rect (0, 0, 10, 10), pos);
		}


		[Fact, AutoInitShutdown]
		public void ContentBottomRightCorner_Not_Redraw_If_Both_Size_Equal_To_Zero ()
		{
			var text = "This is a test\nThis is a test\nThis is a test\nThis is a test\nThis is a test\nThis is a test";
			var label = new Label (text);
			Application.Top.Add (label);

			var sbv = new ScrollBarView (label, true, true) {
				Size = 100,
			};
			sbv.OtherScrollBarView.Size = 100;
			Application.Begin (Application.Top);

			Assert.Equal (100, sbv.Size);
			Assert.Equal (100, sbv.OtherScrollBarView.Size);
			Assert.True (sbv.ShowScrollIndicator);
			Assert.True (sbv.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (sbv.Visible);
			Assert.True (sbv.OtherScrollBarView.Visible);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
This is a tes▲
This is a tes┬
This is a tes┴
This is a tes░
This is a tes▼
◄├─┤░░░░░░░░► 
", output);

			sbv.Size = 0;
			sbv.OtherScrollBarView.Size = 0;
			Assert.Equal (0, sbv.Size);
			Assert.Equal (0, sbv.OtherScrollBarView.Size);
			Assert.False (sbv.ShowScrollIndicator);
			Assert.False (sbv.OtherScrollBarView.ShowScrollIndicator);
			Assert.False (sbv.Visible);
			Assert.False (sbv.OtherScrollBarView.Visible);
			Application.Top.Redraw (Application.Top.Bounds);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
This is a test
This is a test
This is a test
This is a test
This is a test
This is a test
", output);

			sbv.Size = 50;
			sbv.OtherScrollBarView.Size = 50;
			Assert.Equal (50, sbv.Size);
			Assert.Equal (50, sbv.OtherScrollBarView.Size);
			Assert.True (sbv.ShowScrollIndicator);
			Assert.True (sbv.OtherScrollBarView.ShowScrollIndicator);
			Assert.True (sbv.Visible);
			Assert.True (sbv.OtherScrollBarView.Visible);
			Application.Top.Redraw (Application.Top.Bounds);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
This is a tes▲
This is a tes┬
This is a tes┴
This is a tes░
This is a tes▼
◄├──┤░░░░░░░► 
", output);

		}

		[Fact, AutoInitShutdown]
		public void ContentBottomRightCorner_Not_Redraw_If_One_Size_Equal_To_Zero ()
		{
			var text = "This is a test\nThis is a test\nThis is a test\nThis is a test\nThis is a test\nThis is a test";
			var label = new Label (text);
			Application.Top.Add (label);

			var sbv = new ScrollBarView (label, true, false) {
				Size = 100,
			};
			Application.Begin (Application.Top);

			Assert.Equal (100, sbv.Size);
			Assert.Null (sbv.OtherScrollBarView);
			Assert.True (sbv.ShowScrollIndicator);
			Assert.True (sbv.Visible);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
This is a tes▲
This is a tes┬
This is a tes┴
This is a tes░
This is a tes░
This is a tes▼
", output);

			sbv.Size = 0;
			Assert.Equal (0, sbv.Size);
			Assert.False (sbv.ShowScrollIndicator);
			Assert.False (sbv.Visible);
			Application.Top.Redraw (Application.Top.Bounds);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
This is a test
This is a test
This is a test
This is a test
This is a test
This is a test
", output);
		}

		[Fact, AutoInitShutdown]
		public void ShowScrollIndicator_False_Must_Also_Set_Visible_To_False_To_Not_Respond_To_Events ()
		{
			var clicked = false;
			var text = "This is a test\nThis is a test\nThis is a test\nThis is a test\nThis is a test";
			var label = new Label (text) { Width = 14, Height = 5 };
			var btn = new Button (14, 0, "Click Me!");
			btn.Clicked += () => clicked = true;
			Application.Top.Add (label, btn);

			var sbv = new ScrollBarView (label, true, false) {
				Size = 5,
			};
			Application.Begin (Application.Top);

			Assert.Equal (5, sbv.Size);
			Assert.Null (sbv.OtherScrollBarView);
			Assert.False (sbv.ShowScrollIndicator);
			Assert.False (sbv.Visible);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
This is a test[ Click Me! ]
This is a test             
This is a test             
This is a test             
This is a test             
", output);

			ReflectionTools.InvokePrivate (
				typeof (Application),
				"ProcessMouseEvent",
				new MouseEvent () {
					X = 15,
					Y = 0,
					Flags = MouseFlags.Button1Clicked
				});

			Assert.Null (Application.MouseGrabView);
			Assert.True (clicked);

			clicked = false;

			sbv.Visible = true;
			Assert.Equal (5, sbv.Size);
			Assert.False (sbv.ShowScrollIndicator);
			Assert.True (sbv.Visible);
			Application.Top.Redraw (Application.Top.Bounds);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
This is a test[ Click Me! ]
This is a test             
This is a test             
This is a test             
This is a test             
", output);

			ReflectionTools.InvokePrivate (
				typeof (Application),
				"ProcessMouseEvent",
				new MouseEvent () {
					X = 15,
					Y = 0,
					Flags = MouseFlags.Button1Clicked
				});

			Assert.Null (Application.MouseGrabView);
			Assert.True (clicked);
			Assert.Equal (5, sbv.Size);
			Assert.False (sbv.ShowScrollIndicator);
			Assert.False (sbv.Visible);
		}

		[Fact, AutoInitShutdown]
		public void ScrollBarView_Inside_TextView_Always_Scroll ()
		{
			var text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n1234567890\n";
			var tv1 = new TextView () {
				Id = "tv1",
				Width = Dim.Fill (),
				Height = Dim.Fill (),
				Text = text
			};
			var f1 = new FrameView () {
				Width = Dim.Fill (),
				Height = Dim.Percent (70)
			};
			f1.Add (tv1);
			var sBar1 = new TextViewScrollBarView (tv1);
			var tv2 = new TextView () {
				Id = "tv2",
				Width = Dim.Fill (),
				Height = Dim.Fill (),
				Text = text,
				ReadOnly = true
			};
			var f2 = new FrameView () {
				Y = Pos.Bottom (f1),
				Width = Dim.Fill (),
				Height = Dim.Fill ()
			};
			f2.Add (tv2);
			var sBar2 = new TextViewScrollBarView (tv2);
			var win = new Window () {
				Width = Dim.Fill (),
				Height = Dim.Fill ()
			};
			win.Add (f1, f2);

			Application.Top.Add (win);

			Application.Begin (Application.Top);
			((FakeDriver)Application.Driver).SetBufferSize (10, 20);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (0, sBar1.Position);
			Assert.Equal (0, tv1.TopRow);
			Assert.False (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
			Assert.Equal (0, tv1.LeftColumn);
			Assert.Equal (0, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (0, sBar2.Position);
			Assert.Equal (0, tv2.TopRow);
			Assert.False (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (0, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││1    ▲││
││2    ┬││
││3    │││
││4    │││
││5    │││
││6    │││
││7    │││
││8    ┴││
││9    ░││
││10   ▼││
│└──────┘│
│┌──────┐│
││1    ▲││
││2    ┬││
││3    ┴││
││4    ▼││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 9, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 9, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (2, sBar1.Position);
			Assert.Equal (2, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
			Assert.Equal (0, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (1, sBar2.Position);
			Assert.Equal (1, tv2.TopRow);
			Assert.False (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (0, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││3    ▲││
││4    ░││
││5    ┬││
││6    │││
││7    │││
││8    │││
││9    │││
││10   ┴││
││12345▼││
││◄├┤░► ││
│└──────┘│
│┌──────┐│
││2    ▲││
││3    ┬││
││4    ┴││
││5    ▼││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 8, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 8, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (3, sBar1.Position);
			Assert.Equal (3, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
			Assert.Equal (0, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (2, sBar2.Position);
			Assert.Equal (2, tv2.TopRow);
			Assert.False (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (0, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││4    ▲││
││5    ░││
││6    ┬││
││7    │││
││8    │││
││9    │││
││10   │││
││12345┴││
││     ▼││
││◄├┤░► ││
│└──────┘│
│┌──────┐│
││3    ▲││
││4    ┬││
││5    ┴││
││6    ▼││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (3, sBar1.Position);
			Assert.Equal (3, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (1, sBar1.OtherScrollBarView.Position);
			Assert.Equal (1, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (3, sBar2.Position);
			Assert.Equal (3, tv2.TopRow);
			Assert.False (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (0, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││     ▲││
││     ░││
││     ┬││
││     │││
││     │││
││     │││
││0    │││
││23456┴││
││     ▼││
││◄├─┤► ││
│└──────┘│
│┌──────┐│
││4    ▲││
││5    ┬││
││6    ┴││
││7    ▼││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (3, sBar1.Position);
			Assert.Equal (3, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (2, sBar1.OtherScrollBarView.Position);
			Assert.Equal (2, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (4, sBar2.Position);
			Assert.Equal (4, tv2.TopRow);
			Assert.False (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (0, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││     ▲││
││     ░││
││     ┬││
││     │││
││     │││
││     │││
││     │││
││34567┴││
││     ▼││
││◄├─┤► ││
│└──────┘│
│┌──────┐│
││5    ▲││
││6    ┬││
││7    ┴││
││8    ▼││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (3, sBar1.Position);
			Assert.Equal (3, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (3, sBar1.OtherScrollBarView.Position);
			Assert.Equal (3, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (5, sBar2.Position);
			Assert.Equal (5, tv2.TopRow);
			Assert.False (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (0, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││     ▲││
││     ░││
││     ┬││
││     │││
││     │││
││     │││
││     │││
││45678┴││
││     ▼││
││◄├─┤► ││
│└──────┘│
│┌──────┐│
││6    ▲││
││7    ┬││
││8    ┴││
││9    ▼││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (3, sBar1.Position);
			Assert.Equal (3, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (4, sBar1.OtherScrollBarView.Position);
			Assert.Equal (4, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (6, sBar2.Position);
			Assert.Equal (6, tv2.TopRow);
			Assert.False (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (0, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││     ▲││
││     ░││
││     ┬││
││     │││
││     │││
││     │││
││     │││
││56789┴││
││     ▼││
││◄░├┤► ││
│└──────┘│
│┌──────┐│
││7    ▲││
││8    ┬││
││9    ┴││
││10   ▼││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 3, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (3, sBar1.Position);
			Assert.Equal (3, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (5, sBar1.OtherScrollBarView.Position);
			Assert.Equal (5, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (8, sBar2.Position);
			Assert.Equal (8, tv2.TopRow);
			Assert.True (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (1, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││     ▲││
││     ░││
││     ┬││
││     │││
││     │││
││     │││
││     │││
││67890┴││
││     ▼││
││◄░├┤► ││
│└──────┘│
│┌──────┐│
││9    ▲││
││10   ◊││
││12345▼││
││◄├┤░► ││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 2, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.MouseEvent (new MouseEvent { X = 0, Y = 2, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (2, sBar1.Position);
			Assert.Equal (2, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (5, sBar1.OtherScrollBarView.Position);
			Assert.Equal (5, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (9, sBar2.Position);
			Assert.Equal (9, tv2.TopRow);
			Assert.True (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar2.OtherScrollBarView.Position);
			Assert.Equal (0, tv2.LeftColumn);
			Assert.Equal (1, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││     ▲││
││     ░││
││     ┬││
││     │││
││     │││
││     │││
││     │││
││     ┴││
││67890▼││
││◄░├┤► ││
│└──────┘│
│┌──────┐│
││10   ▲││
││12345◊││
││     ▼││
││◄├┤░► ││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (1, sBar1.Position);
			Assert.Equal (1, tv1.TopRow);
			Assert.True (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (5, sBar1.OtherScrollBarView.Position);
			Assert.Equal (5, tv1.LeftColumn);
			Assert.Equal (1, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (9, sBar2.Position);
			Assert.Equal (9, tv2.TopRow);
			Assert.True (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (1, sBar2.OtherScrollBarView.Position);
			Assert.Equal (1, tv2.LeftColumn);
			Assert.Equal (1, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││     ▲││
││     ┬││
││     │││
││     │││
││     │││
││     │││
││     ┴││
││     ░││
││     ▼││
││◄░├┤► ││
│└──────┘│
│┌──────┐│
││0    ▲││
││23456◊││
││     ▼││
││◄├─┤► ││
│└──────┘│
└────────┘", output);

			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar1 }));
			Assert.True (sBar1.MouseEvent (new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Released, View = sBar1 }));
			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Pressed, View = sBar2 }));
			Assert.True (sBar2.OtherScrollBarView.MouseEvent (new MouseEvent { X = 4, Y = 0, Flags = MouseFlags.Button1Released, View = sBar2 }));
			win.Redraw (win.Bounds);
			Application.MainLoop.MainIteration ();
			win.Redraw (win.Bounds);

			// sBar1
			Assert.True (sBar1.Visible);
			Assert.Equal (0, sBar1.Position);
			Assert.Equal (0, tv1.TopRow);
			Assert.False (sBar1.OtherScrollBarView.Visible);
			Assert.Equal (0, sBar1.OtherScrollBarView.Position);
			Assert.Equal (0, tv1.LeftColumn);
			Assert.Equal (0, tv1.BottomOffset);
			Assert.Equal (1, tv1.RightOffset);
			Assert.Equal (new Point (0, 0), tv1.CursorPosition);

			// sBar2
			Assert.True (sBar2.Visible);
			Assert.Equal (9, sBar2.Position);
			Assert.Equal (9, tv2.TopRow);
			Assert.True (sBar2.OtherScrollBarView.Visible);
			Assert.Equal (2, sBar2.OtherScrollBarView.Position);
			Assert.Equal (2, tv2.LeftColumn);
			Assert.Equal (1, tv2.BottomOffset);
			Assert.Equal (1, tv2.RightOffset);
			Assert.Equal (new Point (0, 0), tv2.CursorPosition);

			TestHelpers.AssertDriverContentsWithFrameAre (@"
┌────────┐
│┌──────┐│
││1    ▲││
││2    ┬││
││3    │││
││4    │││
││5    │││
││6    │││
││7    │││
││8    ┴││
││9    ░││
││10   ▼││
│└──────┘│
│┌──────┐│
││     ▲││
││34567◊││
││     ▼││
││◄├─┤► ││
│└──────┘│
└────────┘", output);
		}
	}
}
