package jp.sourceforge.nicoro;

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

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentLinkedQueue;

import jp.sourceforge.nicoro.MessageLoader.EventListener;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.protocol.HTTP;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Xml;

public class LiveMessageLoader extends XmlLoader {
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG && true;
	private static final boolean DEBUG_LOGD_PARSE = Release.IS_DEBUG && true;

	private static int DEFAULT_TIMEOUT_MS = 180 * 1000;

	private String mUrl;
	private String mPort;
	private String mThreadId;
	private String mCookie;
	private String mUserId;
	private Context mContext;

	private Socket mSocket;

	private ConcurrentLinkedQueue<MessageChat> mChats =
		new ConcurrentLinkedQueue<MessageChat>();

	private EventListener mEventListener = null;

	public LiveMessageLoader(String url, String port,
			String threadId, String cookie, String userId,
			Context context) {
		mUrl = url;
		mPort = port;
		mThreadId = threadId;
		mCookie = cookie;
		mUserId = userId;
		mContext = context;
	}

	@Override
	public void run() {
		InputStream in = null;
		OutputStream out = null;

		try {
			mSocket = new Socket(mUrl, Integer.parseInt(mPort));
			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, mSocket.toString());
			}
			mSocket.setSoTimeout(DEFAULT_TIMEOUT_MS);
			in = mSocket.getInputStream();
			out = mSocket.getOutputStream();
			if (!mSocket.isConnected()) {
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, "Socket connect failed");
				}
				return;
			}
			dispatchOnConnected();
			String request = createXmlForRequest();
			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, Log.buf().append("request: ")
						.append(request).toString());
			}
			out.write(request.getBytes(HTTP.UTF_8));
			out.write('\0');
			out.flush();
			boolean result = readAndCreateData(in);

			if (result) {
				dispatchOnFinished();
			} else {
				dispatchOnOccurredError("XML parse failed");
			}
		} catch (NumberFormatException e) {
			String errorMessage = e.toString();
			Log.d(LOG_TAG, errorMessage, e);
			dispatchOnOccurredError(errorMessage);
		} catch (UnknownHostException e) {
			String errorMessage = e.toString();
			Log.d(LOG_TAG, errorMessage, e);
			dispatchOnOccurredError(errorMessage);
		} catch (IOException e) {
			String errorMessage = e.toString();
			Log.d(LOG_TAG, errorMessage, e);
			if (mSocket == null || !mSocket.isOutputShutdown()) {
				dispatchOnOccurredError(errorMessage);
			}
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.toString(), e);
				}
			}
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.toString(), e);
				}
			}
			if (mSocket != null) {
				try {
					mSocket.shutdownInput();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.toString(), e);
				}
				try {
					mSocket.shutdownOutput();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.toString(), e);
				}
				try {
					mSocket.close();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.toString(), e);
				}
				mSocket = null;
			}
		}
	}

	@Override
	protected boolean createDataFromXml(String xmlBody) {
		// TODO 自動生成されたメソッド・スタブ
		return false;
	}

//	@Override
//	protected HttpUriRequest createRequest() {
//		URI uri = null;
//		try {
//			uri = new URI("http", null, mUrl, Integer.parseInt(mPort), null, null, null);
//		} catch (NumberFormatException e1) {
//			Log.d(LOG_TAG, e1.getMessage(), e1);
//		} catch (URISyntaxException e1) {
//			Log.d(LOG_TAG, e1.getMessage(), e1);
//		}
//		if (uri == null) {
//			return null;
//		}
//		if (DEBUG_LOGD) {
//			Log.d(LOG_TAG, uri.toString());
//		}
//
//		HttpPost httpRequest = new HttpPost(uri);
//		httpRequest.addHeader("Cookie", mCookie);
//		SharedPreferences sharedPreference = PreferenceManager.getDefaultSharedPreferences(mContext);
//		String userAgent = sharedPreference.getString(NicoroConfig.USER_AGENT, null);
//		if (userAgent != null) {
//			httpRequest.setHeader("User-Agent", userAgent);
//		}
//		if (DEBUG_LOGD) {
//			Log.d(LOG_TAG, "LiveMessageLoader HTTP request>");
//			Util.logHeaders(LOG_TAG, httpRequest.getAllHeaders());
//		}
//
//		HttpEntity entity;
//		try {
//			entity = new StringEntity(createXmlForRequest(), HTTP.UTF_8);
//		} catch (UnsupportedEncodingException e) {
//			assert false;
//			Log.d(LOG_TAG, e.getMessage(), e);
//			return null;
//		}
//		httpRequest.setEntity(entity);
//
//		return httpRequest;
//	}

	@Override
	protected void dispatchOnFinished() {
		if (mEventListener != null) {
			mEventListener.onFinished(this);
		}
	}

	@Override
	protected void dispatchOnOccurredError(String errorMessage) {
		if (mEventListener != null) {
			mEventListener.onOccurredError(this, errorMessage);
		}
	}

	private void dispatchOnConnected() {
        if (mEventListener != null) {
            mEventListener.onConnected(this);
        }
	}

	@Override
	protected boolean readAndCreateData(InputStream inDownload)
			throws IOException {
		InputStreamReader inReader = null;
		try {
			inReader = new InputStreamReader(inDownload, HTTP.UTF_8);
//			inReader = new InputStreamReader(inDownload, HTTP.UTF_8) {
//				@Override
//				public int read(char[] buf, int offset, int length)
//				throws IOException {
//					int ret = super.read(buf, offset, length);
//					if (DEBUG_LOGD) {
//						if (ret >= 0) {
//							Log.d(LOG_TAG, Log.buf().append(buf, 0, ret)
//									.toString());
//						}
//					}
//					return ret;
//				}
//				@Override
//				public int read() throws IOException {
//					int ret = super.read();
//					if (DEBUG_LOGD) {
//						if (ret >= 0) {
//							Log.d(LOG_TAG, Log.buf().append(ret)
//									.toString());
//						}
//					}
//					return ret;
//				}
//			};
			XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
			factory.setNamespaceAware(true);
			XmlPullParser pullParser = factory.newPullParser();
			pullParser.setInput(inReader);

			String name = null;
			String text = null;
			MessageChat chat = null;

			final ConcurrentLinkedQueue<MessageChat> chats = mChats;

			int next = pullParser.getEventType();
			while (!mIsFinish &&
					next != XmlPullParser.END_DOCUMENT) {
				if (DEBUG_LOGD_PARSE) {
					Log.d(LOG_TAG, Log.buf().append("next=").append(next)
							.toString());
				}
				switch (next) {
				case XmlPullParser.START_TAG:
					name = pullParser.getName();
					if (DEBUG_LOGD_PARSE) {
						Log.d(LOG_TAG, Log.buf().append("<").append(name)
								.append(">").toString());
					}
					if ("chat".equals(name)) {
						chat = createMessageChat(
								pullParser.getAttributeValue(null, "mail"),
								Integer.parseInt(pullParser.getAttributeValue(null, "vpos")));
					} else {
						// その他のタグはとりあえず無視
					}
					break;
				case XmlPullParser.TEXT:
					text = pullParser.getText();
					if (DEBUG_LOGD_PARSE) {
						Log.d(LOG_TAG, Log.buf().append("text=")
								.append(text).toString());
					}
					if (name != null) {
						if ("chat".equals(name)) {
							assert chat != null;
							chat.setText(pullParser.getText());
						} else {
							// その他のタグはとりあえず無視
						}
					}
					break;
				case XmlPullParser.END_TAG:
					name = pullParser.getName();
					if (DEBUG_LOGD_PARSE) {
						Log.d(LOG_TAG, Log.buf().append("</").append(name)
								.append(">").toString());
					}
					if ("chat".equals(name)) {
						assert chat != null;
						chats.add(chat);
						chat = null;
						if (mEventListener != null) {
							mEventListener.onAddedMessage(this);
						}
					} else {
						// その他のタグはとりあえず無視
					}
					name = null;
					break;
				default:
					break;
				}
				next = pullParser.next();
			}

			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, "readAndCreateData end");
			}

			return true;
		} catch (XmlPullParserException e) {
			Log.d(LOG_TAG, e.toString(), e);
			return false;
		} finally {
			if (inReader != null) {
				try {
					inReader.close();
				} catch (IOException e) {
					Log.d(LOG_TAG, e.toString(), e);
				}
			}
		}
	}

	@Override
	protected void shutdownNetwork() {
		Socket socket = mSocket;
		if (socket != null) {
			try {
				socket.shutdownInput();
			} catch (IOException e) {
				Log.d(LOG_TAG, e.toString(), e);
			}
			try {
				socket.shutdownOutput();
			} catch (IOException e) {
				Log.d(LOG_TAG, e.toString(), e);
			}
		}
	}

	private String createXmlForRequest() {
		StringBuilder body = new StringBuilder(256)
			.append("<thread")
			.append(" thread=\"").append(mThreadId).append('"')
//			.append(" res_from=\"-").append(50).append('"')
			.append(" res_from=\"-").append(1).append('"')
			.append(" version=\"20061206\"")
//			.append(" user_id=\"").append(mUserId).append('"')
//			.append(" />");
			.append("/>");
		return body.toString();
	}

	public MessageChat poll() {
		return mChats.poll();
	}

	private MessageChat createMessageChat(String m, int v) {
		return new MessageChat(m, v);
	}
	public void setEventListener(EventListener eventListener) {
		mEventListener = eventListener;
	}

	public interface EventListener {
	    /**
	     * サーバーと接続した
	     * @param loader
	     */
	    public void onConnected(LiveMessageLoader loader);
		/**
		 * 新しいMessageChatが追加された
		 * 注意：連続して呼ばれる可能性あり
		 * @param loader
		 */
		public void onAddedMessage(LiveMessageLoader loader);
		/**
		 * ファイル全体の読み込み完了
		 * @param loader
		 */
		public void onFinished(LiveMessageLoader loader);
		/**
		 * エラー発生
		 * @param loader
		 * @param errorMessage
		 */
		public void onOccurredError(LiveMessageLoader loader, String errorMessage);
	}
}
