// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
//
// Authors:
//	Chris Toshok (toshok@ximian.com)
//

using System;

namespace System.IO.Packaging {

	public static class PackUriHelper
	{
		public static readonly string UriSchemePack = "pack";
		static readonly Uri PackSchemeUri = new Uri("pack://", UriKind.Absolute);
		static readonly char[] _escapedChars = new char[] { '%', ',', '?', '@' };

		
		static PackUriHelper ()
		{
			if (!UriParser.IsKnownScheme (UriSchemePack))
				UriParser.Register (new PackUriParser (), UriSchemePack, -1);
		}
		
		public static int ComparePackUri (Uri firstPackUri, Uri secondPackUri)
		{
			if (firstPackUri == null)
				return secondPackUri == null ? 0 : -1;
			if (secondPackUri == null)
				return 1;

			Check.PackUriIsValid (firstPackUri);
			Check.PackUriIsValid (secondPackUri);

			// FIXME: What exactly is compared. Lets assume originalstring
			return firstPackUri.OriginalString.CompareTo (secondPackUri.OriginalString);
		}

		public static int ComparePartUri (Uri firstPartUri, Uri secondPartUri)
		{
			if (firstPartUri == null)
				return secondPartUri == null ? 0 : -1;
			if (secondPartUri == null)
				return 1;

			Check.PartUriIsValid (firstPartUri);
			Check.PartUriIsValid (secondPartUri);

			return firstPartUri.OriginalString.CompareTo (secondPartUri.OriginalString);
		}

		public static Uri Create (Uri packageUri)
		{
			return Create (packageUri, null, null);
		}

		public static Uri Create (Uri packageUri, Uri partUri)
		{
			return Create (packageUri, partUri, null);
		}

		public static Uri Create (Uri packageUri, Uri partUri, string fragment)
		{
			Check.PackageUri (packageUri);
			Check.PackageUriIsValid (packageUri);
			
			if (partUri != null)
				Check.PartUriIsValid (partUri);
			
			if (fragment != null && (fragment.Length == 0 || fragment[0] != '#'))
				throw new ArgumentException ("Fragment", "Fragment must not be empty and must start with '#'");

			// FIXME: Validate that partUri is a valid one? Must be relative, must start with '/'

			// First replace the slashes, then escape the special characters
			//string orig = packageUri.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped);
			string orig = packageUri.OriginalString;

			foreach (var ch in _escapedChars)
			{
				orig = !orig.Contains(ch.ToString()) ? orig : orig.Replace(ch.ToString(), Uri.HexEscape(ch));
			}

			orig = orig.Replace('/', ',');

			if (partUri != null)
				orig += partUri.OriginalString;

			if ((fragment == null && partUri == null)&& orig[orig.Length - 1] != '/')
				orig += '/';

			if (fragment != null)
				orig += fragment;
			
			return new Uri ("pack://" + orig);
		}

		public static Uri CreatePartUri (Uri partUri)
		{
			Check.PartUri (partUri);
			
			if (partUri.OriginalString[0] != '/')
				partUri = new Uri("/" + partUri.ToString (), UriKind.Relative);
			return partUri;
		}

		public static Uri GetNormalizedPartUri (Uri partUri)
		{
			Check.PartUri (partUri);
			return new Uri (partUri.ToString ().ToUpperInvariant (), UriKind.Relative);
		}

		public static Uri GetPackageUri (Uri packUri)
		{
			Check.PackUri (packUri);
			Check.PackUriIsValid (packUri);

			string s = packUri.Host.Replace(',', '/');
			return new Uri (Uri.UnescapeDataString(s), UriKind.RelativeOrAbsolute);
		}

		public static Uri GetPartUri (Uri packUri)
		{
			Check.PackUri(packUri);
			Check.PackUriIsValid(packUri);

			if (string.IsNullOrEmpty(packUri.AbsolutePath) || packUri.AbsolutePath == "/")
				return null;

			return new Uri(packUri.AbsolutePath, UriKind.Relative);
		}

		public static Uri GetRelationshipPartUri (Uri partUri)
		{
			Check.PartUri (partUri);
			Check.PartUriIsValid (partUri);
			
			int index = partUri.OriginalString.LastIndexOf ("/");
			string s = partUri.OriginalString.Substring (0, index);
			s += "/_rels" + partUri.OriginalString.Substring (index) + ".rels";
			return new Uri (s, UriKind.Relative);
		}

		public static Uri GetRelativeUri (Uri sourcePartUri, Uri targetPartUri)
		{
			Check.SourcePartUri (sourcePartUri);
			Check.TargetPartUri (targetPartUri);

			Uri uri = new Uri ("http://fake.com");
			Uri a = new Uri (uri, sourcePartUri.OriginalString);
			Uri b = new Uri (uri, targetPartUri.OriginalString);

			return a.MakeRelativeUri(b);
		}

		public static Uri GetSourcePartUriFromRelationshipPartUri (Uri relationshipPartUri)
		{
			//Check.RelationshipPartUri (relationshipPartUri);
			if (!IsRelationshipPartUri (relationshipPartUri))
				throw new Exception  ("is not a relationship part!?");
			return null;
		}

		public static bool IsRelationshipPartUri (Uri partUri)
		{
			Check.PartUri (partUri);
			return partUri.OriginalString.StartsWith ("/_rels") && partUri.OriginalString.EndsWith (".rels");
		}

		public static Uri ResolvePartUri (Uri sourcePartUri, Uri targetUri)
		{
			Check.SourcePartUri (sourcePartUri);
			Check.TargetUri (targetUri);
			
			Check.PartUriIsValid (sourcePartUri);
			if (targetUri.IsAbsoluteUri)
				throw new ArgumentException ("targetUri", "Absolute URIs are not supported");

			Uri uri = new Uri ("http://fake.com");
			uri = new Uri (uri, sourcePartUri);
			uri = new Uri (uri, targetUri);

			// Trim out 'http://fake.com'
			return new Uri (uri.OriginalString.Substring (15), UriKind.Relative); 
		}
	}

}