package se.leap.openvpn;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Vector;

import se.leap.bitmaskclient.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.ListActivity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
import android.util.Base64;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import se.leap.openvpn.ConfigParser.ConfigParseError;

public class ConfigConverter extends ListActivity {

	public static final String IMPORT_PROFILE = "se.leap.openvpn.IMPORT_PROFILE";

	private VpnProfile mResult;
	private ArrayAdapter<String> mArrayAdapter;

	private List<String> mPathsegments;

	private String mAliasName=null;

	private int RESULT_INSTALLPKCS12 = 7;

	private String mPossibleName=null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.config_converter);
	}



	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if(item.getItemId()==R.id.cancel){
			setResult(Activity.RESULT_CANCELED);
			finish();
		} else if(item.getItemId()==R.id.ok) {
			if(mResult==null) {
				log("Importing the config had error, cannot save it");
				return true;
			}

			Intent in = installPKCS12();

			if(in != null)
				startActivityForResult(in, RESULT_INSTALLPKCS12);
			else
				saveProfile();

			return true;
		}

		return super.onOptionsItemSelected(item);

	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if(requestCode==RESULT_INSTALLPKCS12) {
			if(resultCode==Activity.RESULT_OK) {
				showCertDialog();
			}
		}

		super.onActivityResult(requestCode, resultCode, data);
	}

	private void saveProfile() {
		Intent result = new Intent();
		ProfileManager vpl = ProfileManager.getInstance(this);

		if(((CheckBox)findViewById(R.id.correcttls)).isChecked() && isOldCNFormat()) {
			convertTLSRemote();
		}
		
		setUniqueProfileName(vpl);
		vpl.addProfile(mResult);
		vpl.saveProfile(this, mResult);
		vpl.saveProfileList(this);
		result.putExtra(VpnProfile.EXTRA_PROFILEUUID,mResult.getUUID().toString());
		setResult(Activity.RESULT_OK, result);
		finish();
	}



	private void convertTLSRemote() {
		if(mResult.mRemoteCN.startsWith("/"))
			mResult.mRemoteCN = mResult.mRemoteCN.substring(1);
		mResult.mRemoteCN = mResult.mRemoteCN.replace("/", ", ");
	}

	public void showCertDialog () {
		try	{
			KeyChain.choosePrivateKeyAlias(this,
					new KeyChainAliasCallback() {

				public void alias(String alias) {
					// Credential alias selected.  Remember the alias selection for future use.
					mResult.mAlias=alias;
					saveProfile();
				}


			},
			new String[] {"RSA"}, // List of acceptable key types. null for any
			null,                        // issuer, null for any
			mResult.mServerName,      // host name of server requesting the cert, null if unavailable
			-1,                         // port of server requesting the cert, -1 if unavailable
			mAliasName);                       // alias to preselect, null if unavailable
		} catch (ActivityNotFoundException anf) {
			Builder ab = new AlertDialog.Builder(this);
			ab.setTitle(R.string.broken_image_cert_title);
			ab.setMessage(R.string.broken_image_cert);
			ab.setPositiveButton(android.R.string.ok, null);
			ab.show();
		}
	}


	private Intent installPKCS12() {
		
		if(!((CheckBox)findViewById(R.id.importpkcs12)).isChecked()) {
			setAuthTypeToEmbeddedPKCS12();
			return null;
			
		}
		String pkcs12datastr = mResult.mPKCS12Filename;
		if(pkcs12datastr!=null && pkcs12datastr.startsWith(VpnProfile.INLINE_TAG)) {
			Intent inkeyintent = KeyChain.createInstallIntent();
			
			pkcs12datastr= pkcs12datastr.substring(VpnProfile.INLINE_TAG.length());
			
			
			byte[] pkcs12data = Base64.decode(pkcs12datastr, Base64.DEFAULT);


			inkeyintent.putExtra(KeyChain.EXTRA_PKCS12,pkcs12data );

			if(mAliasName.equals(""))
				mAliasName=null;

			if(mAliasName!=null){
				inkeyintent.putExtra(KeyChain.EXTRA_NAME, mAliasName);
			}
			return inkeyintent;

		}
		return null;
	}



	private void setAuthTypeToEmbeddedPKCS12() {
		if(mResult.mPKCS12Filename!=null && mResult.mPKCS12Filename.startsWith(VpnProfile.INLINE_TAG)) {
			if(mResult.mAuthenticationType==VpnProfile.TYPE_USERPASS_KEYSTORE)
				mResult.mAuthenticationType=VpnProfile.TYPE_USERPASS_PKCS12;
			
			if(mResult.mAuthenticationType==VpnProfile.TYPE_KEYSTORE)
				mResult.mAuthenticationType=VpnProfile.TYPE_PKCS12;
			
		}
	}





	private void setUniqueProfileName(ProfileManager vpl) {
		int i=0;

		String newname = mPossibleName;

		while(vpl.getProfileByName(newname)!=null) {
			i++;
			if(i==1)
				newname = getString(R.string.converted_profile);
			else
				newname = getString(R.string.converted_profile_i,i);
		}

		mResult.mName=newname;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.import_menu, menu);
		return true;
	}
	
	private String embedFile(String filename) {
		return embedFile(filename, false);		
	}

	private String embedFile(String filename, boolean base64encode)
	{
		if(filename==null)
			return null;

		// Already embedded, nothing to do
		if(filename.startsWith(VpnProfile.INLINE_TAG))
			return filename;

		File possibleFile = findFile(filename);
		if(possibleFile==null)
			return filename;
		else
			return readFileContent(possibleFile,base64encode);

	}

	private File findFile(String filename) {
		File foundfile =findFileRaw(filename);
		
		if (foundfile==null && filename!=null && !filename.equals(""))
			log(R.string.import_could_not_open,filename);

		return foundfile;
	}



	private File findFileRaw(String filename)
	{
		if(filename == null || filename.equals(""))
			return null;

		// Try diffent path relative to /mnt/sdcard
		File sdcard = Environment.getExternalStorageDirectory();
		File root = new File("/");

		Vector<File> dirlist = new Vector<File>();

		for(int i=mPathsegments.size()-1;i >=0 ;i--){
			String path = "";
			for (int j = 0;j<=i;j++) {
				path += "/" + mPathsegments.get(j);
			}
			dirlist.add(new File(path));
		}
		dirlist.add(sdcard);
		dirlist.add(root);


		String[] fileparts = filename.split("/");
		for(File rootdir:dirlist){
			String suffix="";
			for(int i=fileparts.length-1; i >=0;i--) {
				if(i==fileparts.length-1)
					suffix = fileparts[i];
				else
					suffix = fileparts[i] + "/" + suffix;

				File possibleFile = new File(rootdir,suffix);
				if(!possibleFile.canRead())
					continue;

				// read the file inline
				return possibleFile;

			}
		}
		return null;
	}

	String readFileContent(File possibleFile, boolean base64encode) {
		byte [] filedata;
		try {
			filedata = readBytesFromFile(possibleFile);
		} catch (IOException e) {
			log(e.getLocalizedMessage());
			return null;
		}
		
		String data;
		if(base64encode) {
			data = Base64.encodeToString(filedata, Base64.DEFAULT);
		} else {
			data = new String(filedata);

		}
		return VpnProfile.INLINE_TAG + data;
		
	}


	private byte[] readBytesFromFile(File file) throws IOException {
		InputStream input = new FileInputStream(file);

		long len= file.length();


		// Create the byte array to hold the data
		byte[] bytes = new byte[(int) len];

		// Read in the bytes
		int offset = 0;
		int bytesRead = 0;
		while (offset < bytes.length
				&& (bytesRead=input.read(bytes, offset, bytes.length-offset)) >= 0) {
			offset += bytesRead;
		}

		input.close();
		return bytes;
	}

	void embedFiles() {
		// This where I would like to have a c++ style
		// void embedFile(std::string & option)

		if (mResult.mPKCS12Filename!=null) {
			File pkcs12file = findFileRaw(mResult.mPKCS12Filename);
			if(pkcs12file!=null) {
				mAliasName = pkcs12file.getName().replace(".p12", "");
			} else {
				mAliasName = "Imported PKCS12";
			}
		}
			
		
		mResult.mCaFilename = embedFile(mResult.mCaFilename);
		mResult.mClientCertFilename = embedFile(mResult.mClientCertFilename);
		mResult.mClientKeyFilename = embedFile(mResult.mClientKeyFilename);
		mResult.mTLSAuthFilename = embedFile(mResult.mTLSAuthFilename);
		mResult.mPKCS12Filename = embedFile(mResult.mPKCS12Filename,true);
		

		if(mResult.mUsername == null && mResult.mPassword != null ){
			String data =embedFile(mResult.mPassword);
			ConfigParser.useEmbbedUserAuth(mResult, data);			
		}
	}


	@Override
	protected void onStart() {
		super.onStart();

		mArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
		getListView().setAdapter(mArrayAdapter);
		final android.content.Intent intent = getIntent ();

		if (intent != null)
		{
			final android.net.Uri data = intent.getData ();
			if (data != null)
			{
				//log(R.string.import_experimental);
				log(R.string.importing_config,data.toString());
				try {
					if(data.getScheme().equals("file")) {
						mPossibleName = data.getLastPathSegment();
						if(mPossibleName!=null){
							mPossibleName =mPossibleName.replace(".ovpn", "");
							mPossibleName =mPossibleName.replace(".conf", "");
						}
					}
					InputStream is = getContentResolver().openInputStream(data);
					mPathsegments = data.getPathSegments();

					doImport(is);
				} catch (FileNotFoundException e) {
					log(R.string.import_content_resolve_error);
				}
			} 
		} 

		return;
	}

	private void log(String logmessage) {
		mArrayAdapter.add(logmessage);
	}

	private void doImport(InputStream is) {
		ConfigParser cp = new ConfigParser();
		try {
			InputStreamReader isr = new InputStreamReader(is);

			cp.parseConfig(isr);
			VpnProfile vp = cp.convertProfile();
			mResult = vp;
			embedFiles();
			displayWarnings();
			log(R.string.import_done);
			return;

		} catch (IOException e) {
			log(R.string.error_reading_config_file);
			log(e.getLocalizedMessage());
		} catch (ConfigParseError e) {
			log(R.string.error_reading_config_file);
			log(e.getLocalizedMessage());			
		}
		mResult=null;

	}

	private void displayWarnings() {
		if(mResult.mUseCustomConfig) {
			log(R.string.import_warning_custom_options);
			String copt = mResult.mCustomConfigOptions;
			if(copt.startsWith("#")) {
				int until = copt.indexOf('\n');
				copt = copt.substring(until+1);
			}

			log(copt);
		}

		if(mResult.mAuthenticationType==VpnProfile.TYPE_KEYSTORE ||
				mResult.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
			findViewById(R.id.importpkcs12).setVisibility(View.VISIBLE);
		}

		if (isOldCNFormat())
			findViewById(R.id.correcttls).setVisibility(View.VISIBLE);
	}

	private boolean isOldCNFormat() {
		return mResult.mCheckRemoteCN && mResult.mRemoteCN.contains("/") && ! mResult.mRemoteCN.contains("_");
	}

	private void log(int ressourceId, Object... formatArgs) {
		log(getString(ressourceId,formatArgs));
	}
}