//
// RenameMoveCopy.cs: Renames, moves or copies one or more images,
//			with possible its different versions.
//
// Fixes F-Spot enhancement bug 302566 
//	(http://bugzilla.gnome.org/show_bug.cgi?id=302566)
//
// Author:
//   Bengt Thuree (bengt@thuree.com)
//

using System.IO;
using System; 
using Gtk;


namespace FSpot {
	public class RenameMoveCopy : GladeDialog {
		IBrowsableCollection selection;
		PhotoStore store;

		[Glade.Widget] private Gtk.RadioButton rename_radio;
		[Glade.Widget] private Gtk.RadioButton move_radio;
		[Glade.Widget] private Gtk.RadioButton copy_radio;
		[Glade.Widget] private Gtk.Entry move_target_entry;
		[Glade.Widget] private Gtk.Button move_target_folderpopup_button;
		[Glade.Widget] private Gtk.Entry copy_target_entry;
		[Glade.Widget] private Gtk.Button copy_target_folderpopup_button;

		[Glade.Widget] private Gtk.RadioButton no_subdir_radio;
		[Glade.Widget] private Gtk.RadioButton year_subdir_radio;
		[Glade.Widget] private Gtk.RadioButton year_month_subdir_radio;

		[Glade.Widget] private Gtk.Label preview_original_name_text;
		[Glade.Widget] private Gtk.Label preview_new_name_text;
		[Glade.Widget] private Gtk.Entry pattern_name_entry;

		[Glade.Widget] private Gtk.RadioButton no_number_serie_radio;
		[Glade.Widget] private Gtk.RadioButton original_number_serie_radio;
		[Glade.Widget] private Gtk.RadioButton new_number_serie_radio;
		[Glade.Widget] private Gtk.Entry new_number_serie_start_entry;
		[Glade.Widget] private Gtk.Label padd_to_label;
		[Glade.Widget] private Gtk.RadioButton padd_1_radio;
		[Glade.Widget] private Gtk.RadioButton padd_01_radio;
		[Glade.Widget] private Gtk.RadioButton padd_001_radio;
		[Glade.Widget] private Gtk.RadioButton padd_0001_radio;
		[Glade.Widget] private Gtk.RadioButton padd_00001_radio;
		
		System.Threading.Thread command_thread;
		ThreadProgressDialog progress_dialog;
		ProgressItem progress_item;

		int photo_index;
		DateTime sampleTime;
		string NewNamePattern = "";
		string CommandStr = "";
		string targetdir = "";
		bool MoveCopy = false;

		public RenameMoveCopy (PhotoStore store, IBrowsableCollection selection) : base ("rename_dialog")
		{
			this.selection = selection;
			this.store = store;

			Dialog.Modal = false;
			Dialog.TransientFor = null;

			// We just need the first photo, so exit directly.
			foreach (IBrowsableItem photo in selection.Items) {
				preview_original_name_text.Text = System.IO.Path.GetFileNameWithoutExtension (photo.Name);
				sampleTime = photo.Time; // This is for the pattern sample.
				break;					
			}
			
//			preview_new_name_text.Text = preview_original_name_text.Text;
			pattern_name_entry.Text = "<orig>";
			targetdir = System.Environment.GetEnvironmentVariable ("HOME");
			move_target_entry.Text = targetdir;			
			copy_target_entry.Text = targetdir;

			UpdateNewNameSample();

			Dialog.ShowAll ();
			Dialog.Response += HandleResponse;
		}

		void SelectTargetDirectory (bool move_activity)
		{		
			CompatFileChooserDialog file_selector =
				new CompatFileChooserDialog (Mono.Posix.Catalog.GetString ("Select Destination"), 
							     this.Dialog, CompatFileChooserDialog.Action.SelectFolder);
			
			if (move_activity)
				file_selector.Filename = move_target_entry.Text;
			else
				file_selector.Filename = copy_target_entry.Text;
			if (file_selector.Filename == "")
				file_selector.Filename = targetdir;

			int result = file_selector.Run ();
			
			if ((ResponseType)result == ResponseType.Ok)
				if (move_activity)
					move_target_entry.Text = file_selector.Filename;
				else
					copy_target_entry.Text = file_selector.Filename;
				
			file_selector.Destroy ();
		}

		string paddedNewNumber (int newNumber)
		{
			if (padd_01_radio.Active)
				return (String.Format) ("{0:00}", newNumber);
			if (padd_001_radio.Active)
				return (String.Format) ("{0:000}", newNumber);
			if (padd_0001_radio.Active)
				return (String.Format) ("{0:0000}", newNumber);
			if (padd_00001_radio.Active)
				return (String.Format) ("{0:00000}", newNumber);
			return (String.Format) ("{0}", newNumber);
		}
		
		string paddedNewNumber (string newNumber)
		{
			try {
				return paddedNewNumber (Int32.Parse(newNumber));
			}   
			catch  (Exception e) {
				HigMessageDialog md = new HigMessageDialog (	Dialog, 
														DialogFlags.DestroyWithParent,
														MessageType.Error,
														ButtonsType.Close, 
														"Error converting start number",
														"Please re-enter the start number, and ensure that you type a valid number (0-99999)");
				int result = md.Run();
				if (result==0) // Just to get rid of warning message 
					{}
				md.Destroy();
				new_number_serie_start_entry.Text = "";
			}
			return "";
		}
		
		bool IsThisCharNumber (string str)
		{
			try {
				return (Int16.Parse(str) >= 0);	// any number is >= 0 --> return true
			}   
			catch { // If not a number, an error will be thrown and we return false.
				return false;
			}
		}
		
		
		// This method will return the last digits in the filename (without extension)
		// for example "filename-1234" --> returns "1234"
		string getOriginalNumberStr (string origNameNoExt)
		{
			if (origNameNoExt.Length == 0) {
				return "";
			}
			int end_pos = origNameNoExt.Length-1;
			int start_pos = end_pos;
			if (! IsThisCharNumber (origNameNoExt.Substring(end_pos,1))) {
				return "";
			}
			while ((start_pos > 0) && (IsThisCharNumber (origNameNoExt.Substring(start_pos - 1,1)))) {
				start_pos = start_pos -1;
			}
			return origNameNoExt.Substring(start_pos, end_pos - start_pos + 1);
		}
		
		string GetNextPatternToken (string tmpPattern, int _start_pos)
		{
			int NextTokenPos;
			string _ReturnToken = "";
			bool cont = true;
			// If empty string
			if (_start_pos == tmpPattern.Length)
				return _ReturnToken;
			
			// if starts with "<"
			if (cont && tmpPattern[_start_pos] == '<') {
				NextTokenPos=tmpPattern.IndexOf('>', _start_pos);
				if (NextTokenPos==-1) {	// do not contain end ">", so return all 
					_ReturnToken = tmpPattern.Substring(_start_pos);
					return _ReturnToken;
				}
				// return the token contained by "<" and ">"
				_ReturnToken = tmpPattern.Substring(_start_pos, NextTokenPos - _start_pos+1);
				return _ReturnToken;
			} // end starts with "<"
			
			NextTokenPos=tmpPattern.IndexOf('<', _start_pos);
			if (NextTokenPos==-1) { // No more start token "<" so return all
				_ReturnToken = tmpPattern.Substring(_start_pos);
				return _ReturnToken;
			}
			// Return all up to next start token "<"
			_ReturnToken = tmpPattern.Substring(_start_pos, NextTokenPos - _start_pos);
			return _ReturnToken;
		}

		string NewNameFromTarget (string _origNameNoExt, string _orignumber, int new_number, DateTime timeinfo)
		{
			int _current_pos = 0;
			int OriginalNumberLength = _orignumber.Length;
			int OriginalNameLength = _origNameNoExt.Length;
			string _origNameNoNumber = _origNameNoExt.Substring (0, OriginalNameLength - OriginalNumberLength);

			string NewName="";
			string NextPatternToken="";
			string tmpPattern=pattern_name_entry.Text;
			while ( _current_pos < tmpPattern.Length ) {
				NextPatternToken = GetNextPatternToken (tmpPattern, _current_pos);
				_current_pos += NextPatternToken.Length;
				switch (NextPatternToken) {
				case "<YYYY>" 	: 	NewName += timeinfo.ToString("yyyy"); break;
				case "<YY>" 	: 	NewName += timeinfo.ToString("yy"); break;			
				case "<MM>" 	: 	NewName += timeinfo.ToString("MM"); break;
				case "<DD>" 	: 	NewName += timeinfo.ToString("dd"); break;					
				case "<hh>" 	: 	NewName += timeinfo.ToString("hh"); break;
				case "<mm>" 	: 	NewName += timeinfo.ToString("mm"); break;				
				case "<ss>" 	: 	NewName += timeinfo.ToString("ss"); break;
				case "<orig>" 	: 	NewName += _origNameNoNumber; break;					
				default:			NewName += NextPatternToken; break;
				}
			}
			if (original_number_serie_radio.Active)
				NewName += _orignumber;
			else if ((new_number_serie_radio.Active) && (new_number_serie_start_entry.Text != ""))
				NewName += paddedNewNumber (new_number);
			return NewName;
					
		}

		void UpdateNewNameSample ()
		{
			string NewName = "";
			string _newNumber = "0";
			if ((new_number_serie_radio.Active) && (new_number_serie_start_entry.Text != ""))
				_newNumber = paddedNewNumber (new_number_serie_start_entry.Text);	
			
			NewName = NewNameFromTarget (preview_original_name_text.Text, 
					getOriginalNumberStr (preview_original_name_text.Text), 
					Int32.Parse(_newNumber),
					sampleTime );

			preview_new_name_text.Text = NewName;
		}

		void AddToPattern (string addThis, bool clearPattern)
		{
			pattern_name_entry.Text += addThis;
			if (clearPattern)
				pattern_name_entry.Text = "";
		}

		void on_pattern_name_entry_changed (object sender, System.EventArgs args)
		{		
			UpdateNewNameSample ();
		}
		

		void HandleSelectTargetMoveDirectory (object sender, System.EventArgs args)
		{		
			if (move_radio.Active)
				SelectTargetDirectory(move_radio.Active);
		}

		void HandleSelectTargetCopyDirectory (object sender, System.EventArgs args)
		{		
			if (copy_radio.Active)
				SelectTargetDirectory(move_radio.Active);
		}
		

		void on_rename_radio_toggled (object sender, System.EventArgs args)
		{		
			no_subdir_radio.Active = true;
			no_subdir_radio.Sensitive = ! rename_radio.Active;
			year_subdir_radio.Sensitive = ! rename_radio.Active;
			year_month_subdir_radio.Sensitive = ! rename_radio.Active;
		}


		void on_move_radio_toggled (object sender, System.EventArgs args)
		{		
			move_target_entry.Sensitive = move_radio.Active;
			move_target_folderpopup_button.Sensitive = move_radio.Active;
			no_subdir_radio.Sensitive = ! rename_radio.Active;
			year_subdir_radio.Sensitive = ! rename_radio.Active;
			year_month_subdir_radio.Sensitive = ! rename_radio.Active;
		}

		void on_copy_radio_toggled (object sender, System.EventArgs args)
		{		
			copy_target_entry.Sensitive = copy_radio.Active;
			copy_target_folderpopup_button.Sensitive = copy_radio.Active;
			no_subdir_radio.Sensitive = ! rename_radio.Active;
			year_subdir_radio.Sensitive = ! rename_radio.Active;
			year_month_subdir_radio.Sensitive = ! rename_radio.Active;
		}
		
		void on_YYYY_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<YYYY>", false );
		}
		void on_YY_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<YY>", false );
		}
		void on_MM_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<MM>", false );
		}
		void on_DD_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<DD>", false );
		}
		void on_hh_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<hh>", false );
		}
		void on_mm_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<mm>", false );
		}
		void on_ss_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<ss>", false );
		}
		void on_dot_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( ".", false );
		}
		void on_underline_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "_", false );
		}
		void on_minus_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "-", false );
		}
		void on_space_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( " ", false );
		}
		void on_orig_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "<orig>", false );
		}
		void on_clear_button_clicked (object sender, System.EventArgs args)
		{		
			AddToPattern ( "", true );
		}
		
		void on_number_radio_toggled (object sender, System.EventArgs args)
		{		
			new_number_serie_start_entry.Sensitive = new_number_serie_radio.Active;
			padd_to_label.Sensitive = new_number_serie_radio.Active;
			padd_1_radio.Sensitive = new_number_serie_radio.Active;
			padd_01_radio.Sensitive = new_number_serie_radio.Active;
			padd_001_radio.Sensitive = new_number_serie_radio.Active;	
			padd_0001_radio.Sensitive = new_number_serie_radio.Active;	
			padd_00001_radio.Sensitive = new_number_serie_radio.Active;	
			UpdateNewNameSample ();
		}

		void on_new_number_serie_start_entry_changed (object sender, System.EventArgs args)
		{		
			UpdateNewNameSample ();
		}

		private void HandleProgressChanged (ProgressItem item)
		{
			//System.Console.WriteLine ("Changed value = {0}", item.Value);
			progress_dialog.Fraction = (photo_index - 1.0 + item.Value) / (double) selection.Count;	
		}
		

		private void DoWork() 
		{
			FSpot.ProgressItem Progress = new ProgressItem ();
			Progress.Changed += HandleProgressChanged;
			photo_index = 0;
			int NewNumberIndex = 0;
			if (new_number_serie_radio.Active)
				NewNumberIndex = Int32.Parse(new_number_serie_start_entry.Text);
			try {

				foreach (IBrowsableItem item in selection.Items) {
					Photo photo = (Photo) item;
					progress_dialog.Message = System.String.Format (
                     		                         Mono.Posix.Catalog.GetString ("\"{0}\" picture \"{1}\""), CommandStr, photo.Name);
					progress_dialog.Fraction = photo_index / (double) selection.Count;
					photo_index++;
					progress_dialog.ProgressText = System.String.Format (
								Mono.Posix.Catalog.GetString ("{0} of {1}"), photo_index, selection.Count);
								
					string origpath = photo.DirectoryPath;
					string origNameNoExt = Path.GetFileNameWithoutExtension (photo.Name);
					string origNumber = getOriginalNumberStr (origNameNoExt);
					string origDefaultVersion = photo.DefaultVersionPath;
					System.Console.WriteLine ("Name = {0}, Path = {1}, origNumber = {2}", origNameNoExt, origpath, origNumber );					
					string Newpath = origpath; // default (rename) same as original path
					if (copy_radio.Active || move_radio.Active)
						Newpath = targetdir;

					Newpath = Newpath + "/";	// Ensure the path ends with "/"
					// if create subdir, create them here
					if (year_subdir_radio.Active)  {
						string yearstr = photo.Time.ToString("yyyy");		
						Newpath = Newpath + "/" + yearstr + "/";
						Directory.CreateDirectory ( Newpath );
					} else							
						if (year_month_subdir_radio.Active) {
							string yearstr  = photo.Time.ToString("yyyy");		
							string monthstr = photo.Time.ToString("MM");						
							Newpath = Newpath + "/" + yearstr + "/" + monthstr + "/";
							Directory.CreateDirectory ( Newpath );
						}	
					
					if (copy_radio.Active) {

						string NewName = Newpath + NewNameFromTarget (origNameNoExt,
								origNumber, 
								NewNumberIndex, photo.Time) + Path.GetExtension (photo.Name);
						System.Console.WriteLine ("Copying {0} to {1}", origDefaultVersion, NewName );
						File.Copy (origDefaultVersion, NewName);
					} else {
							
							// Do the actual rename/move 						
							System.Console.WriteLine ("Moving/Renaming {0}", Newpath );
							string NewBase = Newpath + 
											NewNameFromTarget (origNameNoExt, 
												origNumber, 
												NewNumberIndex, 
												photo.Time);
							System.Console.WriteLine ("Moving/Renaming {0}, new base={1}", origNameNoExt, NewBase );
							photo.RenameFile (NewBase);
							store.Commit(photo);
						}

					NewNumberIndex++; // increase the number index...
				} // foreach photo
				progress_dialog.Message = System.String.Format (
						Mono.Posix.Catalog.GetString ("Done \"{0}\" Photos"), CommandStr);
				progress_dialog.Fraction = 1.0;
				progress_dialog.ProgressText = System.String.Format (
						Mono.Posix.Catalog.GetString ("\"{0}\" Complete"), CommandStr);
				progress_dialog.ButtonLabel = Gtk.Stock.Ok;

			} catch (System.Exception e) {
// FIXME
// HOW to recover if failure???
			
				progress_dialog.Message = e.ToString ();
				progress_dialog.ProgressText = System.String.Format (
					Mono.Posix.Catalog.GetString ("Error \"{0}\" pictures"), CommandStr);
			}
		}

		
		private void HandleResponse (object sender, Gtk.ResponseArgs args)
		{
			if (args.ResponseId != Gtk.ResponseType.Ok) {
				Dialog.Destroy ();
				return;
			}
			
			if (rename_radio.Active) {
				CommandStr = Mono.Posix.Catalog.GetString ("Renaming");
				MoveCopy = false;
			}
			if (copy_radio.Active) {
				CommandStr = Mono.Posix.Catalog.GetString ("Copying");
				targetdir = copy_target_entry.Text;
				MoveCopy = true;
			}
			if (move_radio.Active) {
				CommandStr = Mono.Posix.Catalog.GetString ("Moving");
				targetdir = move_target_entry.Text;
				MoveCopy = true;
			}

			if (MoveCopy && targetdir == "") {
				HigMessageDialog md = new HigMessageDialog (Dialog, 
									    Gtk.DialogFlags.Modal |
									    Gtk.DialogFlags.DestroyWithParent,
									    Gtk.MessageType.Error, Gtk.ButtonsType.Ok, 
									    Mono.Posix.Catalog.GetString ("Target directory is empty."),
									    Mono.Posix.Catalog.GetString ("To copy or move we have to have a target directory."));
				md.Run ();
				md.Destroy ();
				return;
			} else {

				command_thread = new  System.Threading.Thread (new System.Threading.ThreadStart (this.DoWork));

				command_thread.Name = CommandStr + Mono.Posix.Catalog.GetString (" pictures" );
			
				Dialog.Destroy ();
				progress_dialog = new FSpot.ThreadProgressDialog (command_thread, selection.Count);
				progress_dialog.Start ();
			}
		}
		
	} // RenameMoveCopy

} // NameSpace
