/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * 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 org.wso2.carbon.iot.android.sense.data.publisher;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.wso2.carbon.iot.android.sense.beacon.BeaconScanedData;
import org.wso2.carbon.iot.android.sense.constants.SenseConstants;
import org.wso2.carbon.iot.android.sense.data.publisher.mqtt.AndroidSenseMQTTHandler;
import org.wso2.carbon.iot.android.sense.data.publisher.mqtt.transport.MQTTTransportHandler;
import org.wso2.carbon.iot.android.sense.data.publisher.mqtt.transport.TransportHandlerException;
import org.wso2.carbon.iot.android.sense.event.streams.activity.ActivityData;
import org.wso2.carbon.iot.android.sense.event.streams.application.ApplicationData;
import org.wso2.carbon.iot.android.sense.event.streams.audio.AudioData;
import org.wso2.carbon.iot.android.sense.event.streams.battery.BatteryData;
import org.wso2.carbon.iot.android.sense.event.streams.call.CallData;
import org.wso2.carbon.iot.android.sense.event.streams.data.NetworkData;
import org.wso2.carbon.iot.android.sense.event.streams.location.LocationData;
import org.wso2.carbon.iot.android.sense.event.streams.screen.ScreenData;
import org.wso2.carbon.iot.android.sense.event.streams.sensor.SensorData;
import org.wso2.carbon.iot.android.sense.event.streams.sms.SmsData;
import org.wso2.carbon.iot.android.sense.event.streams.speed.SpeedData;
import org.wso2.carbon.iot.android.sense.speech.detector.util.ProcessWords;
import org.wso2.carbon.iot.android.sense.speech.detector.util.WordData;
import org.wso2.carbon.iot.android.sense.util.LocalRegistry;
import org.wso2.carbon.iot.android.sense.util.SenseDataHolder;

import java.util.ArrayList;
import java.util.List;

/**
 * This is an android service which publishes the data to the server.
 */
public class DataPublisherService extends Service {
    private final String TAG = DataPublisherService.class.getName();
    private Context context;

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        context = this;
        Log.d(TAG, "service started");
        try {
            List<Event> events = new ArrayList<>();
            //retrieve sensor data.
            List<SensorData> sensorDataMap = SenseDataHolder.getSensorDataHolder();
            if (!sensorDataMap.isEmpty()) {
                for (SensorData sensorData : sensorDataMap) {
                    Event event = new Event();
                    event.setTimestamp(sensorData.getTimestamp());
                    switch (sensorData.getSensorType()) {
                        case Sensor.TYPE_ACCELEROMETER:
                            event.setAccelerometer(sensorData.getSensorValues());
                            events.add(event);
                            break;
                        case Sensor.TYPE_MAGNETIC_FIELD:
                            event.setMagnetic(sensorData.getSensorValues());
                            events.add(event);
                            break;
                        case Sensor.TYPE_GYROSCOPE:
                            event.setGyroscope(sensorData.getSensorValues());
                            events.add(event);
                            break;
                        case Sensor.TYPE_LIGHT:
                            event.setLight(sensorData.getSensorValues()[0]);
                            break;
                        case Sensor.TYPE_PRESSURE:
                            event.setPressure(sensorData.getSensorValues()[0]);
                            events.add(event);
                            break;
                        case Sensor.TYPE_PROXIMITY:
                            event.setProximity(sensorData.getSensorValues()[0]);
                            events.add(event);
                            break;
                        case Sensor.TYPE_GRAVITY:
                            event.setGravity(sensorData.getSensorValues());
                            events.add(event);
                            break;
                        case Sensor.TYPE_GAME_ROTATION_VECTOR:
                            event.setRotation(sensorData.getSensorValues());
                            events.add(event);
                            break;
                    }
                }
            }
            SenseDataHolder.resetSensorDataHolder();

            //retrieve battery data.
            List<BatteryData> batteryDataMap = SenseDataHolder.getBatteryDataHolder();
            if (!batteryDataMap.isEmpty()) {
                for (BatteryData batteryData : batteryDataMap) {
                    Event event = new Event();
                    event.setTimestamp(batteryData.getTimestamp());
                    event.setBattery(batteryData.getLevel());
                    event.setBatteryTemperature(batteryData.getTemperature());
                    event.setBatteryStatus(batteryData.getStatus());
                    event.setBatteryState(batteryData.getState().toString());
                    events.add(event);
                }
            }
            SenseDataHolder.resetBatteryDataHolder();

            //retrieve location data.
            List<LocationData> locationDataMap = SenseDataHolder.getLocationDataHolder();

            if (!locationDataMap.isEmpty()) {
                for (LocationData locationData : locationDataMap) {
                    Event event = new Event();
                    event.setTimestamp(locationData.getTimeStamp());
                    event.setGps(new double[]{locationData.getLatitude(), locationData.getLongitude()});
                    events.add(event);
                }
            }
            SenseDataHolder.resetLocationDataHolder();

            //retrieve speed data.
            List<SpeedData> speedDataMap = SenseDataHolder.getSpeedDataHolder();
            if (!speedDataMap.isEmpty()) {
                for (SpeedData speedData : speedDataMap) {
                    Event event = new Event();
                    event.setTimestamp(speedData.getTimeStamp());
                    event.setTurns(speedData.getTurns());
                    event.setSpeed(speedData.getSpeed());

                    events.add(event);
                }
            }
            SenseDataHolder.resetSpeedDataHolder();

            //retrieve speed data.
            List<BeaconScanedData> beaconDataMap = SenseDataHolder.getBeaconScanedDataHolder();
            if (!speedDataMap.isEmpty()) {
                for (BeaconScanedData beaconData : beaconDataMap) {
                    Event event = new Event();
                    event.setBeaconMajor(beaconData.getBeaconMajor());
                    event.setBeaconMinor(beaconData.getBeaconMinor());
                    event.setBeaconProximity(beaconData.getBeaconProximity());
                    event.setBeaconUuid(beaconData.getBeaconUuid());

                    events.add(event);
                }
            }
            SenseDataHolder.resetSpeedDataHolder();

            //retrieve words
            ProcessWords.cleanAndPushToWordMap();
            List<WordData> wordDatMap = SenseDataHolder.getWordDataHolder();
            for (WordData wordData : wordDatMap) {
                if (wordData.getOccurences() == 0) {
                    continue;
                }
                for (int i = 0; i < wordData.getOccurences(); i++) {
                    Event event = new Event();
                    event.setTimestamp(wordData.getTimestamp());
                    event.setWord(wordData.getWord());
                    String word = wordData.getWord();
                    String status = word;
                    if ((!word.equals(SenseConstants.EVENT_LISTENER_STARTED)) && (!word.equals(SenseConstants
                            .EVENT_LISTENER_FINISHED))) {
                        status = SenseConstants.EVENT_LISTENER_ONGOING;
                    }
                    event.setWordStatus(status);
                    events.add(event);
                }
            }
            SenseDataHolder.resetWordDataHolder();

            // retrieve call data.
            List<CallData> callDataList = SenseDataHolder.getCallDataHolder();
            if (!callDataList.isEmpty()) {
                for (CallData callData : callDataList) {
                    Event event = new Event();
                    event.setCallNumber(callData.getPhoneNumber());
                    event.setCallType(callData.getType().toString().toLowerCase());
                    event.setCallStartTime(callData.getStartTime());
                    event.setCallEndTime(callData.getEndTime());
                    event.setTimestamp(callData.getStartTime());

                    events.add(event);
                }
            }
            SenseDataHolder.resetCallDataHolder();

            // retrieve screen data.
            List<ScreenData> screenDataList = SenseDataHolder.getScreenDataHolder();
            if (!screenDataList.isEmpty()) {
                for (ScreenData screenData : screenDataList) {
                    Event event = new Event();
                    event.setScreenState(screenData.getAction());
                    event.setTimestamp(screenData.getTimestamp());

                    events.add(event);
                }
            }
            SenseDataHolder.resetScreenDataHolder();

            // retrieve audio data.
            List<AudioData> audioDataList = SenseDataHolder.getAudioDataHolder();
            if (!audioDataList.isEmpty()) {
                for (AudioData audioData : audioDataList) {
                    Event event = new Event();
                    event.setTimestamp(audioData.getTimestamp());
                    event.setAudioPlaying(audioData.isPlaying());
                    event.setHeadsetOn(audioData.isHeadsetOn());
                    event.setMusicVolume(audioData.getMusicVolume());
                    events.add(event);
                }
            }
            SenseDataHolder.resetAudioDataHolder();

            // retrieve activity data.
            List<ActivityData> activityDataList = SenseDataHolder.getActivityDataHolder();
            if (!activityDataList.isEmpty()) {
                for (ActivityData activityData : activityDataList) {
                    Event event = new Event();
                    event.setTimestamp(activityData.getTimestamp());
                    event.setActivityType(activityData.getActivity());
                    event.setConfidence(activityData.getConfidence());
                    events.add(event);
                }
            }
            SenseDataHolder.resetActivityDataHolder();

            // retrieve sms data.
            List<SmsData> smsDataList = SenseDataHolder.getSmsDataHolder();
            if (!smsDataList.isEmpty()) {
                for (SmsData smsData : smsDataList) {
                    Event event = new Event();
                    event.setTimestamp(smsData.getTimestamp());
                    event.setSmsNumber(smsData.getPhoneNumber());
                    events.add(event);
                }
            }
            SenseDataHolder.resetSmsDataHolder();


            // retrieve application data.
            List<ApplicationData> appDataList = SenseDataHolder.getApplicationDataHolder();
            if (!appDataList.isEmpty()) {
                for (ApplicationData appData : appDataList) {
                    Event event = new Event();
                    event.setTimestamp(appData.getTimestamp());
                    event.setPackageName(appData.getPackageName());
                    event.setAction(appData.getAction().toString());
                    events.add(event);
                }
            }
            SenseDataHolder.resetApplicationDataHolder();

            //Retrieve network data
            List<NetworkData> networkDataList = SenseDataHolder.getNetworkDataHolder();
            if (!networkDataList.isEmpty()) {
                for (NetworkData networkData : networkDataList) {
                    Event event = new Event();
                    event.setTimestamp(networkData.getTimeStamp());
                    event.setDataType(networkData.getDataType());
                    event.setDataReceived(networkData.getDataReceived());
                    event.setDataSent(networkData.getDataSent());
                    events.add(event);
                }
            }
            SenseDataHolder.resetNetworkDataHolder();

            //publish the data
            if (events.size() > 0 && LocalRegistry.isEnrolled(context)) {
                String user = LocalRegistry.getUsername(context);
                String deviceId = LocalRegistry.getDeviceId(context);
                JSONArray jsonArray = new JSONArray();
                for (Event event : events) {
                    event.setOwner(user);
                    event.setDeviceId(deviceId);
                    jsonArray.put(new JSONObject().put("event", event.getEvent()));
                }

                MQTTTransportHandler mqttTransportHandler = AndroidSenseMQTTHandler.getInstance(context);
                if (!mqttTransportHandler.isConnected()) {
                    mqttTransportHandler.connect();
                }
                String topic = LocalRegistry.getTenantDomain(context) + "/" + SenseConstants.DEVICE_TYPE + "/" + deviceId + "/data";
                mqttTransportHandler.publishDeviceData(user, deviceId, jsonArray.toString(), topic);
            }
        } catch (JSONException e) {
            Log.e(TAG, "Json Data Parsing Exception", e);
        } catch (TransportHandlerException e) {
            Log.e(TAG, "Data Publish Failed", e);
        }
        return Service.START_NOT_STICKY;
    }
}