using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using WMPLib;

namespace MusicMetaTagger.UI
{
	/// <summary>
	/// </summary>
	/// <remarks>
	///   Attribute Reference http://msdn2.microsoft.com/en-us/library/bb248367.aspx
	/// </remarks>
	public class MediaPlayerLibraryInterface : IMusicLibraryInterface
	{
		#region Private members

		/// <summary>
		///   Relates TrackQuery object to the
		/// </summary>
		private Dictionary<TrackQueryStatus, IWMPMedia> _trackQueryTracks;

		/// <summary>
		///   Media player interface object (COM)
		/// </summary>
		private WindowsMediaPlayer _windowsMediaPlayer;

		#endregion

		#region Private methods

		/// <summary>
		///   Gets the IWMPMedia track from the associated TrackQueryStatus.
		/// </summary>
		/// <param name = "trackQueryStatus">The track query status.</param>
		/// <returns></returns>
		private IWMPMedia GetTrack(TrackQueryStatus trackQueryStatus)
		{
			if (!_trackQueryTracks.ContainsKey(trackQueryStatus))
				throw new ApplicationException("Doesn't contain track" + trackQueryStatus.TrackTitle);
			return _trackQueryTracks[trackQueryStatus];
		}

		#endregion

		#region IMusicLibraryInterface methods

		/// <summary>
		///   Gets the track queries.
		/// </summary>
		/// <param name = "libraryQuery">The library query.</param>
		/// <param name = "callback">The callback.</param>
		/// <returns></returns>
		public List<TrackQueryStatus> GetTrackQueries(LibraryQuery libraryQuery, IProgressCallback callback)
		{
			if (_trackQueryTracks == null)
				_trackQueryTracks = new Dictionary<TrackQueryStatus, IWMPMedia>();
			var trackQueries = new List<TrackQueryStatus>();

			callback.SetText("Opening Media Player Library");
			_windowsMediaPlayer = new WindowsMediaPlayer();
			var mediaCollection = _windowsMediaPlayer.mediaCollection;

			callback.SetText("Getting tracks");
			var playlist = mediaCollection.getAll();

			var trackCount = libraryQuery.ScanLimit <= 0 ? playlist.count : Math.Min(playlist.count, libraryQuery.ScanLimit);

			callback.SetRange(0, trackCount);
			callback.Begin();

			for (var i = 1; i <= trackCount; i++)
			{
				callback.Increment(1);
				if (i%5 == 0 || i == trackCount)
					callback.SetText(string.Format("Scanning track# {0} / {1}, Found {2} matches", i, trackCount, trackQueries.Count));

				if (callback.IsAborting)
					break;

				var track = playlist.Item[i];

				var trackQuery =
					new TrackQueryStatus
					{
						TrackTitle = track.getItemInfo("Title"),
						ArtistName = track.getItemInfo("Author"),
						AlbumTitle = track.getItemInfo("AlbumId")
					};
				var durationString = track.getItemInfo("Duration");
				if (string.IsNullOrEmpty(durationString))
					durationString = "1";
				trackQuery.TrackLength =
					new TimeSpan(0, 0, 0, Convert.ToInt32(Convert.ToDouble(durationString)));

				var originalIndexString = track.getItemInfo("OriginalIndex");
				if (!string.IsNullOrEmpty(originalIndexString))
					trackQuery.TrackNumber = Convert.ToInt32(originalIndexString);

				var acquisitionTime = Convert.ToDateTime(track.getItemInfo("AcquisitionTime"));

				if (libraryQuery.AddedAfter > acquisitionTime)
					continue;

				if (libraryQuery.BlankGenre && !string.IsNullOrEmpty(track.getItemInfo("Genre")))
					continue;

				if (libraryQuery.BlankRating && !string.IsNullOrEmpty(track.getItemInfo("UserRating")))
					continue;

				if (libraryQuery.BlankYear && !string.IsNullOrEmpty(track.getItemInfo("ReleaseDateYear")))
					continue;

				if (!libraryQuery.MatchTrackQuery(trackQuery))
					continue;

				trackQueries.Add(trackQuery);
				_trackQueryTracks.Add(trackQuery, track);
			}

			return trackQueries;
		}

		/// <summary>
		///   Updates the album art.
		/// </summary>
		/// <remarks>
		///   http://msdn2.microsoft.com/en-us/library/aa392196.aspx
		/// </remarks>
		/// <param name = "trackQuery">The track query.</param>
		/// <param name = "albumArt">The album art file info.</param>
		/// <param name = "overwrite">if set to <c>true</c> [overwrite].</param>
		/// <returns>True if update was successful, false otherwise</returns>
		public bool UpdateAlbumArt(TrackQueryStatus trackQuery, FileInfo albumArt, bool overwrite)
		{
			//IWMPMedia track = GetTrack(trackQuery);

			//IWMPMedia3 t = track as IWMPMedia3;
			//Object o = t.getItemInfoByType("WM/Picture", "", 0);
			//int [] ha;

			//WMPicture pic = new WMPicture();
			//Marshal.Copy(ha., 0, pic, );


			// http://www.ureader.com/message/33380790.aspx

			return false;
		}

		/// <summary>
		///   Updates the genre.
		/// </summary>
		/// <param name = "trackQuery">The track query.</param>
		/// <param name = "genre">The genre.</param>
		/// <param name = "overwrite">if set to <c>true</c> [overwrite].</param>
		/// <returns>True if update was successful, false otherwise</returns>
		public bool UpdateGenre(TrackQueryStatus trackQuery, string genre, bool overwrite)
		{
			var track = GetTrack(trackQuery);
			if (string.IsNullOrEmpty(track.getItemInfo("WM/Genre")) || overwrite)
			{
				track.setItemInfo("WM/Genre", genre);
				return true;
			}
			return false;
		}

		/// <summary>
		///   Updates the year.
		/// </summary>
		/// <param name = "trackQuery">The track query.</param>
		/// <param name = "year">The year.</param>
		/// <param name = "overwrite">if set to <c>true</c> [overwrite].</param>
		/// <returns>True if update was successful, false otherwise</returns>
		public bool UpdateYear(TrackQueryStatus trackQuery, int year, bool overwrite)
		{
			var track = GetTrack(trackQuery);
			if (track.isReadOnlyItem("WM/Year"))
				return false;
			if (string.IsNullOrEmpty(track.getItemInfo("WM/Year")) || overwrite)
			{
				track.setItemInfo("WM/Year", year.ToString(CultureInfo.InvariantCulture));
				return true;
			}
			return false;
		}

		/// <summary>
		///   Updates the rating.
		/// </summary>
		/// <param name = "trackQuery">The track query.</param>
		/// <param name = "rating">The rating from 0 to 100.</param>
		/// <param name = "overwrite">if set to <c>true</c> overwrite existing value.</param>
		/// <returns>True if update was successful, false otherwise</returns>
		public bool UpdateRating(TrackQueryStatus trackQuery, int rating, bool overwrite)
		{
			var track = GetTrack(trackQuery);
			if (Convert.ToInt32(track.getItemInfo("UserRating")) == 0 || overwrite)
			{
				track.setItemInfo("UserRating", rating.ToString(CultureInfo.InvariantCulture));
				return true;
			}
			return false;
		}

		public bool AddComment(TrackQueryStatus trackQuery, string comment)
		{
			// TODO: implement
			return false;
		}

		/// <summary>
		///   Returns true if the trackQuery represents an Mp3.
		/// </summary>
		/// <returns></returns>
		public bool IsMp3(TrackQueryStatus trackQuery)
		{
			var track = GetTrack(trackQuery);
			try
			{
				var trackFileInfo = new FileInfo(track.sourceURL);
				return trackFileInfo.Extension.ToUpper() == "MP3";
			}
			catch
			{
			}
			return false;
		}

		/// <summary>
		///   Returns the Mp3 represented by this trackQuery
		/// </summary>
		/// <param name = "trackQuery"></param>
		/// <returns></returns>
		public FileInfo GetMp3FileInfo(TrackQueryStatus trackQuery)
		{
			var track = GetTrack(trackQuery);
			try
			{
				return new FileInfo(track.sourceURL);
			}
			catch
			{
			}
			return null;
		}

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		private struct WMPicture
		{
			public readonly IntPtr pwszMIMEType;
			public readonly byte bPictureType;
			public readonly IntPtr pwszDescription;
			[MarshalAs(UnmanagedType.U4)] public readonly int dwDataLen;
			public readonly IntPtr pbData;
		}

		#endregion
	}
}