package com.beem.project.beem.otr;

import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

import net.java.otr4j.OtrEngine;
import net.java.otr4j.OtrEngineHost;
import net.java.otr4j.OtrEngineImpl;
import net.java.otr4j.OtrEngineListener;
import net.java.otr4j.OtrKeyManagerImpl;
import net.java.otr4j.OtrPolicy;
import net.java.otr4j.OtrPolicyImpl;
import net.java.otr4j.session.SessionID;
import android.util.Log;

import com.beem.project.beem.service.ChatAdapter;

public class BeemOtrManager implements OtrEngineHost {

	private static final String TAG = "BeemOtrEngineHostImpl";
	private static BeemOtrManager INSTANCE;

	private OtrEngine mOtrEngine;
	private OtrKeyManagerImpl mOtrKeyManager;

	//Map of chat, needed because of the message injection
	private final Map<SessionID, ChatAdapter> mChats = new HashMap<SessionID, ChatAdapter>();

	//We will have a global policy for Beem as long as we won't need to modify the policy per chat.
	private static final OtrPolicy mGlobalPolicy = new OtrPolicyImpl(OtrPolicy.ALLOW_V2 | OtrPolicy.ERROR_START_AKE);



	private Map<SessionID, KeyPair> mKeys = new HashMap<SessionID, KeyPair>();

	public OtrEngine getOtrManager() {
		return mOtrEngine;
	}

	// Private constructor prevents instantiation from other classes
	private BeemOtrManager() {  
		mOtrEngine = new OtrEngineImpl(this);;
		mOtrEngine.addOtrEngineListener(new BeemOtrListener());
		try {
			mOtrKeyManager = new OtrKeyManagerImpl("/sdcard/beem.keystore");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static BeemOtrManager getInstance() {
		if (INSTANCE == null)
			INSTANCE = new BeemOtrManager();
		return INSTANCE;
	}

	/*
	 * We must call addChat before stating a new otr session because we will need the chat instance for message injection
	 */
	public void addChat(final SessionID sessionID, final ChatAdapter chat) {
		mChats.put(sessionID, chat);
	}

	/*
	 * We must remove the chat from the map after we ended the corresponding otr session.
	 */
	public void removeChat(final SessionID sessionID) {
		mChats.remove(sessionID);
	}

	@Override
	public void injectMessage(SessionID sessionID, String msg) {
		ChatAdapter chat = mChats.get(sessionID);
		chat.sendMessage(msg);		
	}

	@Override
	public void showWarning(SessionID sessionID, String warning) {
		Log.d(TAG, "Warning for "+sessionID + " : "+warning);
	}

	@Override
	public void showError(SessionID sessionID, String error) {
		Log.d(TAG, "Error for "+sessionID + " : "+error);
	}

	@Override
	public OtrPolicy getSessionPolicy(SessionID sessionID) {
		return mGlobalPolicy;
	}

	@Override
	public KeyPair getKeyPair(SessionID sessionID) {	
		KeyPair key = mKeys.get(sessionID);

		if (key == null) {
			KeyPairGenerator kg;
			try {
				kg = KeyPairGenerator.getInstance("DSA");
				key = kg.genKeyPair();
				mKeys.put(sessionID, key);
			} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
				return null;
			}
		}

		return key;
	}

	private class BeemOtrListener implements OtrEngineListener {

		@Override
		public void sessionStatusChanged(final SessionID sessionID) {
			Log.d(TAG, "OTR Status changed for " + sessionID + " : " + mOtrEngine.getSessionStatus(sessionID));
			mChats.get(sessionID).otrStateChanged(mOtrEngine.getSessionStatus(sessionID).toString());	
		}
	}
}
