// WideMargin. Simple fast bible software.
// Copyright (C) 2011  Daniel Hughes
//
// This program 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, either version 3 of the License, 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 the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Threading;
using Gtk;
using System.Linq;
using System.Data.Linq;
using System.Collections.Generic;
using WideMargin.Utilities;
using WideMargin.MVCInterfaces;

namespace WideMargin.GUI
{
	[System.ComponentModel.ToolboxItem(true)]
	public partial class SearchPage : Gtk.Bin, IBibleView
	{
		private Label _tabLabel;
		private EventHandler<EventArgs> _closing;
		private EventLocker _eventLocker = new EventLocker();
		private Widget _content;
		private SearchResultsWidget _searchResultsWidget;
		private PassagePage _passagePage;
		private GenericView _genericView;
		private FilteredView _booksView;
		private EventHandler<StringEventArgs> _searchResult;
		private EventHandler<EventArgs> _requestAutoCompleteList;
		private Gdk.Pixbuf _closeImage;
		private ListStore _completionStore;
		private bool _completionInitalised = false;
		private EventHandler<EventArgs> _navigateBackwards;
		private EventHandler<EventArgs> _navigateForwards;
		private EventHandler<EventArgs> _requestAboutInfo;
		private Frame _frame;
		
		public SearchPage ()
		{
			this.Build ();
			
			_bibleBar.Changed += BibleBarChanged;
			_passagePage = new PassagePage();
			_searchResultsWidget = new SearchResultsWidget();
			_genericView = new GenericView();
			_booksView = new FilteredView();
			
			_closeImage = ImageCache.Get().CloseImage;
			this.CanFocus = true;
			EntryCompletion completion = new EntryCompletion();
			completion.TextColumn = 0;
			_completionStore = new ListStore (typeof (string));
			completion.Model = _completionStore;
			_bibleBar.Completion = completion;
			
			_backButton.Image = new Image(ImageCache.Get().BackArrow);
			_forwardButton.Image = new Image(ImageCache.Get().ForwardArrow);
			_menuButton.Image = new Image(ImageCache.Get().Settings);
			_frame = new Frame();
			_frame.ShadowType = ShadowType.EtchedIn;
			vbox4.Add(_frame);
		}
			
		public void FocusBibleBar()
		{
			ThreadPool.QueueUserWorkItem(FocusBibleBarCallback);
		}
			                                              
		public void FocusBibleBarCallback(object state)
		{
			this.Invoke(() => _bibleBar.GrabFocus());
		}
		
		/// <summary>
		/// Tiggered to Request about info to show in
		/// the about box.
		/// </summary>
		public event EventHandler<EventArgs> RequestAboutInfo
		{
			add
			{
				_eventLocker.Add(ref _requestAboutInfo, value);
			}
			remove
			{
				_eventLocker.Remove(ref _requestAboutInfo, value);
			}
		}
		
		public event EventHandler<EventArgs> Closing
		{
			add
			{
				_eventLocker.Add(ref _closing, value);
			}
			remove
			{
				_eventLocker.Remove(ref _closing, value);
			}
		}
		
		public event EventHandler<EventArgs> RequestAutoCompleteList
		{
			add
			{
				_eventLocker.Add(ref _requestAutoCompleteList, value);	
			}
			remove
			{
				_eventLocker.Remove(ref _requestAutoCompleteList, value);
			}
		}
		
		public Widget CreateTabTitle()
		{
			HBox box = new HBox(false, 0);
			box.BorderWidth = 0;
			
			//add tab label
			_tabLabel = new Label("New Tab");
			_tabLabel.ClearFlag(WidgetFlags.CanFocus);
			_tabLabel.SetPadding(0,0);
			box.PackStart(_tabLabel,true,true,0);
			
			//add close button
			Button button = new Button();
			Gtk.Image closeIcon = new Gtk.Image (_closeImage);
			closeIcon.SetPadding (0, 0);
			button.Image = closeIcon;
			button.Clicked += OnCloseClicked;
			button.Relief = ReliefStyle.None;
			button.CanDefault = false;
			button.BorderWidth = 0;
			box.PackEnd(button,false,true,0);
			
			box.ClearFlag (WidgetFlags.CanFocus);
			box.CanFocus = false;
			box.ShowAll();
			return box;
		}
		
		private void OnCloseClicked(object sender, EventArgs args)
		{
			_closing.Fire(this, args);
		}
		
		protected virtual void BibleBarActivated (object sender, System.EventArgs e)
		{
			BibleBarChanged(sender, e);
		}
	
		protected virtual void BibleBarChanged (object sender, System.EventArgs e)
		{
			if(!_completionInitalised)
			{
				_requestAutoCompleteList.Fire(this, new EventArgs());
				_completionInitalised = true;
			}
			
			_searchResult.Fire(this, new StringEventArgs(_bibleBar.Text));
		}
		
		private void ShowWidget(Widget widget)
		{
			
			if(_content != null && widget != _content)
			{
				_frame.Remove(_content);
			}
			if(widget != _content)
			{
				_frame.Add(widget);
				_content = widget;
				vbox4.ShowAll();
			}
		}
		
		public event EventHandler<StringEventArgs> SearchRequest
		{
			add
			{
				_eventLocker.Add(ref _searchResult, value);
			}
			remove
			{
				_eventLocker.Remove(ref _searchResult, value);
			}
		}
		
		/// <summary>
		/// Occurs when the user requests to navigate backwards
		/// </summary>
		public event EventHandler<EventArgs> NavigateBackwards
		{
			add
			{
				_eventLocker.Add(ref _navigateBackwards, value);
			}
			remove
			{
				_eventLocker.Remove(ref _navigateBackwards, value);
			}
		}
		
		/// <summary>
		/// Occurs when the user requests to navigate forwards.
		/// </summary>
		public event EventHandler<EventArgs> NavigateForwards
		{
			add
			{
				_eventLocker.Add(ref _navigateForwards, value);
			}
			remove
			{
				_eventLocker.Remove(ref _navigateForwards, value);
			}
		}
		
		public IPassageView PassageView
		{
			get
			{
				return _passagePage;	
			}
		}
		
		public ISearchResultsView SearchView
		{
			get
			{
				return _searchResultsWidget;	
			}
		}
		
		public IGenericView GenericView
		{
			get
			{
				return _genericView;	
			}
		}
		
		public IFilteredView BooksView
		{
			get
			{
				return _booksView;	
			}
		}
		
		public void ShowPassageView()
		{
			ThreadSafeShow(_passagePage);
		}
		
		public void ShowSearchView()
		{
			ThreadSafeShow(_searchResultsWidget);
		}
		
		public void ShowBooksView()
		{
			ThreadSafeShow(_booksView);	
		}
		
		/// <summary>
		/// Shows the GenericView, this is used for the dayly readings.
		/// </summary>
		public void ShowGenericView()
		{
			ThreadSafeShow(_genericView);	
		}
		
		public void ThreadSafeShow(Widget widget)
		{			
			this.Invoke(() =>
			{
				ShowWidget(widget);
			});
		}
		
		public void ChangeTitle(string title)
		{
			this.Invoke(() =>
			{
				_tabLabel.Text = title;
			});
		}
		
		public void ChangeBibleBar(string text)
		{
			this.Invoke(() =>
			{
				_bibleBar.Text = text;
			});
		}
		
		public void ShowAutoCompleteList(IEnumerable<string> list)
		{
			this.Invoke(() =>
			{
				foreach(string book in list)
				{
					_completionStore.AppendValues (book);
				}
			});
		}
		
		/// <summary>
		/// Occurs when the back button is clicked.
		/// Rises the event to the controller so it 
		/// can navigate backwards
		/// </summary>
		protected virtual void HandleForwardButtonClicked (object sender, EventArgs e)
		{
			_navigateForwards.Fire(this, EventArgs.Empty);
		}
		
		/// <summary>
		/// Occurs when the forward button is clicked.
		/// Raises the event to the controller so it 
		/// can navigate forwards.
		/// </summary>
		protected virtual void HandleBackButtonClicked (object sender, EventArgs e)
		{
			_navigateBackwards.Fire(this, EventArgs.Empty);
		}
		
		/// <summary>
		/// User has clicked on the settings menu button.
		/// Shows the menu
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void HandleMenuClicked (object sender, System.EventArgs e)
		{
			Menu menu = new Menu();
			MenuItem aboutMenuItem = new MenuItem("About");
			aboutMenuItem.Activated += HandleAboutMenuItemActivated;
			menu.Add(aboutMenuItem);
			
			menu.ShowAll();
			menu.Popup(null, null, MenuPosition, 0, 0);
		}

		/// <summary>
		/// The About menu item has been activated
		/// Passes this request on to the controller.
		/// </summary>
		/// <param name="sender">about menu item</param>
		/// <param name="args">default event args</param>
		private void HandleAboutMenuItemActivated (object sender, EventArgs args)
		{
			_requestAboutInfo.Fire(this, args);
		}
		
		/// <summary>
		/// Callback for the setting the position of the settings menu
		/// </summary>
		/// <param name="menu">the menu to set the position for</param>
		/// <param name="x">out param for the x cordinate</param>
		/// <param name="y">out param for the y cordinate</param>
		/// <param name="pushIn">out param for weather to push it onto the screen if drawn off it.</param>
		private void MenuPosition(Menu menu, out int x, out int y, out bool pushIn)
	    {
			_menuButton.ParentWindow.GetOrigin(out x, out y);
			int labelWidth = menu.SizeRequest().Width; //need to look up actual width.
			x += _menuButton.Allocation.X - labelWidth + _menuButton.Allocation.Width;
			y += _menuButton.Allocation.Y + _menuButton.Allocation.Height;
			pushIn = true;
		}
		
		/// <summary>
		/// Show about info
		/// </summary>
		/// <param name="text">About info including XML markup.</param>
		public void ShowAboutInfo(string text)
		{			
			this.Invoke(() =>
			{
				using(AboutDialog dialog = new AboutDialog(text))
				{
					dialog.ParentWindow = this.ParentWindow;
					dialog.Run();
					dialog.Destroy();
				}
			});
		}

		/// <summary>
		/// Runs the action on the UI thread
		/// </summary>
		/// <param name='action'>
		/// Action to run.
		/// </param>
		public void RunOnUiThread(System.Action action)
		{
			this.Invoke(action);
		}
	}
}

