/*
	$Id: TwitterAPI.cs 6 2009-12-26 00:58:57Z catwalk $
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using OAuthLib;

namespace Hiyoko.Twitter{
	/// <summary>
	/// TwitterAPIのプリミティブな関数群。
	/// </summary>
	public static class TwitterAPI{
		
		static TwitterAPI(){
			ServicePointManager.Expect100Continue = false;
		}
		
		#region API
		
		public static HttpWebResponse UpdateStatus(Account account, string status, decimal replyTo, string source){
			const string url = "http://twitter.com/statuses/update.xml";
			List<Parameter> prms = new List<Parameter>();
			prms.Add(new Parameter("status", HttpUtility.UrlEncode(status, Encoding.UTF8)));
			prms.Add(new Parameter("source", source));
			if(replyTo > 0){
				prms.Add(new Parameter("in_reply_to_status_id", replyTo.ToString()));
			}
			return Post(url, prms.ToArray(), account);
		}
		
		public static HttpWebResponse VerifyCredential(Account account){
			const string url = "http://twitter.com/account/verify_credentials";
			return Get(url, new Parameter[0], account);
		}
		
		public static HttpWebResponse GetHomeTimeline(Account account, int count, int page, decimal sinceId, decimal maxId){
			const string url = "http://twitter.com/statuses/home_timeline.xml";
			List<Parameter> prms = new List<Parameter>();
			if(count > 0){
				prms.Add(new Parameter("count", count.ToString()));
			}
			if(page > 0){
				prms.Add(new Parameter("page", page.ToString()));
			}
			if(sinceId > 0){
				prms.Add(new Parameter("since_id", sinceId.ToString()));
			}
			if(maxId > 0){
				prms.Add(new Parameter("max_id", maxId.ToString()));
			}
			return Get(url, prms.ToArray(), account);
		}
		
		public static HttpWebResponse GetUserTimeline(Account account, string id, int count, int page, decimal sinceId, decimal maxId){
			const string url = "http://twitter.com/statuses/user_timeline.xml";
			List<Parameter> prms = new List<Parameter>();
			if(!String.IsNullOrEmpty(id)){
				prms.Add(new Parameter("id", id));
			}
			if(count > 0){
				prms.Add(new Parameter("count", count.ToString()));
			}
			if(page > 0){
				prms.Add(new Parameter("page", page.ToString()));
			}
			if(sinceId > 0){
				prms.Add(new Parameter("since_id", sinceId.ToString()));
			}
			if(maxId > 0){
				prms.Add(new Parameter("maxId", maxId.ToString()));
			}
			return Get(url, prms.ToArray(), account);
		}
		
		public static HttpWebResponse DestroyStatus(Account account, decimal id){
			const string url = "http://twitter.com/statuses/destroy";
			Parameter[] prms = new Parameter[1]{new Parameter("id", id.ToString())};
			return Post(url + "/" + id.ToString() + ".xml", prms, account);
		}
		
		public static HttpWebResponse CreateFavorite(Account account, decimal id){
			const string url = "http://twitter.com/favorites/create";
			Parameter[] prms = new Parameter[1]{new Parameter("id", id.ToString())};
			return Post(url + "/" + id.ToString() + ".xml", prms, account);
		}
		
		public static HttpWebResponse CreateBlock(Account account, decimal id){
			const string url = "http://twitter.com/blocks/create";
			Parameter[] prms = new Parameter[1]{new Parameter("id", id.ToString())};
			return Post(url + "/" + id.ToString() + ".xml", prms, account);
		}
		
		public static HttpWebResponse DestroyFriendship(Account account, decimal id){
			const string url = "http://twitter.com/friendships/destroy";
			Parameter[] prms = new Parameter[1]{new Parameter("id", id.ToString())};
			return Post(url + "/" + id.ToString() + ".xml", prms, account);
		}
		
		public static HttpWebResponse SearchUsers(string searchWord, int count, int page){
			const string url = "http://twitter.com/users/search.xml";
			List<Parameter> prms = new List<Parameter>();
			prms.Add(new Parameter("q", HttpUtility.UrlEncode(searchWord, Encoding.UTF8)));
			if(count > 0){
				prms.Add(new Parameter("per_page", count.ToString()));
			}
			if(page > 0){
				prms.Add(new Parameter("count", page.ToString()));
			}
			return Get(url, prms.ToArray(), null);
		}
		
		#endregion
		
		#region 通信
		
		private static HttpWebResponse Get(string url, Parameter[] prms, Account account){
			HttpWebRequest req = (HttpWebRequest)WebRequest.Create(BuildUrl(url, prms));
			req.Method = "GET";
			req.KeepAlive = false;
			req.CookieContainer = null;
			req.Timeout = timeout;
			req.Proxy = proxy;
			if(account != null){
				req.PreAuthenticate = true;
				req.Credentials = new System.Net.NetworkCredential(account.Username, account.Password);
			}
			
			if(NetworkInterface.GetIsNetworkAvailable()){
				return (HttpWebResponse)req.GetResponse();
			}else{
				throw new WebException("Is not network available.", WebExceptionStatus.ConnectFailure);
			}
		}
		
		private static HttpWebResponse Post(string url, Parameter[] prms, Account account){
			string query = BuildQuery(prms);
			
			HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
			req.Method = "POST";
			req.ContentType = "application/x-www-form-urlencoded";
			req.ContentLength = query.Length;
			req.KeepAlive = false;
			req.CookieContainer = null;
			req.Timeout = timeout;
			req.Proxy = proxy;
			req.PreAuthenticate = true;
			req.Credentials = new System.Net.NetworkCredential(account.Username, account.Password);
			
			if(NetworkInterface.GetIsNetworkAvailable()){
				Stream reqStr = req.GetRequestStream();
				byte[] data = Encoding.ASCII.GetBytes(query);
				reqStr.Write(data, 0, data.Length);
				reqStr.Close();
				return (HttpWebResponse)req.GetResponse();
			}else{
				throw new WebException("Connection Failure", WebExceptionStatus.ConnectFailure);
			}
		}
		
		#region その他
		
		private static string BuildUrl(string url, Parameter[] prms){
			return (prms.Length > 0) ? url + "?" + BuildQuery(prms) : url;
		}
		
		private static string BuildQuery(Parameter[] prms){
			var query = from prm in prms
			            select prm.Name + "=" + prm.Value;
			return String.Join("&", query.ToArray());
		}
		
		public static bool TryParseDateTime(string s, out DateTime result){
			const string format = @"ddd MMM dd HH':'mm':'ss zz'00' yyyy";
			return DateTime.TryParseExact(
				s,
				format,
				System.Globalization.DateTimeFormatInfo.InvariantInfo,
				System.Globalization.DateTimeStyles.None,
				out result);
		}
		/*
		public static bool TryParseColor(string s, out Color color){
			try{
				int n = Convert.ToInt32(s, 16);
				byte r = (byte)((n & 0xff0000) >> 16);
				byte g = (byte)((n & 0x00ff00) >> 8);
				byte b = (byte)((b & 0x0000ff));
				color = Color.FromRgb(r, g, b);
				return true;
			}catch{
				return false;
			}
		}
		*/
		#endregion
		
		private static IWebProxy proxy;
		public static IWebProxy Proxy{
			get{
				return proxy;
			}
			set{
				proxy = value;
			}
		}
		
		public static int timeout = 10000;
		public static int Timeout{
			get{
				return timeout;
			}
			set{
				timeout = value;
			}
		}
		
		#endregion
	}
}