using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using MusicMetaTagger.Core.Utility;
using iTunesLib;

namespace MusicMetaTagger.UI
{
	public class ItunesLibraryInterface : IMusicLibraryInterface, IDisposable
	{
		private iTunesApp _iTunesApp;

		private Dictionary<TrackQueryStatus, IITTrack> _trackQueryTracks;

		/// <summary>
		///   Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		public void Dispose()
		{
			Marshal.ReleaseComObject(_iTunesApp);
			_iTunesApp = null;
			GC.Collect();
		}

		#region IMusicLibraryInterface Members

		/// <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, IITTrack>();
			var trackQueries = new List<TrackQueryStatus>();

			callback.SetText("Opening iTunes Library");
			if (_iTunesApp == null)
				_iTunesApp = new iTunesApp();

			callback.SetText("Getting tracks");
			var tracks = _iTunesApp.LibraryPlaylist.Tracks;

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

			callback.SetRange(0, trackCount);

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

				if (callback.IsAborting)
					break;

				var track = tracks[i];
				
				var trackQuery = new TrackQueryStatus
				                 	{
										TrackTitle = track.Name,
				                 		AlbumTitle = track.Album,
				                 		ArtistName = track.Artist,
				                 		TrackNumber = track.TrackNumber,
										TrackLength = StringUtility.ConvertTimeSpan(track.Time),
				                 		AlbumYear = track.Year,
										AlbumPartOfCompilation = track.Compilation,
										AlbumGenre = track.Genre//,
										//AlbumArtistName = track.AlbumArtist
				                 	};


				if (libraryQuery.AddedAfter > track.DateAdded)
					continue;

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

				if (libraryQuery.BlankRating && track.Rating > 0)
					continue;

				if (libraryQuery.BlankYear && track.Year > 0)
					continue;

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

				// skip pod casts
				if (track.Genre == "Podcast" || track.Genre == "Voice Memo" ) // || track.VideoKind != 0 || track.Podcast)
					continue;

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

			return trackQueries;
		}

		/// <summary>
		///   Updates the album art.
		/// </summary>
		/// <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)
		{
			var iTrack = GetTrack(trackQuery);
			if (albumArt == null)
				return false;
			if (iTrack.Artwork.Count == 0 || overwrite)
			{
				if (overwrite && iTrack.Artwork.Count > 0)
				{
					while (iTrack.Artwork.Count > 0)
						iTrack.Artwork[1].Delete();
				}
				iTrack.AddArtworkFromFile(albumArt.FullName);
				return true;
			}
			return false;
		}

		/// <summary>
		///   Tries to update 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 iTrack = GetTrack(trackQuery);
			if (string.IsNullOrEmpty(iTrack.Genre) || overwrite)
			{
				try
				{
					iTrack.Genre = genre;
					return true;
				}
				catch
				{
				}
			}
			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 iTrack = GetTrack(trackQuery);
			if (iTrack.Year == 0 || overwrite)
			{
				iTrack.Year = year;
				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 iTrack = GetTrack(trackQuery);
			if (iTrack.Rating == 0 || overwrite)
			{
				iTrack.Rating = rating;
				return true;
			}
			return false;
		}

		public bool AddComment(TrackQueryStatus trackQuery, string comment)
		{
			var iTrack = GetTrack(trackQuery);
			if (iTrack.Comment == null)
			{
				iTrack.Comment = comment;
				return true;
			}
			if (!iTrack.Comment.Contains(comment))
			{
				iTrack.Comment += comment;
				return true;
			}
			return false;
		}

		/// <summary>
		///   Returns true if the trackQuery represents an Mp3.
		/// </summary>
		/// <returns></returns>
		public bool IsMp3(TrackQueryStatus trackQuery)
		{
			var iTrack = GetTrack(trackQuery);
			return iTrack.KindAsString == "MPEG audio file";
		}

		/// <summary>
		///   Returns the Mp3 represented by this trackQuery
		/// </summary>
		/// <param name = "trackQuery"></param>
		/// <returns></returns>
		public FileInfo GetMp3FileInfo(TrackQueryStatus trackQuery)
		{
			var file = (IITFileOrCDTrack) GetTrack(trackQuery);
			return file.Location != null ? new FileInfo(file.Location) : null;
		}

		#endregion

		private IITTrack GetTrack(TrackQueryStatus trackQueryStatus)
		{
			if (!_trackQueryTracks.ContainsKey(trackQueryStatus))
				throw new ApplicationException("Doesn't contain track" + trackQueryStatus.TrackTitle);
			return _trackQueryTracks[trackQueryStatus];
		}
	}
}