package jp.sourceforge.nicoro;

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.RandomAccessFile;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpMessage;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.Gravity;
import android.webkit.CookieManager;
import android.widget.Toast;

import static jp.sourceforge.nicoro.Log.LOG_TAG;;

/**
 * 関数群
 */
public class Util {
	private static final boolean DEBUG_LOGV = Release.IS_DEBUG && false;
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG && true;
	
	public static String getFirstHeaderValue(HttpMessage httpMessage, String name) {
		Header header = httpMessage.getFirstHeader(name);
		if (header != null) {
			return header.getValue();
		} else {
			return null;
		}
	}

	/**
	 * 指定パスのファイルからJSONを作成
	 * @param path JSONファイルのパス
	 * @return ファイルから生成したJSONObject。読み込み失敗時はnull
	 */
	public static JSONObject createJSONFromFile(String path) {
		FileReader reader = null;
		JSONObject json = null;
		try {
			reader = new FileReader(path);
			StringBuilder readString = new StringBuilder(1024);
			char[] buf = new char[1024];
			while (true) {
				int readLength = reader.read(buf, 0, buf.length);
				if (readLength < 0) {
					break;
				}
				readString.append(buf, 0, readLength);
			}
			json = new JSONObject(readString.toString());
		} catch (FileNotFoundException e) {
			// ファイルが無いときは問題なし
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, e.getMessage());
			}
		} catch (IOException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
		} catch (JSONException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.getMessage(), e);
				}
			}
		}
		return json;
	}

	/**
	 * JSONを指定パスのファイルに保存
	 * @param path
	 * @param json
	 * @return trueで成功、falseで失敗
	 */
	public static boolean saveJSONToFile(String path, JSONObject json) {
		FileWriter writer = null;
		boolean result = false;
		try {
			writer = new FileWriter(path);
			writer.write(json.toString());
			result = true;
		} catch (IOException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.getMessage(), e);
				}
			}
		}
		return result;
	}
	
	public static void logHeaders(String tag, Header[] headers) {
		for (Header header : headers) {
			Log.d(tag, Log.buf().append(header.getName()).append(": ")
					.append(header.getValue()).toString());
		}
	}
	
	public static void showErrorDialog(final Activity activity,
			String errorMessage, final boolean tryFinishActivity) {
		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
		alertDialogBuilder.setTitle("エラー")
			.setMessage(errorMessage)
			.setCancelable(true)
			.setNeutralButton("閉じる", new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					dialog.dismiss();
					if (tryFinishActivity) {
						activity.finish();
					}
				}
			})
			.show();
	}
	public static void showErrorDialog(final Activity activity,
			int errorMessageResID, final boolean tryFinishActivity) {
		showErrorDialog(activity,
				activity.getString(errorMessageResID),
				tryFinishActivity);
	}
	
	public static void showErrorToast(Context context, String errorMessage) {
    	Toast toast = Toast.makeText(context,
    			errorMessage,
    			Toast.LENGTH_LONG);
    	toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,
    			0, 50);
    	toast.show();
	}
	public static void showErrorToast(Context context, int errorMessageResID) {
		showErrorToast(context, context.getResources().getString(errorMessageResID));
	}

	public static void showInfoToast(Context context, String info) {
		// 流用
		showErrorToast(context, info);
	}
	public static void showInfoToast(Context context, int infoResID) {
		// 流用
		showErrorToast(context, context.getResources().getString(infoResID));
	}
	
	public static String getCookieValueFromManager(String urlBase, String name) {
		CookieManager cookieManager = CookieManager.getInstance();
		cookieManager.removeExpiredCookie();
		String cookie = cookieManager.getCookie(urlBase);
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, Log.buf().append("getCookieValueFromManager: urlBase=")
					.append(urlBase).append(" cookie=").append(cookie).toString());
		}
		if (cookie == null) {
			return null;
		}
		Matcher matcher = Pattern.compile("(" + name + "=[^;\\n\\r]+)").matcher(cookie);
		if (matcher.find()) {
			return matcher.group(1);
		}
		return null;
	}

	public static String getCookieValueFromManager(String urlBase, Matcher matcher) {
		CookieManager cookieManager = CookieManager.getInstance();
		cookieManager.removeExpiredCookie();
		String cookie = cookieManager.getCookie(urlBase);
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, Log.buf().append("getCookieValueFromManager: urlBase=")
					.append(urlBase).append(" cookie=").append(cookie).toString());
		}
		matcher.reset(cookie);
		if (matcher.find()) {
			return matcher.group(1);
		}
		return null;
	}
	
	public static DefaultHttpClient createHttpClient() {
		SchemeRegistry schemeRegistry = new SchemeRegistry();
		schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
		SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
		schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
	
		HttpParams httpParams = new BasicHttpParams();
		HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
		HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);
		DefaultHttpClient httpClient = new DefaultHttpClient(
				new ThreadSafeClientConnManager(httpParams, schemeRegistry),
				httpParams);
		return httpClient;
	}
	
	public static Matcher getMatcher(Matcher matcher, String pattern, CharSequence input) {
		if (matcher == null) {
			return Pattern.compile(pattern).matcher(input);
		} else {
			return matcher.reset(input);
		}
	}
	
	public static String getSingleLineDataFromAPI(
			DefaultHttpClient httpClient, String hostname, String uri,
			String cookie, String userAgent) throws ClientProtocolException, IOException {
		String result = null;
		HttpRequest httpRequest = new HttpGet(uri);
		httpRequest.addHeader("Cookie", cookie);
		if (userAgent != null) {
			httpRequest.setHeader("User-Agent", userAgent);
		}
		HttpResponse httpResponse = httpClient.execute(
				new HttpHost(hostname, 80),
				httpRequest
				);
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, "==========getSingleLineDataFromAPI httpResponse==========");
			Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
			Log.d(LOG_TAG, "==========httpResponse end==========");
		}
		if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, Log.buf().append(hostname).append(uri).append(" entity>>>").toString());
			}
			HttpEntity httpEntity = httpResponse.getEntity();
			result = "";
			LineNumberReader reader = null;
			try {
				reader = new LineNumberReader(new InputStreamReader(httpEntity.getContent()));
				while (true) {
					String line = reader.readLine();
					if (line == null) {
						break;
					}
					if (DEBUG_LOGD) {
						Log.d(LOG_TAG, line);
					}
					result += URLDecoder.decode(line, HTTP.UTF_8);
				}
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, "<<<entity end");
					Log.d(LOG_TAG, Log.buf().append("result=").append(result).toString());
				}
			} finally {
				if (reader != null) {
					reader.close();
				}
			}
		}
		return result;
	}

	public static String getFirstMatch(Matcher matcher) {
		if (matcher.find()) {
			return matcher.group(1);
		}
		return null;
	}

	public static String getFirstMatch(String input, String patternString) {
		Matcher matcher = Pattern.compile(patternString).matcher(input);
		return getFirstMatch(matcher);
	}
	
	public static int readComplete(InputStream in, byte[] b, int offset, int length) throws IOException {
		int readSize = 0;
		while (readSize < length) {
			int ret = in.read(b, offset + readSize, length - readSize);
			if (ret < 0) {
				return ret;
			}
			readSize += ret;
		}
		assert readSize == length;
		return readSize;
	}
	public static int readComplete(InputStream in, byte[] b) throws IOException {
		return readComplete(in, b, 0, b.length);
	}
	
	public static int readComplete(RandomAccessFile in, byte[] b, int offset, int length) throws IOException {
		int readSize = 0;
		while (readSize < length) {
			int ret = in.read(b, offset + readSize, length - readSize);
			if (ret < 0) {
				return ret;
			}
			readSize += ret;
		}
		assert readSize == length;
		return readSize;
	}
	public static int readComplete(RandomAccessFile in, byte[] b) throws IOException {
		return readComplete(in, b, 0, b.length);
	}
	
	public static int readByte(InputStream in) throws EOFException, IOException {
		int temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readByte");
		}
		return temp;
	}
	public static int readShortLE(InputStream in) throws EOFException, IOException {
		int temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readShortLE");
		}
		int ret = (temp & 0xff);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readShortLE");
		}
		ret |= ((temp & 0xff) << 8);
		
		return ret;
	}
	public static int readIntLE(InputStream in) throws EOFException, IOException {
		int temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		int ret = (temp & 0xff);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		ret |= ((temp & 0xff) << 8);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		ret |= ((temp & 0xff) << 16);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		ret |= ((temp & 0xff) << 24);
		
		return ret;
	}

	public static int readByte(RandomAccessFile in) throws EOFException, IOException {
		int temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readByte");
		}
		return temp;
	}
	public static int readShortLE(RandomAccessFile in) throws EOFException, IOException {
		int temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readShortLE");
		}
		int ret = (temp & 0xff);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readShortLE");
		}
		ret |= ((temp & 0xff) << 8);
		
		return ret;
	}
	public static int readIntLE(RandomAccessFile in) throws EOFException, IOException {
		int temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		int ret = (temp & 0xff);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		ret |= ((temp & 0xff) << 8);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		ret |= ((temp & 0xff) << 16);
		temp = in.read();
		if (temp < 0) {
			throw new EOFException("Util#readIntLE");
		}
		ret |= ((temp & 0xff) << 24);
		
		return ret;
	}
	
	/**
	 * 型推論を使用したHashMapの生成
	 * @param <K>
	 * @param <V>
	 * @return
	 */
	public static <K, V> HashMap<K, V> newHashMap() {
		return new HashMap<K, V>();
	}
	/**
	 * 型推論を使用したHashMapの生成
	 * @param <K>
	 * @param <V>
	 * @param capacity	the initial capacity of this {@code HashMap}.
	 * @return
	 */
	public static <K, V> HashMap<K, V> newHashMap(int capacity) {
		return new HashMap<K, V>(capacity);
	}
	
	/**
	 * 型推論を使用したArrayListの生成
	 * @param <K>
	 * @param <V>
	 * @return
	 */
	public static <E> ArrayList<E> newArrayList() {
		return new ArrayList<E>();
	}
	/**
	 * 型推論を使用したArrayListの生成
	 * @param <K>
	 * @param <V>
	 * @param capacity	the initial capacity of this {@code ArrayList}.
	 * @return
	 */
	public static <E> ArrayList<E> newArrayList(int capacity) {
		return new ArrayList<E>(capacity);
	}
}
