using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml;
using HtmlAgilityPack;
using MusicMetaTagger.Core.Model;

namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
{
	public static class WebXmlClient
	{
		/// <summary>
		///   Returns the HTML grabbed from the passed in URL.
		/// </summary>
		/// <param name="url"> The URL. </param>
		/// <returns> A string containing html </returns>
		public static string GetHtml(Uri url)
		{
			var tries = 0;
			while (true)
			{
				try
				{
					var client = new WebClient();
					using (var data = client.OpenRead(url))
					using (var reader = new StreamReader(data))
					{
						return reader.ReadToEnd();
					}
				}
				catch (Exception ex)
				{
					if (tries++ > 3)
						throw new WebException(ex.Message + " : " + url, ex);
					Thread.Sleep(100);
				}
			}
		}

		public static void SaveFile(Uri url, string filename)
		{
			var client = new WebClient();
			client.DownloadFile(url, filename);
		}

		/// <summary>
		///   Returns the HTML parsed into a standard XmlDocument. Uses the HtmlAgilityPack library for "out of the web" (poorly formatted) html file support.
		/// </summary>
		/// <param name="html"> The HTML </param>
		/// <returns> An XmlDocument from the HTML </returns>
		public static XmlDocument GetHtmlXml(string html)
		{
			// Sometimes the biography will have "<..." in it.  Annoying.
			html = html.Replace("<...", "");
			html = html.Replace("<class=\"subtitle\">", "");
			html = html.Replace("< ", "");

			var htmlDoc = new HtmlDocument {OptionOutputAsXml = true};
			htmlDoc.LoadHtml(html);
			// this element will have unencoded entities in an attribute, messing up the xml
			var selectSingleNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@class='rovi-share']");
			if (selectSingleNode != null)
				selectSingleNode.Remove();
			selectSingleNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='similar-albums']");
			if (selectSingleNode != null)
				selectSingleNode.Remove();
			// fix for ajax results having more than one root node
			if (htmlDoc.DocumentNode.ChildNodes.All(n => n.Name != "html"))
			{
				var el = htmlDoc.CreateElement("html");
				var nodes = htmlDoc.DocumentNode.ChildNodes;
				el.AppendChildren(nodes);
				htmlDoc.DocumentNode.RemoveAllChildren();
				htmlDoc.DocumentNode.AppendChild(el);
			}

			var xmlDocument = new XmlDocument();
			using (Stream stream = new MemoryStream())
			using (var xmlTextWriter = new XmlTextWriter(stream, Encoding.UTF8))
			{
				htmlDoc.Save(xmlTextWriter);

				stream.Seek(0, SeekOrigin.Begin);

				using (var streamReader = new StreamReader(stream))
					xmlDocument.Load(streamReader);
			}
			return xmlDocument;
		}

		/// <summary>
		///   Returns the HTML grabbed from the passed in URL parsed into a standard XmlDocument. Uses the HtmlAgilityPack library for "out of the web" html file support.
		/// </summary>
		/// <param name="url"> The URL. </param>
		/// <returns> An XmlDocument from the HTML </returns>
		public static XmlDocument GetHtmlXml(Uri url)
		{
			return GetHtmlXml(GetHtml(url));
		}

		// TODO: Refactor!
		/// <summary>
		///   Downloads the cover art to a local file. If the album doesn't have cover art, this will return null.
		/// </summary>
		/// <exception cref="HttpException">Thrown on HTTP error.</exception>
		/// <returns> </returns>
		public static FileInfo DownloadCoverArt(Album album)
		{
			if (String.IsNullOrEmpty(album.CoverUrl))
				return null;
			if (album.CoverUrl.Contains("no_cover"))
				return null;

			var directoryInfo = new DirectoryInfo(".\\ImageCache");
			if (!Directory.Exists(directoryInfo.FullName))
				directoryInfo.Create();

			var fileInfo = new FileInfo(directoryInfo.FullName + "\\" + album.AlbumId + ".jpg");

			if (!fileInfo.Exists)
				SaveFile(new Uri(album.CoverUrl), fileInfo.FullName);

			return fileInfo;
		}
	}
}