using System;
using System.Collections;
using System.IO;
using System.Web;
 
namespace SemWeb {
	public class ParserException : ApplicationException {
		public ParserException (string message) : base (message) {}
		public ParserException (string message, Exception cause) : base (message, cause) {}
	}

	public abstract class RdfReader : StatementSource, IDisposable {
		Entity meta = Statement.DefaultMeta;
		string baseuri = null;
		ArrayList warnings = new ArrayList();
		Hashtable variables = new Hashtable();
		bool reuseentities = false;
		NamespaceManager nsmgr = new NamespaceManager();

		public Entity Meta {
			get {
				return meta;
			}
			set {
				meta = value;
			}
		}
		
		public string BaseUri {
			get {
				return baseuri;
			}
			set {
				baseuri = value;
			}
		}
		
		public bool ReuseEntities {
			get {
				return reuseentities;
			}
			set {
				reuseentities = value;
			}
		}
		
		bool StatementSource.Distinct { get { return false; } }
		
		public NamespaceManager Namespaces { get { return nsmgr; } }
		
		public ICollection Variables { get { return variables.Keys; } }
		
		public IList Warnings { get { return ArrayList.ReadOnly(warnings); } }
		
		protected void AddVariable(Variable variable) {
			variables[variable] = variable;
		}

		public abstract void Select(StatementSink sink);
		
		public virtual void Dispose() {
		}
		
		public static RdfReader Create(string type, string source) {
			switch (type) {
				case "xml":
				case "text/xml":
					return new RdfXmlReader(source);
				case "n3":
				case "text/n3":
					return new N3Reader(source);
				default:
					throw new ArgumentException("Unknown parser type: " + type);
			}
		}
		
		public static RdfReader LoadFromUri(Uri webresource) {
			// TODO: Add Accept header for HTTP resources.
			
			System.Net.WebRequest rq = System.Net.WebRequest.Create(webresource);
			System.Net.WebResponse resp = rq.GetResponse();
			
			string mimetype = resp.ContentType;
			if (mimetype.IndexOf(';') > -1)
				mimetype = mimetype.Substring(0, mimetype.IndexOf(';'));
			
			switch (mimetype.Trim()) {
				case "text/xml":
				case "application/xml":
				case "application/rss+xml":
				case "application/rdf+xml":
					return new RdfXmlReader(resp.GetResponseStream());
					
				case "text/rdf+n3":
				case "application/n3":
				case "application/turtle":
				case "application/x-turtle":
					return new N3Reader(new StreamReader(resp.GetResponseStream(), System.Text.Encoding.UTF8));
			}
			
			if (webresource.LocalPath.EndsWith(".rdf") || webresource.LocalPath.EndsWith(".xml") || webresource.LocalPath.EndsWith(".rss"))
				return new RdfXmlReader(resp.GetResponseStream());
			
			if (webresource.LocalPath.EndsWith(".n3") || webresource.LocalPath.EndsWith(".ttl") || webresource.LocalPath.EndsWith(".nt"))
				return new N3Reader(new StreamReader(resp.GetResponseStream(), System.Text.Encoding.UTF8));

			throw new InvalidOperationException("Could not determine the RDF format of the resource.");
		}
		
		internal static TextReader GetReader(string file) {
			if (file == "-") return Console.In;
			return new StreamReader(file);
		}
		
		protected void OnWarning(string message) {
			warnings.Add(message);
		}
		
		internal string GetAbsoluteUri(string baseuri, string uri) {
			if (baseuri == null) {
				//if (uri == "")
				//throw new ParserException("An empty relative URI was found in the document but could not be converted into an absolute URI because no base URI is known for the document.");
				return uri;
			}
			if (uri.IndexOf(':') != -1) return uri;
			try {
				UriBuilder b = new UriBuilder(baseuri);
				b.Fragment = null; // per W3 RDF/XML test suite
				return new Uri(b.Uri, uri, true).ToString();
			} catch (UriFormatException e) {
				return baseuri + uri;
			}			
		}

	}
	
	internal class MultiRdfReader : RdfReader {
		private ArrayList parsers = new ArrayList();
		
		public ArrayList Parsers { get { return parsers; } }
		
		public override void Select(StatementSink storage) {
			foreach (RdfReader p in Parsers)
				p.Select(storage);
		}
	}
}

