package com.usai.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Iterator;
import java.util.Set;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectTimeoutException;
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.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
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.HttpConnectionParams;
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 com.usai.apex.ApexTrackingApplication;
import android.content.Context;
import android.content.SharedPreferences.Editor;
import android.database.DatabaseUtils.InsertHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;

public class Network
{
	public static final int BEHAVIOR_SEARCH = 0;
	public static final int BEHAVIOR_RESULT = 1;

	private static final int REQUEST_TIMEOUT = 30 * 1000;// request time out 20
															// secs
	private static final int SO_TIMEOUT = 30 * 1000; // so time out 20 secs;
	public static int AP_USER_AUTH = 1;
	public static int AP_USER_NOT_AUTH = 2;
	public static int AP_USER_NOT_EXIST = 3;
	public static int AP_SESSION_EXPIRED = 4;

	public static int AP_UPLOAD_SUCCESS = 4;
	public static int AP_UPLOAD_FAIL = 5;
	//
	public static final int RESULT_FALSE = 0;
	public static final int RESULT_TRUE = -1;
	public static final int RESULT_NET_ERROR = -3;
	public static final int RESULT_NET_NOTAVAILABLE = -4;
	public static final int RESULT_ERROR = -5;
	public static final int RESULT_LOCALFILE_ERROR = -7;
	public static final int RESULT_USERAUTH_ERROR = -9;
	public static final int RESULT_UPDATE_USERAUTH_ERROR = -11;
	public static final int RESULT_SESSION_EXPIRED = -13;

	public static String URL_UPDATE_AUTH = "https://ra.apexshipping.com/login.php";
	public static String URL_REQUEST_COUNT = "https://ra.apexshipping.com/main.php";
	public static String URL_REQUEST_RECORDS = "https://ra.apexshipping.com/main.php";

	private static HttpClient getNewHttpClient()
	{
		try
		{
			KeyStore trustStore = KeyStore.getInstance(KeyStore
					.getDefaultType());
			trustStore.load(null, null);

			SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			HttpParams params = new BasicHttpParams();
			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
			HttpConnectionParams.setConnectionTimeout(params, REQUEST_TIMEOUT);
			HttpConnectionParams.setSoTimeout(params, SO_TIMEOUT);
			SchemeRegistry registry = new SchemeRegistry();
			registry.register(new Scheme("http", PlainSocketFactory
					.getSocketFactory(), 80));
			registry.register(new Scheme("https", sf, 443));

			ClientConnectionManager ccm = new ThreadSafeClientConnManager(
					params, registry);

			return new DefaultHttpClient(ccm, params);
		}
		catch (Exception e)
		{
			return new DefaultHttpClient();
		}
	}

	public static String getJson(String url, Bundle parms)
	{
		String TAG = "net_dbg@GetJson";
		Log.d(TAG, "entry");

		// if (true)
		// return fakegetJson(url);
		try
		{
			// BasicHttpParams httpParams = new BasicHttpParams();
			// HttpConnectionParams.setConnectionTimeout(httpParams,
			// REQUEST_TIMEOUT);
			// HttpConnectionParams.setSoTimeout(httpParams, SO_TIMEOUT);
			HttpClient client = getNewHttpClient();// new
													// DefaultHttpClient(httpParams);
			HttpPost post = new HttpPost(url);
			MultipartEntity reqEntity = new MultipartEntity(
					HttpMultipartMode.BROWSER_COMPATIBLE);

			Set<String> keys = parms.keySet();
			Log.d(TAG, "================parms============");
			for (String key : keys)
			{
				if (key.contains("_file"))
				{
					;
				}
				else
					reqEntity.addPart(key, new StringBody(parms.get(key)
							.toString()));
				Log.d(TAG, "key=" + key + "    val="
						+ parms.get(key).toString());
			}
			Log.d(TAG, "================parms============");

			post.setEntity(reqEntity);
			HttpResponse response = client.execute(post);
			int statucode = response.getStatusLine().getStatusCode();
			if (statucode == HttpStatus.SC_OK)
			{
				HttpEntity resEntity = response.getEntity();
				if (resEntity != null)
				{
					Log.d(TAG,
							"Response: content len==>"
									+ resEntity.getContentLength());
					InputStream is = resEntity.getContent();
					try
					{

						BufferedReader br = new BufferedReader(
								new InputStreamReader(is, "utf-8"), 8);
						StringBuilder sb = new StringBuilder();
						String line = null;
						while ((line = br.readLine()) != null)
						{
							sb.append(line + "\n");
						}

						Log.d(TAG, "Response: content begin");
						Log.d(TAG, sb.toString());
						Log.d(TAG, "Response: content end");

						if (sb.length() <= 0)
						{

							return null;
						}
						return sb.toString();

					}
					catch (Exception e)
					{
						Log.e(TAG, e.toString());
						return null;
						// TODO: handle exception
					}
					finally
					{
						is.close();

					}

				}
				else
				{
					/*
					 * resEntity is null
					 */
					Log.d(TAG, "RESPONSE ENTITY IS NULL");
					return null;
				}

			}
			else
			{
				/*
				 * Http error; out put error code;
				 */
				Log.d(TAG, "HTTP " + statucode);

				HttpEntity resEntity = response.getEntity();
				if (resEntity != null)
				{

					InputStream is = resEntity.getContent();
					try
					{

						BufferedReader br = new BufferedReader(
								new InputStreamReader(is, "utf-8"), 8);
						StringBuilder sb = new StringBuilder();
						String line = null;
						while ((line = br.readLine()) != null)
						{
							sb.append(line + "\n");
						}

						Log.d(TAG, "Response: content begin");
						Log.d(TAG, sb.toString());
						Log.d(TAG, "Response: content end");
						return null;
					}
					catch (Exception e)
					{
						Log.e(TAG, e.toString());
						return null;
						// TODO: handle exception
					}
					finally
					{
						is.close();

					}

				}
				else
				{
					/*
					 * resEntity is null
					 */
					Log.e(TAG, "RESPONSE ENTITY IS NULL");
					return null;
				}
			}

		}
		catch (ConnectTimeoutException e)
		{
			Log.d(TAG, "request time out");
			return null;

		}
		catch (Exception e)
		{
			Log.d(TAG, e.toString());
			return null;
		}

	}

	public static int get_recordcount(String name, String password, Bundle parms)
	{
		String TAG = "net_dbg@get_recordcount";

		parms.putString("action", "handset_search_count");
		parms.putString("sessionid", ApexTrackingApplication.get_sessionid());
		Log.d(TAG, "sessionid=" + ApexTrackingApplication.get_sessionid());
		String jstr = getJson(Network.URL_REQUEST_COUNT, parms);
		if (jstr == null || jstr.length() <= 0)
		{
			Log.d(TAG, "json is wrong");
			return Network.RESULT_NET_ERROR;
		}
		JSONObject jsobj;

		// array = new JSONArray(json);
		try
		{
			jsobj = new JSONObject(jstr);
			if (jsobj.length() > 0)
			{
				if (jsobj.getInt("result") != Network.AP_USER_AUTH)
				{
					// session expired
					Log.d(TAG,
							"USER NOT AUTHORIZED CODE="
									+ jsobj.getInt("result"));
					return RESULT_SESSION_EXPIRED;
					// if (get_Auth(name, password) == RESULT_TRUE)
					// {
					// return get_recordcount(name, password, parms);
					//
					// }
					// else
					// {
					//
					// Log.d(TAG,
					// "USER NOT AUTHORIZED CODE="
					// + jsobj.getInt("result"));
					// return RESULT_SESSION_EXPIRED;
					// }
				}
				else
				{
					return jsobj.getInt("count");
				}
			}
			else
			{
				Log.d(TAG, "json is wrong");
				return Network.RESULT_NET_ERROR;
			}
		}
		catch (JSONException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return RESULT_ERROR;
	}

	public static String get_records(String name, String password, Bundle parms)
	{
		String TAG = "net_dbg@get_records";
		// parms.putString("user", name);
		// parms.putString("password", password);
		// if (!Network.NetworkIsAvailable())
		// {
		// Log.d(TAG, "network not available!");
		// return "Network.RESULT_NET_NOTAVAILABLE"; // network not available
		// }
		parms.putString("action", "handset_search");
		parms.putString("sessionid", ApexTrackingApplication.get_sessionid());
		String jstr = getJson(Network.URL_REQUEST_RECORDS, parms);
		if (jstr == null || jstr.length() <= 0)
		{
			Log.d(TAG, "json is wrong");
			return null;
		}
		JSONObject jsobj;
		//
		// array = new JSONArray(json);
		try
		{
			jsobj = new JSONObject(jstr);
			if (jsobj.length() > 0)
			{
				if (jsobj.getInt("result") != Network.AP_USER_AUTH)
				{
					// session expired
					Log.d(TAG,
							"USER NOT AUTHORIZED CODE="
									+ jsobj.getInt("result"));
					return null;
					// if (get_Auth(name, password) == RESULT_TRUE)
					// {
					// return get_records(name, password, parms);
					//
					// }
					// else
					// {
					//
					// Log.d(TAG,
					// "USER NOT AUTHORIZED CODE="
					// + jsobj.getInt("result"));
					// return null;
					// }
				}
				else
				{
					return jstr;
				}
			}
			else
			{
				Log.d(TAG, "json is wrong");
				return null;
			}
		}
		catch (JSONException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

		// return RESULT_ERROR;
	}

	public static int get_Auth(String name, String password)
	{

		String TAG = "net_dbg@get_Auth";
		Log.d(TAG, "u:" + name + ";p:" + password);
		if (!Network.NetworkIsAvailable())
		{
			Log.d(TAG, "network not available!");
			return Network.RESULT_NET_NOTAVAILABLE; // network not available
		}
		Bundle parms = new Bundle();
		parms.putString("user", name);
		parms.putString("password", password);
		parms.putString("action", "handset_login");
		int ver = ApexTrackingApplication.get_instance()
				.getSharedPreferences("Apex_auth", Context.MODE_PRIVATE)
				.getInt("AuthInfoVer", 0);
		parms.putString("auth_ver", ver + "");
		String jstr = getJson(Network.URL_UPDATE_AUTH, parms);
		/*
		 * error occur while get authorization info from server. include can not
		 * reach server , wrong parms ,server get wrong , etc.
		 */
		if (jstr == null || jstr.length() <= 0)
		{
			Log.d(TAG, "json is wrong");
			return Network.RESULT_NET_ERROR;
		}

		return parse_authinfo(jstr, name);
	}

	private static int parse_authinfo(String json, String user)
	{
		String TAG = "net_dbg@parse_authinfo";
		Log.d(TAG, json);
		// JSONArray array;
		JSONObject jsobj;
		SQLiteDatabase db = null;
		try

		{

			// array = new JSONArray(json);
			jsobj = new JSONObject(json);
			if (jsobj.length() > 0)
			{
				// JSONObject objresult = jsobj.get.getJSONObject(0);// result
				// the
				// server result
				// ;
				if (jsobj.getInt("result") != Network.AP_USER_AUTH)
				{
					// user not authorized return
					Log.d(TAG,
							"USER NOT AUTHORIZED CODE="
									+ jsobj.getInt("result"));
					return RESULT_FALSE;
				}

				JSONObject objheader = jsobj.getJSONObject("header");
				// JSONObject objsessionid = jsobj.getJSONObject(1); // session
				// id
				// on
				// server;
				ApexTrackingApplication.put_sessionid(objheader
						.getString("sessionid"));
				Log.d(TAG, "sessionid=" + objheader.getString("sessionid"));
				// JSONObject objupdate = array.getJSONObject(3); // whether
				// need
				// update ;
				if (objheader.getBoolean("update") == false)
				{
					// no update on the server;
					return RESULT_TRUE;
				}
				// JSONObject objver = array.getJSONObject(4); // new version
				// get
				// from server;

				Editor editor = ApexTrackingApplication
						.get_instance()
						.getSharedPreferences("Apex_auth", Context.MODE_PRIVATE)
						.edit();

				editor.putInt("AuthInfoVer", objheader.getInt("ver"));
				JSONObject objfuncs = jsobj.getJSONObject("functions");
				Iterator it = objfuncs.keys();
				db = dbUtil.OpenDB(ApexTrackingApplication.get_instance(),
						null, true);
				String sql = "insert into fields_info(name,aname,field_type,function_name,behavior,priority,show,user) values(?,?,?,?,?,?,?,?)";
				db.execSQL("update fields_info set abandon = 1 where user ='"
						+ user + "'");
				SQLiteStatement stat = db.compileStatement(sql);
				db.beginTransaction();
				String field_name, field_type, alias_name;
				while (it.hasNext()) // loop for each function
				{
					String func_name = (String) it.next();

					// Set<String> funset = new HashSet<String>();
					JSONObject objfun = objfuncs.getJSONObject(func_name);
					int behavior, priority = 999;
					int show = 1;
					JSONObject objbehavior = objfun.getJSONObject("search");
					Iterator itbehavior = objbehavior.keys();
					behavior = BEHAVIOR_SEARCH;
					while (itbehavior.hasNext()) // loop for search fields in
													// certain function
					{
						String field = (String) itbehavior.next();
						JSONObject field_info = objbehavior
								.getJSONObject(field);
						field_name = field_info.getString("name");
						field_type = field_info.getString("type");
						alias_name = field_info.getString("alias");
						int id = dbUtil.get_recordid(db, "fields_info",
								"name='" + field_name + "' and behavior="
										+ behavior + " and function_name='"
										+ func_name + "' and user='" + user
										+ "'");
						if (id >= 0) // record exist;
						{
							db.execSQL("update fields_info set abandon = 0 where _id ="
									+ id);
						}
						else
						{
							stat.bindString(1, field_name);
							stat.bindString(2, alias_name);
							stat.bindString(3, field_type);
							stat.bindString(4, func_name);
							stat.bindLong(5, behavior);
							stat.bindLong(6, priority);
							stat.bindLong(7, show);
							stat.bindString(8, user);
							stat.executeInsert();
						}
					}

					objbehavior = objfun.getJSONObject("result");
					Iterator itresult = objbehavior.keys();
					behavior = BEHAVIOR_RESULT;
					while (itresult.hasNext()) // loop for result fields in
												// certain function
					{
						String field = (String) itresult.next();
						JSONObject field_info = objbehavior
								.getJSONObject(field);
						field_name = field_info.getString("name");
						field_type = field_info.getString("type");
						alias_name = field_info.getString("alias");
						int id = dbUtil.get_recordid(db, "fields_info",
								"name='" + field_name + "' and behavior="
										+ behavior + " and function_name='"
										+ func_name + "' and user='" + user
										+ "'");
						if (id >= 0) // record exist;
						{
							db.execSQL("update fields_info set abandon = 0 where _id ="
									+ id);
						}
						else
						{
							stat.bindString(1, field_name);
							stat.bindString(2, alias_name);
							stat.bindString(3, field_type);
							stat.bindString(4, func_name);
							stat.bindLong(5, behavior);
							stat.bindLong(6, priority);
							stat.bindLong(7, show);
							stat.bindString(8, user);
							stat.executeInsert();
						}
					}
					// editor.putString(func_name, objfun.toString());
					// JSONArray arrfun = jsobj.get(i);
					// JSONObject objname = arrfun.getJSONObject(0);
					//
					// funset.add(objfun.getString("name"));
					// funset.add(objfun.getJSONObject("search").toString());
					// funset.add(objfun.getJSONObject("result").toString());
					// editor.putStringSet(objfun.getString("name"), funset);
					// String value = obj.getString(key);
					// JSONArray array = obj.getJSONArray(key);
					// for (int i = 0; i < array.length(); i++)
					// {
					// JSONObject jsonobject = array.getJSONObject(i);
					// jsonobject.put("name", key);
					// jsonobject.put("exp",
					// key + "=" + jsonobject.getString("value"));
					// newArray.put(jsonobject);
					// }
				}
				db.execSQL("delete from fields_info where abandon = 1");
				db.setTransactionSuccessful();
				db.endTransaction();
				editor.commit();
				return RESULT_TRUE;
			}
			Log.d(TAG, "json is wrong");
			return RESULT_USERAUTH_ERROR;
		}
		catch (JSONException e1)
		{
			// TODO Auto-generated catch block
			e1.printStackTrace();
			Log.d(TAG, "json is wrong");
		}
		finally
		{
			dbUtil.CloseDB(db);
		}

		// try
		// {
		// m_RemoteVerCode = Integer
		// .parseInt(obj.getString("verCode"));
		// m_RemoteVerName = obj.getString("verName");
		// m_NewVerUrl = obj.getString("URL");
		// }
		// catch (Exception e)
		// {
		// m_RemoteVerCode = -1;
		// m_RemoteVerName = "";
		// m_NewVerUrl = "";
		// dbgUtil.Log(Log.INFO,TAG, "download failed json file wrong!");
		// return false;
		// }
		Log.d(TAG, "json is wrong");
		return RESULT_USERAUTH_ERROR;
	}

	// private void writeData(JSONObject allData)
	// {
	//
	// File file = new File(Environment.getExternalStorageDirectory()
	// .toString() + File.separator + "live.txt");
	// if (!file.exists())
	// {
	// try
	// {
	// file.createNewFile();
	// }
	// catch (IOException e)
	// {
	// e.printStackTrace();
	// }
	// }
	// FileOutputStream fos;
	// OutputStreamWriter osw = null;
	// try
	// {
	// fos = new FileOutputStream(file);
	//
	// osw = new OutputStreamWriter(fos, "UTF-8");
	// osw.write(allData.toString());
	// osw.flush();
	// }
	// catch (FileNotFoundException e)
	// {
	// e.printStackTrace();
	// }
	// catch (UnsupportedEncodingException e)
	// {
	// e.printStackTrace();
	// }
	// catch (IOException e)
	// {
	// e.printStackTrace();
	// }
	// finally
	// {
	// if (osw != null)
	// {
	// try
	// {
	// osw.close();
	// }
	// catch (IOException e)
	// {
	// e.printStackTrace();
	// }
	// }
	// }
	// }

	public static boolean NetworkIsAvailable()// Context context)
	{
		String TAG = "net_dbg@CheckNetwork";
		ConnectivityManager connManager = (ConnectivityManager) ApexTrackingApplication
				.get_instance().getSystemService(Context.CONNECTIVITY_SERVICE);
		// .getSystemService(Context.CONNECTIVITY_SERVICE);

		NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
		if (networkInfo == null)
		{
			Log.d(TAG, "can not get Active NetworkInfo!");
			// dbgUtil.Log(Log.DEBUG, "Current Network info",
			// "can not get Active NetworkInfo!");
			return false;
		}
		NetworkInfo.State netState = networkInfo.getState();
		if (netState != NetworkInfo.State.CONNECTED)
		{
			Log.d(TAG, "not Connected!State=" + netState);
			// dbgUtil.Log(Log.DEBUG, "Current Network info",
			// "not Connected!State=" + netState);
			return false;
		}
		// int iconntype = -1;
		// iconntype = networkInfo.getType();

		// boolean bUseMobileNetwork = context.getSharedPreferences(
		// "PhoneAsstPref", 0).getBoolean("UseMobileNetwork", false);
		//
		// if (bUseMobileNetwork == false
		// && iconntype != ConnectivityManager.TYPE_WIFI && iconntype != 9/*
		// earthnet */)
		// {
		// Log.d(TAG,);
		// dbgUtil.Log(Log.DEBUG, "Current Network info",
		// "not allowed!Connection type=" + networkInfo.getTypeName());
		// return false;
		// }
		boolean bavailable = networkInfo.isAvailable();
		String strtype = networkInfo.getTypeName();

		Log.d(TAG, " type = " + strtype + " abailable = " + bavailable
				+ " state " + netState);
		// dbgUtil.Log(Log.INFO, "Current Network info", " type = " + strtype
		// + " abailable = " + bavailable + " state " + netState);
		return bavailable;
	}

	public static class SSLSocketFactoryEx extends SSLSocketFactory
	{

		SSLContext sslContext = SSLContext.getInstance("TLS");

		public SSLSocketFactoryEx(KeyStore truststore)
				throws NoSuchAlgorithmException, KeyManagementException,
				KeyStoreException, UnrecoverableKeyException
		{
			super(truststore);

			TrustManager tm = new X509TrustManager()
			{
				public java.security.cert.X509Certificate[] getAcceptedIssuers()
				{
					return null;
				}

				@Override
				public void checkClientTrusted(
						java.security.cert.X509Certificate[] chain,
						String authType)
						throws java.security.cert.CertificateException
				{
				}

				@Override
				public void checkServerTrusted(
						java.security.cert.X509Certificate[] chain,
						String authType)
						throws java.security.cert.CertificateException
				{
				}
			};
			sslContext.init(null, new TrustManager[] { tm }, null);
		}

		@Override
		public Socket createSocket(Socket socket, String host, int port,
				boolean autoClose) throws IOException, UnknownHostException
		{
			return sslContext.getSocketFactory().createSocket(socket, host,
					port, autoClose);
		}

		@Override
		public Socket createSocket() throws IOException
		{
			return sslContext.getSocketFactory().createSocket();
		}
	}
}
