/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.blinkt.openvpn;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Arrays;

import android.app.PendingIntent;
import android.content.Intent;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.VpnService;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.widget.Toast;

public class OpenVpnService extends VpnService implements Handler.Callback, Runnable {
    private static final String TAG = "OpenVpnService";

    private String[] mArgv;

    private Handler mHandler;
    private Thread mThread;

    private ParcelFileDescriptor mInterface;

    @Override
    public void onRevoke() {
    	managmentCommand("signal SIGINT\n");
    	mThread=null;
    	stopSelf();
    };
    
	
	public void managmentCommand(String cmd) {
		LocalSocket mgmtsocket;
		try {
			byte[] buffer = new byte[400];
			mgmtsocket = new LocalSocket();
					
			mgmtsocket.connect(new LocalSocketAddress(getCacheDir().getAbsolutePath() + "/" +  "mgmtsocket", 
					LocalSocketAddress.Namespace.FILESYSTEM)); 
			//mgmtsocket  = new Dat("127.0.0.1",OpenVPNClient.MANAGMENTPORT));
			
			//OutputStreamWriter outw = new OutputStreamWriter(mgmtsocket.getOutputStream());
			mgmtsocket.getInputStream().read(buffer);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
			//outw.write(cmd);
			mgmtsocket.getOutputStream().write(cmd.getBytes());
			//outw.flush();
			try {
				Thread.sleep(400);
			} catch (InterruptedException e) {
			}

			mgmtsocket.close();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
    
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The handler is only used to show messages.
        if (mHandler == null) {
            mHandler = new Handler(this);
        }

        // Stop the previous session by interrupting the thread.
        if (mThread != null) {
        	managmentCommand("signal SIGINT\n");
            mThread.interrupt();
        }

        // Thread already running, reuse existing,  
        
        // Extract information from the intent.
        String prefix = getPackageName();
        mArgv = intent.getStringArrayExtra(prefix + ".ARGV");
        

        // Start a new session by creating a new thread.
        mThread = new Thread(this, "OpenVPNThread");
        mThread.start();
        
        if(intent.hasExtra(prefix +".PKCS12PASS"))
        {
        	try {
        		String pkcs12password = intent.getStringExtra(prefix +".PKCS12PASS");
				Thread.sleep(3000);
				
				managmentCommand("password 'Private Key' " + pkcs12password + "\n");
			} catch (InterruptedException e) {
			}
        	
        }
        if(intent.hasExtra(prefix +".USERNAME"))
        {
        	try {
        		String user = managmentEscape(intent.getStringExtra(prefix +".USERNAME"));
        		String pw = managmentEscape(intent.getStringExtra(prefix +".PASSWORD"));
				Thread.sleep(3000);
				
				
				managmentCommand("username 'Auth' " + user+ "\n" + 
						"password 'Auth' " + pw + "\n");
			} catch (InterruptedException e) {
			}
        
        }
        
        
        return START_STICKY;
    }

    private String managmentEscape(String unescape) {
    	String escapedString = unescape.replace("\\", "\\\\");
    	escapedString = escapedString.replace("\"","\\\"");
    	escapedString = escapedString.replace("\n","\\n");
    	return '"' + escapedString + '"';
	}


	@Override
    public void onDestroy() {
        if (mThread != null) {
        	managmentCommand("signal SIGINT\n");

            mThread.interrupt();
        }
    }

    @Override
    public boolean handleMessage(Message message) {
        if (message != null) {
            Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    public synchronized void run() {
        try {
            Log.i(TAG, "Starting o");

            
            OpenVPN.setCallback(this);


            // We try to create the tunnel for several times. The better way
            // is to work with ConnectivityManager, such as trying only when
            // the network is avaiable. Here we just use a counter to keep
            // things simple.
            //for (int attempt = 0; attempt < 10; ++attempt) {
                mHandler.sendEmptyMessage(R.string.connecting);

                // Log argv
                
                OpenVPN.logMessage(0, "argv:" , Arrays.toString(mArgv));
                
                OpenVPN.startOpenVPNThreadArgs(mArgv);
                
                
                
                // Sleep for a while. This also checks if we got interrupted.
                Thread.sleep(3000);
            //}
            Log.i(TAG, "Giving up");
        } catch (Exception e) {
            Log.e(TAG, "Got " + e.toString());
        } finally {
            try {
                mInterface.close();
            } catch (Exception e) {
                // ignore	
            }
            mInterface = null;
            

            mHandler.sendEmptyMessage(R.string.disconnected);
            Log.i(TAG, "Exiting");
        }
    }


	public ParcelFileDescriptor openTun(String localip) {
		// FIXME: hardcoded assumptions
        Builder builder = new Builder();
        builder.addRoute("0.0.0.0", 0);
        builder.addAddress(localip, 24 );
        builder.addDnsServer("131.234.137.23");
        builder.addSearchDomain("blinkt.de");
        builder.setSession("OpenVPN - " + localip);
        Intent intent = new Intent(getBaseContext(),LogWindow.class);
        PendingIntent startLW = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
        builder.setConfigureIntent(startLW);
        mInterface = builder.establish();
        return mInterface;

	}
}