package com.funambol.omadmclient.android.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.content.res.AssetManager;  

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Timer;
import java.util.TimerTask;

import com.funambol.omadmclient.android.service.NotificationCallbackInterface;

import android.util.Log;


public class OmaDmClinetService extends Service 
{
    //------------------------------------------------------------------
    public final static String LOGTAG = "OmaDmClientServiceJ";

    private final String dataPathPrefix = "/data/data/";
    private final String initPath = "appdata";
    private final String pkgName = "com.funambol.omadmclient.android.service"; 

    private NotificationCallbackInterface m_callBackInterface;

    //------------------------------------------------------------------
    static{
        try {
            Log.i(LOGTAG, "Load libwbxml2.so");
            System.loadLibrary("wbxml2");
        }
        catch (UnsatisfiedLinkError ule) {
            Log.i(LOGTAG, "ERROR: Could not load libwbxml2.so");
        }

        try {
            Log.i(LOGTAG, "Load libLogger.so");
            System.loadLibrary("Logger");
        }
        catch (UnsatisfiedLinkError ule) {
            Log.i(LOGTAG, "ERROR: Could not load libLogger.so");
        }

        try {
            Log.i(LOGTAG, "Load libOMADMClient.so");
            System.loadLibrary("OMADMClient");
        }
        catch (UnsatisfiedLinkError ule) {
            Log.i(LOGTAG, "ERROR: Could not load libOMADMClient.so");
        }

        try {
            Log.i(LOGTAG, "Load libOMADMClientJNIAdapter.so");
            System.loadLibrary("OMADMClientJNIAdapter");
        }
        catch (UnsatisfiedLinkError ule) {
            Log.i(LOGTAG, "ERROR: Could not load libOMADMClientJNIAdapter.so");
        }
    }
    //------------------------------------------------------------------

    // native method by DaemonDM
    public static native long CreateEngine();
    public static native long StartEngine();
    public static native long StopEngine();

    public static native long CreateMessageListener();
    public static native long StartMessageListener();
    public static native long StopMessageListener();

    //------------------------------------------------------------------
    private Timer timer = new Timer();
    private final long mPeriod = 500;
    private boolean mServiceStarted = false;
    //------------------------------------------------------------------

    public IBinder onBind(Intent intent) 
    { 
        return null; 
    }

    public void onCreate() 
    {
        Log.i(LOGTAG, "onCreate started");
        super.onCreate();

        m_callBackInterface = new NotificationCallbackInterface(this);

        if (CreateEngine() != 0)
         {
            Log.i(LOGTAG, "CreateEngine success");
         }
        else
         {
            Log.i(LOGTAG, "CreateEngine failed");
         }

        if (!initApplicationData())
         {
            Log.i(LOGTAG, "Could not initialize application data");
         }
        else
         {
            Log.i(LOGTAG, "Application data initialized successful");
         }

        Log.i(LOGTAG, "BEFORE startservice");
        startservice();
        Log.i(LOGTAG, "AFTER startservice");

        Log.i(LOGTAG, "onCreate finished");
    }
/**************************
    public void onStart(Intent intent, int startId)
    {
        Log.i(LOGTAG, "onStart started");
        super.onStart(intent, startId);

        Log.i(LOGTAG, "onStart finished");
    }
*************************/
    /**** For after API Level-5 ****/
    public int onStartCommand(Intent intent, int flags, int startId) 
    {
        Log.i(LOGTAG, "onStartCommand started");
        super.onStartCommand(intent, flags, startId);

        Log.i(LOGTAG, "onStartCommand finished");

       return START_STICKY;
    }

    public void onDestroy()
    {
        Log.i(LOGTAG, "onDestroy started");
        Log.i(LOGTAG, "BEFORE stopservice");
        stopservice();
        Log.i(LOGTAG, "BEFORE stopservice");

        super.onDestroy();

        Log.i(LOGTAG, "onDestroy finished");
    }

    private void startservice() 
    {
        if (mServiceStarted)
        {
            return;
        }

        Log.i(LOGTAG, "startservice: before starting daemon engine");

        if (StartEngine() != 0)
        {
            Log.i(LOGTAG, "StartEngine success");
            mServiceStarted = true; // todo: add thread safety

            if (StartMessageListener() > 0)
            {
                Log.i(LOGTAG, "StartMessageListener success");
            }
            else
            {
                Log.i(LOGTAG, "StartMessageListener failed");
            }
        }
        else
        {
            Log.i(LOGTAG, "StartEngine failed");
        }

        Log.i(LOGTAG, "startservice: after starting daemon engine");

        timer.scheduleAtFixedRate(
            new TimerTask()
            {
                public void run()
                {
                    //Do whatever you want to do every Period
                }
           }, 0, mPeriod
        );

    }

    private void stopservice()
    {
        if (!mServiceStarted)
        {
            Log.i(LOGTAG, "stopservice: service not started");
            return;
        }

        if (timer != null)
        {
            timer.cancel();
        }

        if (StopMessageListener() != 0)
        {
            Log.i(LOGTAG, "StopMessageListener success");
        }
        else
        {
            Log.i(LOGTAG, "StopMessageListener failed");
        }

        if (StopEngine() != 0)
        {
            Log.i(LOGTAG, "StopEngine success");
            mServiceStarted = false;
        }
        else
        {
            Log.i(LOGTAG, "StopEngine failed");
        }

       Log.i(LOGTAG, "stopservice: after stopping daemon engine");
    }

    private boolean restoreApplicationDataFromAssets()
    {

        AssetManager assetManager = this.getAssets();
        if (assetManager == null)
        {
            Log.i(LOGTAG, "Can't get AssetManager instance");
            return false;
        }

       boolean res = restoreApplicationDataFileFromAssets(assetManager, "conf.priv", "DataStorage/conf/conf.priv");
       res = res && restoreApplicationDataFileFromAssets(assetManager, "LogRequired.txt", "Logs/LogRequired.txt");
       res = res && restoreApplicationDataFileFromAssets(assetManager, "StdObjs.at", "DataStorage/AccessType/StdObjs.at");

       return res;
    }

    private boolean restoreApplicationDataFileFromAssets(AssetManager mng, String inFile, String outFile)
    {
        try 
        {
            InputStream in = mng.open(inFile);
            if (in != null)
            {
                final int bufferSize = 1024;
                byte[] buffer = new byte[bufferSize];
                outFile = dataPathPrefix + pkgName + File.pathSeparator + outFile;

                // create file path if not exist
                File path = new File(new File(outFile).getParent());
                if (!path.mkdirs())
                 {
                    Log.i(LOGTAG, "File::mkdirs() failed. Path: " + path.getAbsolutePath());
                 }

                // create file and copy data from asset
                OutputStream out = new FileOutputStream(outFile);
                int readBytes = 0;
                while ((readBytes = in.read(buffer)) != -1)
                 {
                    out.write(buffer, 0, readBytes);
                 }
                out.flush();
                out.close();
            }
        }
        catch (IOException err)
        {
            //"Can't read or write file using assetManager. Error message: "
            String strError = err.toString(); 
            Log.i(LOGTAG, strError);
            return false;
        }

        return true;
    }

    private boolean createApplicationDataFilePredefinedPaths(String predefined_path)
    {
        // create file path if not exist
        String str_path = dataPathPrefix + pkgName + File.pathSeparator + predefined_path;
        File path = new File(str_path);
        if (!path.mkdirs())
        {
            Log.i(LOGTAG, "File::mkdirs() failed. Path: " + path.getAbsolutePath());
        }

        return true;
    }

    private boolean initApplicationData()
    {
    	 final File dataDir = getApplicationContext().getFilesDir();
        AssetManager assetManager = getResources().getAssets();
 

        try {
            copyFiles(assetManager, null, initPath, dataDir);
        } catch (IOException e) {
            String strError = "initApplicationData(): " + e.toString();
            Log.i(LOGTAG, strError);
            return false;
         }

       return true;
    }

    private void copyFiles(AssetManager assetManager, final String parentPath, final String filename, final File toDir) throws IOException
    {
        String assetpath = (parentPath != null ? parentPath + File.separator + filename : filename);

        if (isDirectory(assetManager, assetpath)) {
            if (!toDir.exists()) {
                toDir.mkdirs();
                toDir.setExecutable(true, false);
            }
            for (String child : assetManager.list(assetpath))
            {
                File nfile = new File(toDir, child);
                copyFiles(assetManager, assetpath, child, nfile);
                nfile.setReadable(true, false);
                nfile.setWritable(true, false);
            }
        } else {
                File nfile = new File(toDir.getParentFile(), filename);
                copyData(assetManager.open(assetpath), new FileOutputStream(nfile));
                nfile.setReadable(true, false);
                nfile.setWritable(true, false);
        }
    }

    private boolean isDirectory(AssetManager assetManager, final String path) throws IOException
    {
        boolean res = false;

        try {
            if (assetManager.list(path).length > 0) {
                res = true;
            } else {
                assetManager.open(path);
             }
        } catch (FileNotFoundException fnfe) {
            res = true;
        }
        return res;
    }

    private void copyData(final InputStream in, final OutputStream out) throws IOException
    {
        BufferedInputStream bis = new BufferedInputStream(in);
        try {
            writeData(bis, out);
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException ioe) {
                    String strError = "copyData: " + ioe.toString();
                    Log.i(LOGTAG, strError);
                  }
            }
        }
    }

    private void writeData(final InputStream is, final OutputStream os) throws IOException
    {
        final int BUFFER_SIZE = 1024;
        BufferedOutputStream bos = null;

        try {
            bos = new BufferedOutputStream(os);
            byte[] buffer = new byte[BUFFER_SIZE];
            int len = 0;
            while ( (len = is.read(buffer, 0, buffer.length)) > 0) {
                bos.write(buffer, 0, len);
              }
            bos.flush();
        } finally {
            if (bos != null) { 
                try {
                    bos.close();
                } catch (IOException ioe) {
                    String strError = "writeData: " + ioe.toString();
                    Log.i(LOGTAG, strError);
                  }
             }
        }
    }

}
