/*
 * Copyright (c) 2015,2016 Oracle and/or its affiliates.  All rights reserved.
 *
 * This software is dual-licensed to you under the MIT License (MIT) and
 * the Universal Permissive License (UPL).  See the LICENSE file in the root
 * directory for license terms.  You may choose either license, or both.
 */

package com.oracle.iot.sample;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.provider.Settings;
import android.text.Html;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class MainActivity extends Activity {
    /**
     * The primary interface we will be calling on the GD sample service.
     */
    IGatewayDeviceSampleService gdService = null;
    /**
     * Another interface we use on the GD sample service.
     */
    IGatewayDeviceSampleServiceSecondary gdSecondaryService = null;
    TextView sensorInfoText1;
    TextView sensorInfoText2;
    TextView mCallbackText;
    private boolean mIsBound = false;
    private boolean gdServiceStopped = false;
    private int pid;
    private int gdwv_max_hygro_threshold;
    private int last_gdwv_max_hygro_threshold = 0;
    private int gdwv_max_thermo_threshold;
    private int gdwv_min_thermo_threshold;
    private int last_gdwv_max_thermo_threshold = 0;
    private int last_gdwv_min_thermo_threshold = 0;
    private double last_gdwv_max_thermo = 0;
    private double last_gdwv_min_thermo = 0;
    Intent serviceIntent;
    private String startTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        if (currentapiVersion >= 23){ // using 23 since definition for marshmallow is not available in current sdk
            // only applies to marshmallow and above versions
            Intent intent = new Intent();
            String packageName = this.getPackageName();
            if(pm.isIgnoringBatteryOptimizations(getPackageName())) {
                intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
            }else{
                intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + packageName));
            }
            startActivity(intent);
        }

        Button resetButton = (Button) findViewById(R.id.resetButton);
        Button exitButton = (Button)findViewById(R.id.exitButton);
        final SharedPreferences sharedPref = getApplicationContext().getSharedPreferences(
                getString(R.string.preference_file_key), Context.MODE_PRIVATE);
        if(sharedPref.getBoolean(getString(R.string.use_provided_bks), false)){
            resetButton.setVisibility(View.GONE);
        }
        resetButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
                    resetApp();
                }else{
                    AlertDialog dialog;
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder  = builder.setMessage("Application provisioining information will be lost and cannot be recovered. Continue?");
                    builder = builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            // User clicked Try again button
                            dialog.dismiss();
                            resetApp();
                        }
                    });
                    builder = builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            // User cancelled the dialog
                            dialog.dismiss();
                        }
                    });
                    dialog = builder.create();
                    dialog.show();
                }
            }
        });

        exitButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent anIntent = new Intent(MainActivity.this, SelectProvisioningActivity.class);
                anIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                anIntent.putExtra("EXIT", true);
                startActivity(anIntent);
                finish();
            }
        });

        // Configure text view
        mCallbackText = (TextView)findViewById(R.id.Output);
        sensorInfoText1= (TextView) findViewById(R.id.sensorinfo1);
        sensorInfoText2= (TextView) findViewById(R.id.sensorinfo2);
        serviceIntent = new Intent(MainActivity.this, GatewayDeviceSampleService.class);
        startService(serviceIntent);
        gdServiceStopped = false;

        // Establish connections with the service, binding
        // by interface names.  This allows other applications to be
        // installed that replace the remote service by implementing
        // the same interface.
        Intent intent = new Intent(MainActivity.this, GatewayDeviceSampleService.class);
        intent.setAction(IGatewayDeviceSampleService.class.getName());
        bindService(intent, gdServiceConnection, Context.BIND_AUTO_CREATE);
        intent.setAction(IGatewayDeviceSampleServiceSecondary.class.getName());
        bindService(intent, gdServiceSecondaryConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
		SimpleDateFormat fmt = new SimpleDateFormat("MM/dd/yy, hh:mm:ss a", Locale.ROOT);
		startTime = fmt.format(new Date());
    }

    void resetApp(){
        unbindService(gdServiceConnection);
        unbindService(gdServiceSecondaryConnection);
        stopService(serviceIntent);
        SharedPreferences sharedPref = getApplicationContext().getSharedPreferences(
                getString(R.string.preference_file_key), Context.MODE_PRIVATE);
        SharedPreferences.Editor prefsEditor = sharedPref.edit();
        prefsEditor.remove(getString(R.string.ta_file_path));
        deleteFile(getString(R.string.last_known));
        prefsEditor.apply();
        Intent anIntent = new Intent(MainActivity.this, SelectProvisioningActivity.class);
        anIntent.putExtra("RESET", true);
        startActivity(anIntent);
        finish();
    }

    /**
     * Check if a certain service is already running so I can bind/unbind to it and stop it.
     */
    private boolean isServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if(serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Class for interacting with the main interface of the GD Sample service.
     */
    private ServiceConnection gdServiceConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            gdService = IGatewayDeviceSampleService.Stub.asInterface(service);
            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                gdService.registerCallback(gdCallback);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even
                // do anything with it; we can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }

            // As part of the sample, tell the user what happened.
            Toast.makeText(MainActivity.this, R.string.gd_service_connected,
                    Toast.LENGTH_SHORT).show();
        }
        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            gdService = null;
            // As part of the sample, tell the user what happened.
            Toast.makeText(MainActivity.this, R.string.gd_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };
    /**
     * Class for interacting with the secondary interface of the GD Sample service.
     */
    private ServiceConnection gdServiceSecondaryConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            // Connecting to a secondary interface is the same as any
            // other interface.
            gdSecondaryService = IGatewayDeviceSampleServiceSecondary.Stub.asInterface(service);
        }
        public void onServiceDisconnected(ComponentName className) {
            gdSecondaryService = null;
        }
    };

    /**
     * This implementation is used to receive callbacks from the GD Sample
     * service.
     */
    private IGatewayDeviceSampleServiceCallback gdCallback =
            new IGatewayDeviceSampleServiceCallback.Stub() {
        /**
         * This is called by the remote service regularly to tell us about
         * new values.
         */
        public void valueChanged(String value) {
            mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value));
        }
        /**
         * This is called by the remote service in order to tell us the
         * hygrometer max threshold value when it has been asked for.
         */
        public void maxGdHygro(String value) {
            mHandler.sendMessage(mHandler.obtainMessage(GD_MAX_HYGRO_MSG, value));
        }
        /**
         * This is called by the remote service in order to tell us the
         * thermometer max threshold value when it has been asked for.
         */
        public void maxGdThermo(String value) {
            mHandler.sendMessage(mHandler.obtainMessage(GD_MAX_THERMO_MSG, value));
        }
        /**
         * This is called by the remote service to display error messages.
         */
        public void errorMessage(String value) {
            mHandler.sendMessage(mHandler.obtainMessage(ERROR_MSG, value));
        }
    };

    private static final int BUMP_MSG = 1;
    private static final int GD_MAX_HYGRO_MSG = 4;
    private static final int GD_MAX_THERMO_MSG = 5;
    private static final int ERROR_MSG = 6;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BUMP_MSG:
                    mCallbackText
                            .append(msg.obj+"\n");
                    if(msg.obj.toString().contains("Set"))
                        if(msg.obj.toString().contains("humidity"))
                            sensorInfoText1.setText(Html.fromHtml(parseSensorInfo(msg.obj.toString(), true)));
                        else if(msg.obj.toString().contains("temp"))
                            sensorInfoText2.setText(Html.fromHtml(parseSensorInfo(msg.obj.toString(), false)));
                    break;
                case GD_MAX_HYGRO_MSG:
                    gdwv_max_hygro_threshold = Integer.parseInt(msg.obj.toString());
                    if (last_gdwv_max_hygro_threshold == 0) {
                        // this is our first received max threshold value
                        // during this binding
                        last_gdwv_max_hygro_threshold = gdwv_max_hygro_threshold;
                    } else {
                        // otherwise alter the content of the EditText
                        // only in case the value has changed
                        if (last_gdwv_max_hygro_threshold != gdwv_max_hygro_threshold) {
                            last_gdwv_max_hygro_threshold = gdwv_max_hygro_threshold;
                        }
                    }
                    break;
                case GD_MAX_THERMO_MSG:
                    String[] gdThermoToBeShown = msg.obj.toString().split(":");
                    gdwv_max_thermo_threshold = Integer.parseInt(gdThermoToBeShown[0]);
                    gdwv_min_thermo_threshold = Integer.parseInt(gdThermoToBeShown[1]);
                    if(last_gdwv_max_thermo_threshold == 0) {
                        // this is our first received max threshold value
                        // during this binding
                        last_gdwv_max_thermo_threshold = gdwv_max_thermo_threshold;
                    } else {
                        // otherwise alter the content of the EditText
                        // only in case the value has changed
                        if(last_gdwv_max_thermo_threshold != gdwv_max_thermo_threshold) {
                            last_gdwv_max_thermo_threshold = gdwv_max_thermo_threshold;
                        }
                    }

                    if(last_gdwv_min_thermo_threshold != gdwv_min_thermo_threshold) {
                        last_gdwv_min_thermo_threshold = gdwv_min_thermo_threshold;
                    }

                    break;
                case ERROR_MSG:
                    // As part of the sample, tell the user what happened.
                    String errorMsg = msg.obj.toString();
                    mCallbackText
                            .append("\nERROR: " +  errorMsg);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }

        private String parseSensorInfo(String s,boolean humiditySensor){
            String info="";
            String[] result = s.split(":");
			// Don't need to check first 6 values
			// (First 3 is time hh:mm:ss, the 4th is ID, the 5th is "Set", the 6th is "temp"=%NUMBER%)
			for (int i = 6; i < result.length; i++) {
				if (result[i].contains("minTemp")) {
					String number = result[i].split("=")[1].replaceAll("^\"|\"$", "");
					last_gdwv_min_thermo = Double.parseDouble(number);
				} else if (result[i].contains("maxTemp")) {
					String number = result[i].split("=")[1].replaceAll("^\"|\"$", "");
					last_gdwv_max_thermo = Double.parseDouble(number);
				}
			}
            if (humiditySensor) {
                info+="<b>Humidity Sensor "+result[3]+"</b><br>";
                info += "Current Value " + result[5].split("=")[1] + "%<br>";
                info += "Max Threshold " + last_gdwv_max_hygro_threshold+"%";
            }else{
                info += "<b>Temperature Sensor " + result[3] + "</b><br>";
                info += "Current Value " + result[5].split("=")[1] + " °C<br>";
                info += "Min/Max Values " + last_gdwv_min_thermo+"/"+last_gdwv_max_thermo + " °C<br>";
                info += "Min/Max Threshold " + last_gdwv_min_thermo_threshold+"/"+last_gdwv_max_thermo_threshold + " °C<br>";
                info += "Start Time " + startTime;
            }
            return info;
        }


    };
}
