/*
 * Copyright (c) 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.enterpriseclientsample;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.oracle.iot.sample.R;

import com.oracle.iot.client.DeviceModelAttribute;
import com.oracle.iot.client.impl.DeviceModelImpl;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import oracle.iot.client.DeviceModel;
import oracle.iot.client.enterprise.Device;
import oracle.iot.client.enterprise.EnterpriseClient;
import oracle.iot.client.enterprise.VirtualDevice;

public class HumiditySensorActivity extends Activity {
    private static final String ERROR_TAG = "IOT_ERROR";
    private static final String MSG_TAG = "IOT";
    private static final String MAX_THRESHOLD_ATTRIBUTE = "maxThreshold";
    private static final String HUMIDITY_ATTRIBUTE_NAME = "humidity";
    private VirtualDevice vDevice;
    TextView currentHumidity;
    TextView humiditytThreshold;
    TextView manufacturer;
    TextView modelNumber;
    TextView humidAlert;
    Context context;
    String deviceId;
    Button applyThreshold;
    TextView newHumiditytThreshold;
    String newThreshold;

    // Map model format URN to its name
    private static final Map<String,String> formatNames = new HashMap<String, String>() {
        {
            put("urn:com:oracle:iot:device:humidity_sensor:too_humid", "tooHumidAlert");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_humidity_sensor);
        EnterpriseClientUtil util = EnterpriseClientUtil.getInstance(this);
        util.setCurrentContext(this);
        deviceId = util.getSelectedDeviceToMonitor().device.getId();
        util.getSelectedDeviceToMonitor().device.getValue(Device.Field.MANUFACTURER);
        TextView deviceNameTV = (TextView)findViewById(R.id.deviceTitle);
        deviceNameTV.setText(deviceId);
        currentHumidity = (TextView)findViewById(R.id.humidityValue);
        humiditytThreshold = (TextView)findViewById(R.id.humidityThresholdValue);
        newHumiditytThreshold = (TextView)findViewById(R.id.newThresholdValue);
        manufacturer = (TextView)findViewById(R.id.manufacturer);
        modelNumber= (TextView)findViewById(R.id.modelNumber);
        manufacturer.setText("Manufacturer: "+util.getSelectedDeviceToMonitor().device.getValue(Device.Field.MANUFACTURER));
        modelNumber.setText( "Model Number: "+util.getSelectedDeviceToMonitor().device.getValue(Device.Field.MODEL_NUMBER));
        humidAlert = (TextView)findViewById(R.id.tooHumidAlert);
        humidAlert.setVisibility(View.INVISIBLE);
        applyThreshold = (Button)findViewById(R.id.thresholdButton);
        applyThreshold.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                updateThreshold();
            }
        });
        newHumiditytThreshold.setOnKeyListener(new View.OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                // If the event is a key-down event on the "enter" button
                if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
                        (keyCode == KeyEvent.KEYCODE_ENTER)) {
                    // Perform action on key press
                    updateThreshold();
                    return true;
                }
                return false;
            }
        });
        final SensorOperation sensorThread = new SensorOperation(getApplicationContext());
        sensorThread.execute(this);
    }

    private void updateThreshold(){
        try {

            Log.d(MSG_TAG, "Setting New Threshold -> " + newHumiditytThreshold.getText().toString());
            newThreshold = newHumiditytThreshold.getText().toString();
            new Thread()
            {
                @Override
                public void run()
                {
                    vDevice.set(MAX_THRESHOLD_ATTRIBUTE,Integer.parseInt(newThreshold));
                    super.run();
                }
            }.start();
        }catch(Exception e){
            Log.d(ERROR_TAG, e.getMessage());
        }
    }
    private class SensorOperation extends AsyncTask<Context, String, Void> {
        DeviceListItem dli = EnterpriseClientUtil.getInstance(null).getSelectedDeviceToMonitor();
        EnterpriseClient client;
        String humidity;
        String max;
        String min;
        int pickMin;
        DeviceModel deviceModel = null;
        String modelUrn = null;
        private boolean publishFinish;

        public SensorOperation (Context mContext){
            context = mContext;
            humidity = "50";
            max = "100";
            min = "0";
            pickMin = Integer.parseInt(min);
        }

        @Override
        protected Void doInBackground(Context... params) {
            try {
                client = EnterpriseClientUtil.getInstance(null).getECL();
            }catch(Exception e){
                vDevice = null;
                publishProgress("No valid device Id!");
                return null;
            }
            if (deviceId == null) {
                vDevice = null;
                publishProgress("No valid device Id!");
                return null;
            }


            try {
                modelUrn = EnterpriseClientUtil.getInstance(null).getSelectedDeviceToMonitor().deviceModel.urn;
                deviceModel = client.getDeviceModel(modelUrn);
                vDevice = client.createVirtualDevice(deviceId, deviceModel);
                publishFinish = false;
                publishProgress("Init " + modelUrn);
                while (publishFinish == false) {
                    Thread.currentThread().sleep(200);
                }
                setupHandlers(vDevice);
            } catch (Exception e) {
                Log.d(ERROR_TAG, e.getMessage());
            }

            return null;
        }

        @Override
            protected void onProgressUpdate(String... s) {
            if(vDevice == null) {
                humidAlert.setText(s[0]);
                humidAlert.setVisibility(View.VISIBLE);
                return;
            }
            if(s[0].contains("Init")) {
                Map<String, DeviceModelAttribute> atts = ((DeviceModelImpl) deviceModel).getDeviceModelAttributes();
                DeviceModelAttribute dma = atts.get(MAX_THRESHOLD_ATTRIBUTE);
                max = vDevice.get(MAX_THRESHOLD_ATTRIBUTE).toString();
                humiditytThreshold.setText(max.trim());
                publishFinish = true;
                return;
            }
            try {
                if(s[0].contains("onChange")) {
                    humidity = vDevice.get(HUMIDITY_ATTRIBUTE_NAME).toString();
                    max = vDevice.get(MAX_THRESHOLD_ATTRIBUTE).toString();
                    if((Integer)vDevice.get(HUMIDITY_ATTRIBUTE_NAME) < Integer.parseInt(max.trim()))
                        humidAlert.setVisibility(View.INVISIBLE);
                    currentHumidity.setText(humidity.trim());
                    humiditytThreshold.setText(max.trim());
                    Log.d(MSG_TAG, "Humidity -> " + humidity);
                    Log.d(MSG_TAG, "Maximum threshold -> " + max);
                }


                if(s[0].contains("onAlert")) {
                    int idxStart = s[0].indexOf("(");
                    int idxEnd = s[0].indexOf(")");
                    String alert = s[0].substring(idxStart + 1, idxEnd);

                    switch(alert) {
                        case "tooHumidAlert":
                            Log.d(MSG_TAG, "Setting too humid Message");
                            humidAlert.setVisibility(View.VISIBLE);
                            break;
                    }

                    Log.d(MSG_TAG, "Alert -> " + alert);
                }

            } catch (Exception e) {
                Log.d(ERROR_TAG, e.getMessage());
            }
        }

        private void setupHandlers(VirtualDevice device) {

            setupHandlers(device, false);
        }

        AtomicBoolean breakWaitLoop = new AtomicBoolean(false);

        private void setupHandlers(VirtualDevice device,
                                   final boolean displayOnlyThreshold) {
            device.setOnChange(new VirtualDevice.ChangeCallback<VirtualDevice>() {
                public void onChange(VirtualDevice.ChangeEvent<VirtualDevice> event) {
                    VirtualDevice virtualDevice = event.getVirtualDevice();

                    boolean maxThresholdChanged = false;
                    StringBuilder msg = new StringBuilder(new Date().toString());
                    msg.append(": ");
                    msg.append(virtualDevice.getEndpointId());
                    msg.append(" : onChange : ");
                    boolean first = true;
                    VirtualDevice.NamedValue<?> namedValue = event.getNamedValue();
                    while (namedValue != null) {
                        String attributeName = namedValue.getName();
                        Object attributeValue = namedValue.getValue();

                        if (!first) {
                            msg.append(',');
                        } else {
                            first = false;
                        }

                        maxThresholdChanged |= MAX_THRESHOLD_ATTRIBUTE.equals(attributeName);
                        msg.append('\"');
                        msg.append(attributeName);
                        msg.append("\"=");
                        msg.append(String.valueOf(attributeValue));

                        namedValue = namedValue.next();
                    }

                    if (displayOnlyThreshold) {
                        msg.insert(0, System.getProperty("line.separator"));
                    }

                    Log.d(MSG_TAG, msg.toString());
                    breakWaitLoop.set(displayOnlyThreshold && maxThresholdChanged);

                    publishProgress(msg.toString());
                }
            });
            // set a callback for any alert that may be raised for this virtual device
            device.setOnAlert(
                    new VirtualDevice.AlertCallback() {
                        @Override
                        public void onAlert(VirtualDevice.AlertEvent event) {
                            VirtualDevice virtualDevice = event.getVirtualDevice();

                            StringBuilder msg = new StringBuilder(new Date().toString());
                            msg.append(" : ");
                            msg.append(virtualDevice.getEndpointId());
                            msg.append(" : onAlert : ");

                            boolean first = true;
                            VirtualDevice.NamedValue<?> namedValue = event.getNamedValue();
                            while (namedValue != null) {
                                String name = namedValue.getName();
                                Object value = namedValue.getValue();

                                if (!first) {
                                    msg.append(',');
                                } else {
                                    first = false;
                                }

                                msg.append('\"');
                                msg.append(name);
                                msg.append("\"=");
                                msg.append(String.valueOf(value));

                                namedValue = namedValue.next();
                            }

                            final String alertName = formatNames.get(event.getURN());
                            if (alertName != null) {
                                msg.append(" (")
                                        .append(alertName)
                                        .append(")");

                            }

                            if (displayOnlyThreshold) {
                                msg.insert(0, System.getProperty("line.separator"));
                            }

                            Log.d(MSG_TAG, msg.toString());
                            publishProgress(msg.toString());
                        }
                    });

            device.setOnError(
                    new VirtualDevice.ErrorCallback<VirtualDevice>() {
                        public void onError(VirtualDevice.ErrorEvent event) {
                            VirtualDevice device = (VirtualDevice)event.getVirtualDevice();
                            StringBuilder msg = new StringBuilder(new Date().toString() + " : onError : " +
                                    device.getEndpointId() + " : \"" + event.getMessage());

                            // Normally some error processing would go here,
                            // but since this a sample, notify the main thread
                            // to end the sample.
                            breakWaitLoop.set(true);

                            publishProgress(msg.toString());
                        }
                    });
        }
    }
}
