package jp.co.corecorp.s3pdf;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.UUID;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class S3Context {
	public static final int START_SYNCHRONIZE = 0;
	public static final int START_SERVICE_SYNC = 1;
	public static final int START_RESOURCE_SYNC = 2;
	public static final int END_RESOURCE_SYNC = 3;
	public static final int END_SERVICE_SYNC = 4;
	public static final int END_SYNCHRONIZE_SYNC = 5;
	public static final int FAILED_SYNCHRONIZE = 9;
	
	private Context context;
	private String	host;
	private int 	port;
	private S3ConnectHandler connectHandler;

	private String	user;
	private String	passwd;
	private String	name1;
	private String	name2;
	private boolean loggedin;
	private S3LoginHandler	loginHandler;
	private S3Handler 		completeHandler;
	private S3RegisterHandler 	registerServiceHandler;
	private S3SyncHandler	syncHandler;

	private S3ServiceManager sm;
	private S3LocalStore ls;
	private S3Connection cn;
	
    private static String sID = null;
    private static final String METADATADIR = "s3";
    private static final String DEVICEIDFILE = "deviceid";
    
	public S3Context(Context ctx) {
		context	= ctx;
		sm	= new S3ServiceManager(this);
		ls	= null;
		cn	= null;
		loggedin	= false;
	}
	public Context getContext() {
		return context;
	}
	public S3ServiceManager getServiceManager() {
		return sm;
	}
	public S3LocalStore getLocalStore() {
		return ls;
	}	
	public S3Connection getConnection() {
		return cn;
	}
	public boolean isLoggedin() {
		return loggedin;
	}
    public synchronized File getMetadtaDir() {
//    	File metadir = new File(context.getFilesDir(), METADATADIR);
    	File metadir = new File(Environment.getExternalStorageDirectory(), METADATADIR);
    	if( metadir.exists() ) {
    		if( !metadir.isDirectory() ) {
    			metadir.delete();
    		}
    	}
    	if( !metadir.exists() ) {
    		metadir.mkdir();
    	}
    	return metadir;
    }
    public synchronized String getDeviceId() {
        if (sID == null) {
            File devFile = new File(getMetadtaDir(), DEVICEIDFILE);
            try {
                if (!devFile.exists())
                    writeDeviceIdFile(devFile);
                sID = readDeviceIdFile(devFile);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return sID;
    }

    private synchronized String readDeviceIdFile(File devFile) throws IOException {
        RandomAccessFile f = new RandomAccessFile(devFile, "r");
        byte[] bytes = new byte[(int) f.length()];
        f.readFully(bytes);
        f.close();
        return new String(bytes);
    }

    private synchronized void writeDeviceIdFile(File devFile) throws IOException {
        FileOutputStream out = new FileOutputStream(devFile);
        String id = UUID.randomUUID().toString();
        out.write(id.getBytes());
        out.close();
    }	

	boolean isNetworkActive() {
		ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
		if( cm == null ) return false;
		NetworkInfo network = cm.getActiveNetworkInfo();
		if( network == null ) {
			return false;
		}
		if( !network.isConnected() ) {
			return false;
		}
		return true;
	}
	boolean isOnline() {
		return cn != null && cn.isLoggedin(); 
	}
	boolean isOffline() {
		return cn == null || !cn.isLoggedin(); 
	}
	
	private Handler connectStab = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if( connectHandler != null ) {
				Bundle b = msg.getData();
				int code = b.getInt("C");
				if( code == 0 ) {
					connectHandler.onConnect(S3Context.this);
				} else {
					connectHandler.onError(S3Context.this, b.getString("M"));
				}
			}
		}
	};
	
	void connect(String host, int port, S3ConnectHandler handler) {
		Log.d("s3ctx","do connect");
		this.connectHandler = handler;
		this.host 	= host;
		this.port	= port;
		class R extends Thread {
			String host;
			int port;
			public void run() {
				try {
					if( cn != null ) {
						if( cn.isConnected() ) {
							cn.disconnect();
						}
						cn = null;
					}
					if( ls != null ) {
						ls = null;
					}
					if( isNetworkActive() ) {
						cn	= new S3Connection(S3Context.this);
						try {
							cn.connect(host, port);
							Log.d("s3ctx", "network is enable");
						}
						catch(S3Exception e) {
							Log.d("s3ctx", "network is diable");
							cn = null;
						}
					}
					ls = new S3LocalStore(S3Context.this);
					ls.connect(host, port);
				}
				catch(S3Exception e) {
					Log.d("s3ctx",e.toString());
					Bundle notifyData = new Bundle();
					notifyData.putInt("C", 1);
					notifyData.putString("M", e.getMessage());
					Message notifyMsg = Message.obtain();
					notifyMsg.setData(notifyData);
					cn = null;
					ls = null;
					connectStab.sendMessage(notifyMsg);
					return;
				}
				Bundle notifyData = new Bundle();
				notifyData.putInt("C", 0);
				Message notifyMsg = Message.obtain();
				notifyMsg.setData(notifyData);
				connectStab.sendMessage(notifyMsg);
			}
		};
		R proc = new R();
		proc.host	= host;
		proc.port	= port;
		proc.start();
	}

	private Handler disconnectStab = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if( completeHandler != null ) {
				completeHandler.onComplete(S3Context.this);
			}
		}
	};
	
	void disconnect(S3Handler handler) {
		this.completeHandler = handler;
		Thread proc = new Thread() {
			public void run() {
				if( cn != null ) {
					if( cn.isConnected() ) {
						cn.disconnect();
					}
					cn	= null;
				}
				loggedin = false;
				Message notifyMsg = Message.obtain();
				disconnectStab.sendMessage(notifyMsg);
			}
		};
		proc.start();
	}
	
	private Handler loginStab = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if( loginHandler != null ) {
				Bundle b = msg.getData();
				int code = b.getInt("C");
				if( code == 0 ) {
					loginHandler.onLogin(S3Context.this);
				} else {
					loginHandler.onError(S3Context.this, b.getString("M"));
				}
			}
		}
	};	
	
	void login(String user, String passwd, S3LoginHandler handler) {
		this.user	= user;
		this.passwd	= passwd;
		this.loginHandler = handler;
		Log.d("s3ctx.login","begin");
		class R extends Thread {
			String _user;
			String _pass;
			R(String u, String p) {
				_user = u;
				_pass = p;
			}
			public void run() {
				try {
					if( cn != null ) {
						Log.d("s3ctx.login","network login");
						cn.login(_user, _pass);
						Log.d("s3ctx.login","network login ok");
					}
					Log.d("s3ctx.login","local login");
					ls.login(_user, _pass);
					Log.d("s3ctx.login","local login ok");
				}
				catch(S3Exception e) {
					loggedin = false;
					Bundle notifyData = new Bundle();
					notifyData.putInt("C", 1);
					notifyData.putString("M", e.getMessage());
					Message notifyMsg = Message.obtain();
					notifyMsg.setData(notifyData);
					loginStab.sendMessage(notifyMsg);
					return;
				}
				loggedin = true;
				Bundle notifyData = new Bundle();
				notifyData.putInt("C", 0);
				Message notifyMsg = Message.obtain();
				notifyMsg.setData(notifyData);
				loginStab.sendMessage(notifyMsg);
			}
		};
		R proc = new R(user, passwd);
		proc.start();
	}
	
	void registerAccount(String user, String passwd, String name1, String name2, S3LoginHandler handler) {
		this.user	= user;
		this.passwd	= passwd;
		this.name1	= name1;
		this.name2	= name2;
		this.loginHandler = handler;
		Log.d("s3ctx.registerAccount","begin");

		class Proc extends Thread {
			String _user;
			String _passwd;
			String _name1;
			String _name2;
			Proc(String u, String p, String n1, String n2) {
				_user 	= u;
				_passwd	= p;
				_name1	= n1;
				_name2	= n2;
			}
			public void run() {
				try {
					if( cn == null ) {
						throw new S3Exception(getContext().getString(R.string.notConnectionError));
					}
					cn.register(_user, _passwd, _name1, _name2);
					ls.register(_user, _passwd, _name1, _name2);
				}
				catch(S3Exception e) {
					Bundle notifyData = new Bundle();
					notifyData.putInt("C", 1);
					notifyData.putString("M", e.getMessage());
					Message notifyMsg = Message.obtain();
					notifyMsg.setData(notifyData);
					loginStab.sendMessage(notifyMsg);
					return;
				}
				Bundle notifyData = new Bundle();
				notifyData.putInt("C", 0);
				Message notifyMsg = Message.obtain();
				notifyMsg.setData(notifyData);
				loginStab.sendMessage(notifyMsg);
			}
		};
		Proc proc = new Proc(user, passwd, name1, name2);
		proc.start();
	}
	
	void logout(S3Handler handler) {
		this.completeHandler = handler;
		Thread proc = new Thread() {
			public void run() {
				if( ls != null ) {
					ls.disconnect();
					ls	= null;
				}
				if( cn != null ) {
					cn.disconnect();
					cn	= null;
				}
				loggedin = false;
				Message notifyMsg = Message.obtain();
				disconnectStab.sendMessage(notifyMsg);
			}
		};
		proc.start();
	}
	
	private Handler registerServiceStab = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if( registerServiceHandler != null ) {
				Bundle b = msg.getData();
				int code = b.getInt("C");
				String svn = b.getString("S");
				if( code == 0 ) {
					registerServiceHandler.onRegisterComplete(S3Context.this, svn);
				} else {
					registerServiceHandler.onError(S3Context.this, b.getString("M"));
				}
			}
		}
	};
	void registerService(String svc, S3RegisterHandler handler) {
		this.registerServiceHandler = handler;
		class Proc extends Thread {
			String _service;
			Proc(String s) {
				_service 	= s;
			}
			public void run() {
				try {
					sm.connectService(_service);
				}
				catch(S3Exception e) {
					Bundle notifyData = new Bundle();
					notifyData.putInt("C", 9);
					notifyData.putString("S", _service);
					notifyData.putString("M", e.getMessage());
					Message notifyMsg = Message.obtain();
					notifyMsg.setData(notifyData);
					registerServiceStab.sendMessage(notifyMsg);
					return;
				} catch (S3TypeException e) {
					Bundle notifyData = new Bundle();
					notifyData.putInt("C", 9);
					notifyData.putString("S", _service);
					notifyData.putString("M", e.getMessage());
					Message notifyMsg = Message.obtain();
					notifyMsg.setData(notifyData);
					registerServiceStab.sendMessage(notifyMsg);
					return;
				}
				Bundle notifyData = new Bundle();
				notifyData.putInt("C", 0);
				notifyData.putString("S", _service);
				Message notifyMsg = Message.obtain();
				notifyMsg.setData(notifyData);
				registerServiceStab.sendMessage(notifyMsg);
			}
		};
		Proc proc = new Proc(svc);
		proc.start();
	}
	
	
	void setSyncHandler(S3SyncHandler handler) {
		syncHandler	= handler;
	}
	private Handler syncStab = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if( syncHandler != null ) {
				Bundle b = msg.getData();
				int code = b.getInt("C");
				String svn = b.getString("S");
				if( code == 0 ) {
					syncHandler.onSyncComplete(S3Context.this, svn);
				} else {
					syncHandler.onError(S3Context.this, b.getString("M"));
				}
			}
		}
	};
	
	void sync(S3Application svc) {
		class Proc extends Thread {
			S3Application _svc;
			Proc(S3Application svc) {
				_svc	= svc;
			}
			public void run() {
				try {
					sm.sync(_svc, syncStab);
				}
				catch(S3SyncException e) {
					Bundle notifyData = new Bundle();
					notifyData.putInt("C", 9);
					notifyData.putString("S", e.getServiceName());
					notifyData.putString("R", e.getResourceName());
					notifyData.putString("M", e.getMessage());
					Message notifyMsg = Message.obtain();
					notifyMsg.setData(notifyData);
					syncStab.sendMessage(notifyMsg);
					return;
				}
			}
		};
		Proc proc = new Proc(svc);
		proc.start();		
	}
}
