using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MyNamespace
{
	/// <summary>
	///		HelpForm for several of Harry's programs
	///
	/// ---------------- *** LEGAL NOTICE: *** ------------------
	///
	/// This program is free software; you can redistribute it and/or modify
	/// it under the terms of the GNU General Public License (GPL) as published
	/// by the Free Software Foundation. cf. the file COPYING.txt.
	///
	/// ------------ *** end of legal notice *** ------------------
	/// </summary>

	public class HelpForm : Form
	{
		private Button buttonFind;
		private Button buttonFindUp;
		private Label labelTitle;
		private MainMenu mainMenuHelp;
		private MenuItem menuExit;
		private MenuItem menuFile;
		private MenuItem menuItem4;
		private MenuItem menuPrint;
		private MenuItem menuPrintPreview;
		private MenuItem menuPrintSetup;
		private PrintDialog printDialogHelp;
		private PrintPreviewDialog printPreviewDialog;
		private RichTextBox richTextOut;
		private StringReader stringReader;
		private System.Drawing.Printing.PrintDocument printDocumentHelp;
		private TextBox textBoxFind;
		private Timer timerHelp;

		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.IContainer components;

		public HelpForm()
		{
			InitializeComponent();
			helpTextOut = richTextOut;
			helpTextOut.Clear();
			findSt = "";
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			RunForm.helpWidth = this.Width;
			RunForm.helpHeight = this.Height;
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(HelpForm));
			this.mainMenuHelp = new MainMenu(this.components);
			this.menuFile = new MenuItem();
			this.menuPrintSetup = new MenuItem();
			this.menuPrint = new MenuItem();
			this.menuPrintPreview = new MenuItem();
			this.menuItem4 = new MenuItem();
			this.menuExit = new MenuItem();
			this.richTextOut = new RichTextBox();
			this.labelTitle = new Label();
			this.printDialogHelp = new PrintDialog();
			this.printDocumentHelp = new System.Drawing.Printing.PrintDocument();
			this.printPreviewDialog = new PrintPreviewDialog();
			this.timerHelp = new Timer(this.components);
			this.textBoxFind = new TextBox();
			this.buttonFind = new Button();
			this.buttonFindUp = new Button();
			this.SuspendLayout();
			// 
			// mainMenuHelp
			// 
			this.mainMenuHelp.MenuItems.AddRange(new MenuItem[] {
			this.menuFile});
			// 
			// menuFile
			// 
			this.menuFile.Index = 0;
			this.menuFile.MenuItems.AddRange(new MenuItem[] {
			this.menuPrintSetup,
			this.menuPrint,
			this.menuPrintPreview,
			this.menuItem4,
			this.menuExit});
			this.menuFile.Text = "&File";
			// 
			// menuPrintSetup
			// 
			this.menuPrintSetup.Index = 0;
			this.menuPrintSetup.Text = "P&rint Setup";
			this.menuPrintSetup.Click += new System.EventHandler(this.menuPrintSetup_Click);
			// 
			// menuPrint
			// 
			this.menuPrint.Index = 1;
			this.menuPrint.Shortcut = Shortcut.CtrlP;
			this.menuPrint.Text = "&Print...";
			this.menuPrint.Click += new System.EventHandler(this.menuPrint_Click);
			// 
			// menuPrintPreview
			// 
			this.menuPrintPreview.Index = 2;
			this.menuPrintPreview.Text = "Print Pre&view";
			this.menuPrintPreview.Click += new System.EventHandler(this.menuPrintPreview_Click);
			// 
			// menuItem4
			// 
			this.menuItem4.Index = 3;
			this.menuItem4.Text = "-";
			// 
			// menuExit
			// 
			this.menuExit.Index = 4;
			this.menuExit.Text = "E&xit";
			this.menuExit.Click += new System.EventHandler(this.menuExit_Click);
			// 
			// richTextOut
			// 
			this.richTextOut.BackColor = System.Drawing.SystemColors.Window;
			this.richTextOut.Font = new System.Drawing.Font("Courier New", 9.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
			this.richTextOut.Location = new System.Drawing.Point(0, 35);
			this.richTextOut.Name = "richTextOut";
			this.richTextOut.ReadOnly = true;
			this.richTextOut.Size = new System.Drawing.Size(680, 428);
			this.richTextOut.TabIndex = 0;
			this.richTextOut.Text = "";
			this.richTextOut.KeyDown += new KeyEventHandler(this.richTextOut_KeyDown);
			// 
			// labelTitle
			// 
			this.labelTitle.Font = new System.Drawing.Font("Tahoma", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
			this.labelTitle.Location = new System.Drawing.Point(24, 8);
			this.labelTitle.Name = "labelTitle";
			this.labelTitle.Size = new System.Drawing.Size(416, 23);
			this.labelTitle.TabIndex = 1;
			this.labelTitle.Text = "<App.Title> Help Form";
			// 
			// printDocumentHelp
			// 
			this.printDocumentHelp.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(this.printDocumentHelp_PrintPage);
			// 
			// printPreviewDialog
			// 
			this.printPreviewDialog.AutoScrollMargin = new System.Drawing.Size(0, 0);
			this.printPreviewDialog.AutoScrollMinSize = new System.Drawing.Size(0, 0);
			this.printPreviewDialog.ClientSize = new System.Drawing.Size(792, 677);
			this.printPreviewDialog.Enabled = true;
			this.printPreviewDialog.Icon = ((System.Drawing.Icon)(resources.GetObject("printPreviewDialog.Icon")));
			this.printPreviewDialog.MaximizeBox = false;
			this.printPreviewDialog.Name = "printPreviewDialog";
			this.printPreviewDialog.Visible = false;
			// 
			// timerHelp
			// 
			this.timerHelp.Enabled = true;
			this.timerHelp.Interval = 500;
			this.timerHelp.Tick += new System.EventHandler(this.timerHelp_Tick);
			// 
			// textBoxFind
			// 
			this.textBoxFind.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
			this.textBoxFind.Location = new System.Drawing.Point(504, 8);
			this.textBoxFind.Name = "textBoxFind";
			this.textBoxFind.Size = new System.Drawing.Size(100, 22);
			this.textBoxFind.TabIndex = 2;
			this.textBoxFind.KeyDown += new KeyEventHandler(this.richTextOut_KeyDown);
			// 
			// buttonFind
			// 
			this.buttonFind.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
			this.buttonFind.Location = new System.Drawing.Point(440, 8);
			this.buttonFind.Name = "buttonFind";
			this.buttonFind.Size = new System.Drawing.Size(56, 24);
			this.buttonFind.TabIndex = 4;
			this.buttonFind.Text = "F3 Find:";
			this.buttonFind.Click += new System.EventHandler(this.buttonFind_Click);
			this.buttonFind.KeyDown += new KeyEventHandler(this.richTextOut_KeyDown);
			// 
			// buttonFindUp
			// 
			this.buttonFindUp.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
			this.buttonFindUp.Location = new System.Drawing.Point(608, 8);
			this.buttonFindUp.Name = "buttonFindUp";
			this.buttonFindUp.Size = new System.Drawing.Size(48, 24);
			this.buttonFindUp.TabIndex = 5;
			this.buttonFindUp.Text = "F2 Up";
			this.buttonFindUp.Click += new System.EventHandler(this.buttonFindUp_Click);
			this.buttonFindUp.KeyDown += new KeyEventHandler(this.richTextOut_KeyDown);
			// 
			// HelpForm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(682, 435);
			this.Controls.Add(this.buttonFindUp);
			this.Controls.Add(this.buttonFind);
			this.Controls.Add(this.textBoxFind);
			this.Controls.Add(this.labelTitle);
			this.Controls.Add(this.richTextOut);
			this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
			this.Menu = this.mainMenuHelp;
			this.Name = "HelpForm";
			this.Text = "<App.Title> - Help";
			this.Resize += new System.EventHandler(this.HelpForm_Resize);
			this.Closing += new System.ComponentModel.CancelEventHandler(this.HelpForm_Closing);
			this.KeyDown += new KeyEventHandler(this.richTextOut_KeyDown);
			this.ResumeLayout(false);
			this.PerformLayout();

		}
		#endregion

		public void Help() // -------- Begin Main program Help
		{
			if (Global.helpUp >= 3) { Dispose(true); return; } // allow at most 3 Help Forms up
			Global.helpUp++;
			this.Cursor = Cursors.Arrow;
			this.Text = Global.title + " - Help";
			labelTitle.Text = Global.title + " Help";
			HelpFile = Global.pName + "Help.txt";
			ReadHelp();
			RunForm.FocusBothUs();
			helpTextOut.Select(0, 0);	// move cursor to top
			helpTextOut.Focus();		// and focus
			this.Width = RunForm.helpWidth;			// Resize the form
			this.Height = RunForm.helpHeight;
		}

		private void ReadHelp()
		{
			StreamReader readHelpFile = null;
			string line;
			try
			{
				helpTextOut.Text = "";					// clear text output window
				PLn("");
				PLn("Reading Help File: \"" + HelpFile + '"');
				string dir;
				if (!Common.OpenRead(out readHelpFile, HelpFile, out dir)) // open the file
				{
					PLn("File not found: \"" + HelpFile + '"');	// file not found
					return;
				}
				helpTextOut.Text = "";					// clear text output window again
				line = readHelpFile.ReadLine();			// read first line of file
				while (line != null)					// loop until end of file
				{
					PLn("  " + line);					// output a line of Help
					line = readHelpFile.ReadLine();		// read in next line
				}
			}
			catch (Exception ex)
			{
				PLn("ReadHelp: " + ex.Message);			// read error to HelpForm
				Common.ReportError("ReadHelp", ex.Message);	// and output screen
			}
			finally
			{
				if (readHelpFile != null)
				{
					readHelpFile.Close();				// close the file
				}
			}
		}

		private void menuPrint_Click(object sender, System.EventArgs e)
		// read Help from the RichTextBox and print it
		{
			string stringText = this.helpTextOut.Text;
			stringReader = new StringReader(stringText);
			printDialogHelp.Document = printDocumentHelp;
			if (printDialogHelp.ShowDialog() == DialogResult.OK)
			{
				this.printDocumentHelp.Print();
			}
		}

		private void Prn(string st) // print string to textOut window
		{
			helpTextOut.AppendText(st);
		}

		private void PLn(string st) // print string ... and new line to textOut
		{
			Prn(st + "\n");
		}

		private void menuExit_Click(object sender, System.EventArgs e) // exit help
		{
			Global.helpUp--;
			Dispose(true);
		}

		private void menuPrintSetup_Click(object sender, System.EventArgs e) // show printer setup dialog
		{
			this.printDialogHelp.Document = this.printDocumentHelp;
			printDialogHelp.ShowDialog();
		}

		private void HelpForm_Resize(object sender, System.EventArgs e) // resize the help form
		{
			int jTop = labelTitle.Top + labelTitle.Height + 5;
			this.richTextOut.SetBounds(1, jTop, this.ClientRectangle.Width - 2, this.ClientRectangle.Height - jTop -1);
		}

		private void printDocumentHelp_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs ev)
		{
			float linesPerPage = 0;
			float yPosition = 0;
			int count = 0;
			float leftMargin = ev.MarginBounds.Left;
			float topMargin = ev.MarginBounds.Top;
			string line = null;
			Font printFont = this.helpTextOut.Font;
			SolidBrush myBrush = new SolidBrush(Color.Black);

			// work out the number of lines per page, using the MarginBounds.
			linesPerPage = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics);

			// iterate over the string using the StringReader, printing each line.
			while (count < linesPerPage && (line=stringReader.ReadLine()) != null)
			{
				// calculate the next line position based on
				// the height of the font according to the printing device
				yPosition = topMargin + (count * printFont.GetHeight(ev.Graphics));

				// draw the next line in the rich edit control
				ev.Graphics.DrawString(line, printFont, myBrush, leftMargin, yPosition, new StringFormat());
				count++;
			}

			// if there are more lines, print another page.
			if(line != null)
				ev.HasMorePages = true;
			else
			{
				ev.HasMorePages = false;						// end of print
				string stringText = helpTextOut.Text;			// prepare to print again
				stringReader = new StringReader(stringText);
			}

			myBrush.Dispose();
		}

		private void menuPrintPreview_Click(object sender, System.EventArgs e)
		{
			try
			{
				string stringText = helpTextOut.Text;
				stringReader = new StringReader(stringText);
				printPreviewDialog.Document = printDocumentHelp;
				printPreviewDialog.FormBorderStyle = FormBorderStyle.Fixed3D;
				printPreviewDialog.ShowDialog();
			}
			catch (Exception ex)
			{
				System.Console.WriteLine(ex.Message.ToString());
				Common.ReportError("menuPrintPreview_Click", ex.Message);
			}
		}

		private void richTextOut_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Escape) // ESC key pressed so Exit Help
			{
				Global.helpUp--;
				Dispose(true);
			}
			if (e.KeyCode == Keys.F2)	// F2 key pressed so do a find going up
			{
				buttonFindUp_Click(sender, e);
			}
			if (e.KeyCode == Keys.F3 || e.KeyCode == Keys.Return)	// F3 key or Return pressed so do a normal find
			{
				buttonFind_Click(sender, e);
			}
			if (e.KeyCode == Keys.F)	// F key pressed so copy selected text to textBoxFind
			{
				if (e.Control)
				{
					string st = helpTextOut.SelectedText;	// only copy up to first newline
					int i = st.IndexOf("\n");
					if (i >= 0) st = st.Substring(0, i);
					textBoxFind.Text = st;
					textBoxFind.Select(0, st.Length);		// move cursor found location
					textBoxFind.Focus();					// and focus
				}
			}
		}

		internal class Helpers
		{
			[Flags]
				public enum PlaySoundFlags : int
			{
				SND_SYNC = 0x0000,	/* play synchronously (default) */
				SND_ASYNC = 0x0001,	/* play asynchronously */
				SND_NODEFAULT = 0x0002,	/* silence (!default) if sound not found */
				SND_MEMORY = 0x0004,	/* pszSound points to a memory file */
				SND_LOOP = 0x0008,		/* loop the sound until next sndPlaySound */
				SND_NOSTOP = 0x0010,	/* don't stop any currently playing sound */
				SND_NOWAIT = 0x00002000,	/* don't wait if the driver is busy */
				SND_ALIAS = 0x00010000,		/* name is a registry alias */
				SND_ALIAS_ID = 0x00110000,	/* alias is a predefined ID */
				SND_FILENAME = 0x00020000,	/* name is file name */
				SND_RESOURCE = 0x00040004	/* name is resource name or atom */
			}

			[DllImport("winmm.dll")]
			public static extern bool PlaySound( string szSound, IntPtr hMod, PlaySoundFlags flags );

			[DllImport("kernel32.dll")]
			public static extern bool Beep(int freq, int dur);
		}

		public class Sound
		{
			public static void Play( string strFileName )
			{
				try
				{
					Helpers.PlaySound( strFileName, IntPtr.Zero, Helpers.PlaySoundFlags.SND_FILENAME | Helpers.PlaySoundFlags.SND_ASYNC );
				}
				catch
				{
					// DllImport("winmm.dll") file not found or no PlaySound entry point in the file
					// so generate no sound
				}
			}
		}

		public int FindMyText(string searchText, int searchStart, int searchEnd)
		{
			// Initialize the return value to false by default.
			int returnValue = -1;

			// Ensure that a search string and a valid starting point are specified.
			if (searchText.Length > 0 && searchStart >= 0)
			{
				// Ensure that a valid ending value is provided.
				if (searchEnd > searchStart || searchEnd == -1)
				{
					// Obtain the location of the search string in the richTextBox.
					int indexToText = richTextOut.Find(searchText, searchStart, searchEnd, RichTextBoxFinds.None);
					// Determine whether the text was found in richTextBox1.
					if(indexToText >= 0)
					{
						// Return the index to the specified search text.
						returnValue = indexToText;
					}
				}
			}
			return returnValue;
		}
		public int FindMyTextUp(string searchText, int searchStart, int searchEnd)
		{
			// Initialize the return value to false by default.
			int returnValue = -1;

			// Ensure that a search string and a valid starting point are specified.
			if (searchText.Length > 0 && searchStart >= 0)
			{
				// Ensure that a valid ending value is provided.
				if (searchEnd > searchStart || searchEnd == -1)
				{
					// Obtain the location of the search string in the richTextBox.
					int indexToText = richTextOut.Find(searchText, searchStart, searchEnd, RichTextBoxFinds.Reverse);
					// Determine whether the text was found in richTextBox1.
					if(indexToText >= 0)
					{
						// Return the index to the specified search text.
						returnValue = indexToText;
					}
				}
			}
			return returnValue;
		}

		private void buttonFind_Click(object sender, System.EventArgs e)
		{
			int found = -1;
			helpTextOut.Focus();				// focus on output text
			if (textBoxFind.Text != "")
				findSt = textBoxFind.Text;
			int length = findSt.Length;
			if (length == 0)
				return;
			int scope = helpTextOut.Text.Length;
			int start = helpTextOut.SelectionStart;
			int begin = Math.Min(start + 1, scope);
			if (begin != scope)
				found = FindMyText(findSt, begin, scope);
			if (found < 0)
			{
				int end = Math.Min(start + length, scope);
				found = FindMyText(findSt, 0, end);
				if (found >= 0)
				{
					Sound.Play("Wrap.wav");
				}
				else
				{
					Sound.Play("NotFound.wav");
				}
			}
			else
			{
				TryBeep(277, 50);					// beep Frequency 277Hz, Length 50ms
			}										// 277 is middle C#
			if (found >= 0)
			{
				helpTextOut.Select(found, length);	// move cursor found location
				helpTextOut.Focus();				// and focus
			}
		}

		private void buttonFindUp_Click(object sender, System.EventArgs e)
		{
			int found = -1;
			helpTextOut.Focus();				// focus on output text
			if (textBoxFind.Text != "")
				findSt = textBoxFind.Text;
			int length = findSt.Length;
			if (length == 0)
				return;
			int scope = helpTextOut.Text.Length;
			int start = helpTextOut.SelectionStart + length;
			int end = Math.Max(start - 1, 0);
			if (end != 0)
				found = FindMyTextUp(findSt, 0, end);
			if (found < 0)
			{
				int begin = Math.Max(start - length, 0);
				found = FindMyTextUp(findSt, begin, scope);
				if (found >= 0)
				{
					Sound.Play("Wrap.wav");
				}
				else
				{
					Sound.Play("NotFound.wav");
				}
			}
			else
			{
				TryBeep(277*2, 50);					// beep Frequency 554Hz, Length 50ms
			}										// 554 is one octave above middle C#
			if (found >= 0)
			{
				helpTextOut.Select(found, length);	// move cursor found location
				helpTextOut.Focus();				// and focus
			}
		}

		private void TryBeep(int freq, int dur)
		{
			try
			{
				Helpers.Beep(freq, dur);			// beep freq, duration
			}
			catch
			{
				// DllImport("kernel32.dll") file not found or no Beep entry point in the file
				// so no beep
			}
		}

		private void timerHelp_Tick(object sender, System.EventArgs e)
		{
			if (!Global.runFormUp)
			{
				Global.helpUp--;
				Dispose(true);
			}
		}

		private void HelpForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			Global.helpUp--;
		}

		private readonly RichTextBox helpTextOut;
		private string findSt;		// string to find
		private string HelpFile;	// file name of Help file
	}
}
