using System;
using System.Net;
using System.Linq;
using System.Xml.Linq;
using System.IO;
using System.Text;
using System.Web;
using System.Text.RegularExpressions;

namespace Hiyoko.Net{
	public abstract class UrlShorter{
		public virtual string Shorten(string longUrl){
			using(WebResponse res = RequireShorten(longUrl).WebRequest.GetResponse()){
				return this.Shorten(res);
			}
		}
		public abstract string Shorten(WebResponse res);
		public abstract WebRequestData RequireShorten(string longUrl);
		
		public virtual bool CanExpand(string shortUrl){
			return true;
		}
		public virtual string Expand(string shortUrl){
			using(WebResponse res = RequireExpand(shortUrl).WebRequest.GetResponse()){
				return this.Expand(res);
			}
		}
		public abstract string Expand(WebResponse res);
		public abstract WebRequestData RequireExpand(string shortUrl);
		
		public virtual string ShortenUrlsInText(string text){
			Regex regex = new Regex(@"[a-z]+:\/\/[-_.!~*\'()a-z0-9;\/?:\@&=+\$,%#]+", RegexOptions.IgnoreCase | RegexOptions.Singleline);
			return regex.Replace(text, delegate(Match match){
				string url = this.Shorten(match.Value);
				return (url != null) ? url : match.Value;
			});
		}
		
		public virtual string ExpandUrlsInText(string text){
			Regex regex = new Regex(@"[a-z]+:\/\/[-_.!~*\'()a-z0-9;\/?:\@&=+\$,%#]+", RegexOptions.IgnoreCase | RegexOptions.Singleline);
			return regex.Replace(text, delegate(Match match){
				string url = this.Expand(match.Value);
				return (url != null) ? url : match.Value;
			});
		}
	}
	
	public class Bitly : UrlShorter{
		public string Login{get; private set;}
		public string ApiKey{get; private set;}
		public static readonly string Version = "2.0.1";
		
		public Bitly(string login, string apiKey){
			this.Login = login;
			this.ApiKey = apiKey;
		}
		
		public override string Shorten(WebResponse res){
			string xmlString;
			using(Stream stream = res.GetResponseStream()){
				using(StreamReader reader = new StreamReader(stream, Encoding.UTF8)){
					xmlString = reader.ReadToEnd();
				}
			}
			XElement xml = XElement.Parse(xmlString);
			int errorCode = (int)xml.Element("errorCode");
			if(errorCode != 0){
				return null;
			}else{
				return (string)xml.Element("results").Element("nodeKeyVal").Element("shortUrl");
			}
		}
		
		public override WebRequestData RequireShorten(string longUrl){
			const string api = "http://api.bit.ly/shorten";
			
			HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(
				api +
				"?version=" + Version + "&login=" + this.Login + "&apiKey=" + this.ApiKey +
				"&format=xml&longUrl=" + HttpUtility.UrlEncode(longUrl)
			);
			req.KeepAlive = false;
			req.CookieContainer = null;
			req.Method = "GET";
			
			return new WebRequestData(req);
		}
		
		private Regex canExpandRegex = new Regex("^http(|s)://bit.ly/.+", RegexOptions.Compiled);
		public override bool CanExpand(string shortUrl){
			return this.canExpandRegex.IsMatch(shortUrl);
		}
		
		public override string Expand(WebResponse res){
			string json;
			using(Stream stream = res.GetResponseStream()){
				using(StreamReader reader = new StreamReader(stream, Encoding.UTF8)){
					json = reader.ReadToEnd();
				}
			}
			
			Match match = Regex.Match(json, "\"longUrl\"[ \t]*:[ \t]*\"([^\"]+)\"");
			if(match.Success){
				return match.Groups[1].Value;
			}else{
				return null;
			}
		}
		
		public override WebRequestData RequireExpand(string shortUrl){
			const string api = "http://api.bit.ly/expand";
			
			HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(
				api +
				"?version=" + Version + "&login=" + this.Login + "&apiKey=" + this.ApiKey +
				"&shortUrl=" + shortUrl
			);
			req.KeepAlive = false;
			req.CookieContainer = null;
			req.Method = "GET";
			
			return new WebRequestData(req);
		}
	}
}