package jp.sourceforge.nicoro;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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 android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Xml;

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

public class MessageLoader extends HttpXmlLoader {
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG && true;
	private static final boolean DEBUG_LOGD_PARSE = Release.IS_DEBUG && false;
	
	protected int mResSize = 0;
	
	private String mUrl;
	protected String mThreadId;
	private String mCookie;
	protected String mUserId;
	protected String mThreadKey;
	protected String mForce184;
	private Context mContext;
	
	private Vector<MessageChat> mChats;

	private EventListener mEventListener = null;
	
	public MessageLoader(String url, String threadId, String cookie,
			String userId, String threadKey, String force184,
			Context context) {
		mUrl = url;
		mThreadId = threadId;
		mCookie = cookie;
		mUserId = userId;
		mThreadKey = threadKey;
		mForce184 = force184;
		mContext = context;
	}
	
	public Vector<MessageChat> getChats() {
		return mChats;
	}
	
	private Vector<MessageChat> parse(String body, Vector<MessageChat> chats) {
		assert mResSize > 0;
		if (chats == null) {
			chats = new Vector<MessageChat>(mResSize);
		} else {
			chats.clear();
		}
		
		XmlPullParser pullParser = Xml.newPullParser();
		try {
			pullParser.setInput(new StringReader(body));
			
			int next;
			String name = null;
			MessageChat chat = null;
			
			while ((next = pullParser.next()) != XmlPullParser.END_DOCUMENT) {
				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)) {
//						assert chat == null;
						chat = createMessageChat(
								pullParser.getAttributeValue(null, "mail"),
								Integer.parseInt(pullParser.getAttributeValue(null, "vpos")));
					} else {
						// その他のタグはとりあえず無視
					}
					break;
				case XmlPullParser.TEXT:
					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;
					} else {
						// その他のタグはとりあえず無視
					}
					name = null;
					break;
				default:
					break;
				}
			}
		} catch (XmlPullParserException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
		} catch (IOException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
		}
		
		if (DEBUG_LOGD_PARSE) {
			for (MessageChat chat : chats) {
				Log.d(LOG_TAG, chat.toString());
			}
		}
		
		return chats;
	}
	
	public void setVideoLength(String length) {
		if (length != null) {
			Pattern pattern = Pattern.compile("([0-9]+:)?([0-9]+):([0-9]+)");
			Matcher matcher = pattern.matcher(length);
			if (matcher.find()) {
				if (matcher.group(1) != null) {
					// 1時間以上
					mResSize = 1000;
					return;
				} else {
					int minutes = Integer.parseInt(matcher.group(2));
					int seconds = Integer.parseInt(matcher.group(3));
					if (minutes < 1) {
						mResSize = 100;
					} else if (minutes < 5) {
						mResSize = 250;
					} else if (minutes < 10) {
						mResSize = 500;
					} else {
						mResSize = 1000;
					}
					assert mResSize > 0;
					return;
				}
			}
		}
		// エラー時は最低値
		Log.d(LOG_TAG, Log.buf().append("VideoLength parse failed: length=")
				.append(length).toString());
		mResSize = 100;
	}
	
	@Override
	protected HttpUriRequest createRequest() {
		HttpPost httpRequest = new HttpPost(mUrl);
		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, "MessageLoader HTTP request>");
			Util.logHeaders(LOG_TAG, httpRequest.getAllHeaders());
		}
		
		assert mResSize > 0;
		
		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 boolean createDataFromXml(String xmlBody) {
		Vector<MessageChat> chats = mChats;
		mChats = null;
		mChats = parse(xmlBody, chats);
		// ソート
		Collections.sort(mChats, MessageChat.getVposComparator());
		if (DEBUG_LOGD) {
			for (MessageChat c : mChats) {
				Log.d(LOG_TAG, c.toString());
			}
		}
		
		return (mChats != null);
	}
	
	@Override
	protected void dispatchOnFinished() {
		if (mEventListener != null) {
			mEventListener.onFinished(this);
		}
	}
	@Override
	protected void dispatchOnOccurredError(String errorMessage) {
		if (mEventListener != null) {
			mEventListener.onOccurredError(this, errorMessage);
		}
	}
	
	@Override
	protected boolean readAndCreateData(InputStream inDownload)
	throws IOException {
		String xmlBody = readEntityAndDecode(inDownload);
		return createDataFromXml(xmlBody);
	}
	
	protected String createXmlForRequest() {
		StringBuilder body = new StringBuilder(256)
		.append("<thread res_from=\"-")
		.append(mResSize)
		.append("\" version=\"20061206\" thread=\"")
		.append(mThreadId)
		.append("\" user_id=\"")
		.append(mUserId);
		if (mThreadKey != null) {
			body.append("\" threadkey=\"").append(mThreadKey);
		}
		if (mForce184 != null) {
			body.append("\" force_184=\"").append(mForce184);
		}
		body.append("\" />");
		return body.toString();
	}
	
	protected MessageChat createMessageChat(String m, int v) {
		return new MessageChat(m, v);
	}
	
	public void setEventListener(EventListener eventListener) {
		mEventListener = eventListener;
	}
	
	public interface EventListener {
		/**
		 * ファイル全体の読み込み完了
		 * @param streamLoader
		 */
		public void onFinished(MessageLoader messageLoader);
		/**
		 * エラー発生
		 * @param streamLoader
		 * @param errorMessage
		 */
		public void onOccurredError(MessageLoader messageLoader, String errorMessage);
	}
}
