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 System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Hiyoko.Net{
	public abstract class WebUploader{
		public virtual string Upload(string filename){
			using(WebResponse res = this.RequestUpload(filename).WebRequest.GetResponse()){
				return this.Upload(res);
			}
		}
		public abstract string Upload(WebResponse response);
		public abstract WebRequestData RequestUpload(string filename);
		public abstract BitmapImage GetThumbnail(string url);
	}
	
	public class Twitpic : WebUploader{
		private const string UploadApiUrl = "http://twitpic.com/api/upload";
		private const string UploadAndPostUrl = "http://twitpic.com/api/uploadAndPost";
		
		private static Dictionary<string, string> fileContentTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		
		public string Username{get; private set;}
		public string Password{get; private set;}
		
		static Twitpic(){
			fileContentTypes.Add(".jpg", "image/jpeg");
			fileContentTypes.Add(".jpeg", "image/jpeg");
			fileContentTypes.Add(".png", "image/png");
			fileContentTypes.Add(".gif", "image/gif");
		}
		
		public Twitpic(){
		}
		
		public Twitpic(string username, string password){
			this.Username = username;
			this.Password = password;
		}
		
		public override string Upload(WebResponse response){
			using(StreamReader reader = new StreamReader(response.GetResponseStream())){
				XDocument doc = XDocument.Parse(reader.ReadToEnd());
				XElement rsp = doc.Element("rsp");
				if((string)rsp.Attribute("stat") == "ok"){
					return (string)rsp.Element("mediaurl");
				}else{
					int error = (int)rsp.Element("err").Attribute("code");
					switch(error){
						case 1001:
							throw new WebException("ユーザー名とパスワードが不正です。");
						case 1002:
							throw new WebException("画像が見つかりません。");
						case 1003:
							throw new WebException("画像が不正な形式です。");
						case 1004:
							throw new WebException("画像が4MBより大きいです。");
						default:
							throw new WebException("不明なエラーです。");
					}
				}
			}
		}
		
		public override WebRequestData RequestUpload(string filename){
			if(String.IsNullOrEmpty(this.Username)){
				throw new InvalidOperationException();
			}
			
			string fileContentType;
			if(!(fileContentTypes.TryGetValue(Path.GetExtension(filename), out fileContentType))){
				throw new ArgumentException();
			}
			
			byte[] binaryImageData = null;
			using(FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read)){
				binaryImageData = new byte[stream.Length];
				stream.Read(binaryImageData, 0, (int)stream.Length);
			}
			
			string boundary = Guid.NewGuid().ToString();
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create(UploadApiUrl);
			const string encoding = "iso-8859-1";

			request.PreAuthenticate = true;
			request.AllowWriteStreamBuffering = true;
			request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
			request.Method = "POST";

			string header = string.Format("--{0}", boundary);
			string footer = string.Format("--{0}--", boundary);

			StringBuilder contents = new StringBuilder();
			contents.AppendLine(header);

			string fileHeader = String.Format("Content-Disposition: file; name=\"{0}\"; filename=\"{1}\"", "media", filename);
			string fileData = Encoding.GetEncoding(encoding).GetString(binaryImageData);

			contents.AppendLine(fileHeader);
			contents.AppendLine(String.Format("Content-Type: {0}", fileContentType));
			contents.AppendLine();
			contents.AppendLine(fileData);

			contents.AppendLine(header);
			contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "username"));
			contents.AppendLine();
			contents.AppendLine(this.Username);

			contents.AppendLine(header);
			contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "password"));
			contents.AppendLine();
			contents.AppendLine(this.Password);

			contents.AppendLine(footer);

			byte[] data = Encoding.GetEncoding(encoding).GetBytes(contents.ToString());
			request.ContentLength = data.Length;
			
			return new WebRequestData(request, data);
		}
		
		public override BitmapImage GetThumbnail(string url){
			return this.GetThumbnail(url, false);
		}
		
		public BitmapImage GetThumbnail(string url, bool isMini){
			var match = Regex.Match(url, "http://twitpic.com/([^/]+)");
			if(match.Success){
				var uri = new Uri(
					((isMini) ? "http://twitpic.com/show/mini/" : "http://twitpic.com/show/thumb/") + match.Groups[1].Value);
				return new BitmapImage(uri);
			}else{
				return null;
			}
		}
	}
}