/*
	$Id: TwitterAPI.cs 14 2010-01-06 17:27:40Z catwalk $
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Hiyoko.Net.OAuth;

namespace Hiyoko.Net.Twitter{
	/// <summary>
	/// TwitterAPIのプリミティブな関数群。
	/// </summary>
	public static partial class TwitterAPI{
		
		// ConsumerKey、ConsumerSecretは非公開ファイルで定義。
		private static Consumer Consumer{get; set;}
		
		static TwitterAPI(){
			ServicePointManager.Expect100Continue = false;
			Consumer = new Consumer(ConsumerKey, ConsumerSecret);
		}
		
		#region API
		
		public static WebRequestData 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", status));
			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 WebRequestData VerifyCredential(Account account){
			const string url = "http://twitter.com/account/verify_credentials";
			return Get(url, new Parameter[0], account);
		}
		
		public static WebRequestData 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 WebRequestData 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 WebRequestData 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 WebRequestData 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 WebRequestData 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 WebRequestData 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 WebRequestData 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", searchWord));
			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 WebRequestData Get(string url, Parameter[] prms){
			HttpWebRequest req = GetWebRequest(url + ((prms.Length > 0) ? ("?" + Parameter.ConCat(prms)) : ""), prms);
			req.Method = "GET";
			
			return new WebRequestData(req);
		}
		
		private static WebRequestData Post(string url, Parameter[] prms){
			string query = Parameter.ConCat(prms);
			byte[] data = Encoding.ASCII.GetBytes(query);
			
			HttpWebRequest req = GetWebRequest(url, prms);
			req.Method = "POST";
			req.ContentType = "application/x-www-form-urlencoded";
			req.ContentLength = query.Length;
			
			return new WebRequestData(req, data);
		}
		
		private static WebRequestData Get(string url, Parameter[] prms, Account account){
			AccessToken accessToken = account.AccessToken;
			
			HttpWebRequest req = GetWebRequest(url + ((prms.Length > 0) ? ("?" + Parameter.ConCat(prms)) : ""), prms);
			req.Method = "GET";
			
			Consumer.AccessProtectedResource(accessToken, req, url, "http://twitter.com/", prms);
			return new WebRequestData(req);
		}
		
		private static WebRequestData Post(string url, Parameter[] prms, Account account){
			AccessToken accessToken = account.AccessToken;
			string query = Parameter.ConCat(prms);
			byte[] data = Encoding.ASCII.GetBytes(query);
			
			HttpWebRequest req = GetWebRequest(url, prms);
			req.Method = "POST";
			req.ContentType = "application/x-www-form-urlencoded";
			req.ContentLength = query.Length;
			
			Consumer.AccessProtectedResource(accessToken, req, url, "http://twitter.com/", prms);
			return new WebRequestData(req, data);
		}
		
		private static HttpWebRequest GetWebRequest(string url, Parameter[] prms){
			HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
			req.KeepAlive = false;
			req.CookieContainer = null;
			req.Timeout = timeout;
			req.Proxy = proxy;
			return req;
		}
		
		#region OAuth
		
		public static AccessToken GetAccessToken(RequestToken requestToken, string verifier){
			return Consumer.RequestAccessToken(
				verifier, requestToken, "http://twitter.com/oauth/access_token", "http://twitter.com/");
		}
		
		public static RequestToken ObtainUnauthorizedRequestToken(){
			return Consumer.ObtainUnauthorizedRequestToken("http://twitter.com/oauth/request_token", "http://twitter.com/");
		}
		
		public static string BuildUserAuthorizationURL(RequestToken reqToken){
			return Consumer.BuildUserAuthorizationURL("http://twitter.com/oauth/authorize", reqToken);
		}
		
		#endregion
		
		#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;
				Consumer.Proxy = value;
			}
		}
		
		private static int timeout = 10000;
		public static int Timeout{
			get{
				return timeout;
			}
			set{
				timeout = value;
			}
		}
		
		#endregion
	}
}