/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you 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.device.mgt.core.device.details.mgt.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO;
import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException;
import org.wso2.carbon.device.mgt.core.geo.geoHash.GeoHashGenerator;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public class DeviceDetailsDAOImpl implements DeviceDetailsDAO {

    private static Log log = LogFactory.getLog(DeviceDetailsDAOImpl.class);

    @Override
    public void addDeviceInformation(int deviceId, int enrolmentId, DeviceInfo deviceInfo)
            throws DeviceDetailsMgtDAOException {

        Connection conn;
        PreparedStatement stmt = null;
        try {
            conn = this.getConnection();

            stmt = conn.prepareStatement("INSERT INTO DM_DEVICE_DETAIL (DEVICE_ID, DEVICE_MODEL, " +
                    "VENDOR, OS_VERSION, OS_BUILD_DATE, BATTERY_LEVEL, INTERNAL_TOTAL_MEMORY, INTERNAL_AVAILABLE_MEMORY, " +
                    "EXTERNAL_TOTAL_MEMORY, EXTERNAL_AVAILABLE_MEMORY,  CONNECTION_TYPE, " +
                    "SSID, CPU_USAGE, TOTAL_RAM_MEMORY, AVAILABLE_RAM_MEMORY, PLUGGED_IN, UPDATE_TIMESTAMP, ENROLMENT_ID) " +
                    "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");

            stmt.setInt(1, deviceId);
            stmt.setString(2, deviceInfo.getDeviceModel());
            stmt.setString(3, deviceInfo.getVendor());
            stmt.setString(4, deviceInfo.getOsVersion());
            stmt.setString(5, deviceInfo.getOsBuildDate());
            stmt.setDouble(6, deviceInfo.getBatteryLevel());
            stmt.setDouble(7, deviceInfo.getInternalTotalMemory());
            stmt.setDouble(8, deviceInfo.getInternalAvailableMemory());
            stmt.setDouble(9, deviceInfo.getExternalTotalMemory());
            stmt.setDouble(10, deviceInfo.getExternalAvailableMemory());
            stmt.setString(11, deviceInfo.getConnectionType());
            stmt.setString(12, deviceInfo.getSsid());
            stmt.setDouble(13, deviceInfo.getCpuUsage());
            stmt.setDouble(14, deviceInfo.getTotalRAMMemory());
            stmt.setDouble(15, deviceInfo.getAvailableRAMMemory());
            stmt.setBoolean(16, deviceInfo.isPluggedIn());
            stmt.setLong(17, System.currentTimeMillis());
            stmt.setInt(18, enrolmentId);

            stmt.execute();

        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while inserting device details to database.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, null);
        }

    }

    @Override
    public void addDeviceProperties(Map<String, String> propertyMap, int deviceId, int enrolmentId)
            throws DeviceDetailsMgtDAOException {

        if (propertyMap.isEmpty()) {
            if(log.isDebugEnabled()) {
                log.debug("Property map of device id :" + deviceId + " is empty.");
            }
            return;
        }
        Connection conn;
        PreparedStatement stmt = null;
        try {
            conn = this.getConnection();
            stmt = conn.prepareStatement("INSERT INTO DM_DEVICE_INFO (DEVICE_ID, KEY_FIELD, VALUE_FIELD, ENROLMENT_ID) " +
                    "VALUES (?, ?, ?, ?)");

            for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
                stmt.setInt(1, deviceId);
                stmt.setString(2, entry.getKey());
                stmt.setString(3, entry.getValue());
                stmt.setInt(4, enrolmentId);
                stmt.addBatch();
            }
            stmt.executeBatch();
        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while inserting device properties to database.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, null);
        }

    }

    @Override
    public DeviceInfo getDeviceInformation(int deviceId, int enrolmentId) throws DeviceDetailsMgtDAOException {
        Connection conn;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        DeviceInfo deviceInfo = null;
        try {
            conn = this.getConnection();

            String sql = "SELECT * FROM DM_DEVICE_DETAIL WHERE DEVICE_ID = ? AND ENROLMENT_ID = ?";
            stmt = conn.prepareStatement(sql);
            stmt.setInt(1, deviceId);
            stmt.setInt(2, enrolmentId);
            rs = stmt.executeQuery();

            if (rs.next()) {
                deviceInfo = new DeviceInfo();
//                deviceInfo.setIMEI(rs.getString("IMEI"));
//                deviceInfo.setIMSI(rs.getString("IMSI"));
                deviceInfo.setDeviceModel(rs.getString("DEVICE_MODEL"));
                deviceInfo.setVendor(rs.getString("VENDOR"));
                deviceInfo.setOsVersion(rs.getString("OS_VERSION"));
                deviceInfo.setOsBuildDate(rs.getString("OS_BUILD_DATE"));
                deviceInfo.setBatteryLevel(rs.getDouble("BATTERY_LEVEL"));
                deviceInfo.setInternalTotalMemory(rs.getDouble("INTERNAL_TOTAL_MEMORY"));
                deviceInfo.setInternalAvailableMemory(rs.getDouble("INTERNAL_AVAILABLE_MEMORY"));
                deviceInfo.setExternalTotalMemory(rs.getDouble("EXTERNAL_TOTAL_MEMORY"));
                deviceInfo.setExternalAvailableMemory(rs.getDouble("EXTERNAL_AVAILABLE_MEMORY"));
//                deviceInfo.setOperator(rs.getString("OPERATOR"));
                deviceInfo.setConnectionType(rs.getString("CONNECTION_TYPE"));
//                deviceInfo.setMobileSignalStrength(rs.getDouble("MOBILE_SIGNAL_STRENGTH"));
                deviceInfo.setSsid(rs.getString("SSID"));
                deviceInfo.setCpuUsage(rs.getDouble("CPU_USAGE"));
                deviceInfo.setTotalRAMMemory(rs.getDouble("TOTAL_RAM_MEMORY"));
                deviceInfo.setAvailableRAMMemory(rs.getDouble("AVAILABLE_RAM_MEMORY"));
                deviceInfo.setPluggedIn(rs.getBoolean("PLUGGED_IN"));
                deviceInfo.setUpdatedTime(new java.util.Date(rs.getLong("UPDATE_TIMESTAMP")));
            }

            return deviceInfo;
        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while fetching the details of the registered devices.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
        }
    }

    @Override
    public Map<String, String> getDeviceProperties(int deviceId, int enrolmentId) throws DeviceDetailsMgtDAOException {

        Connection conn;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        Map<String, String> map = new HashMap<>();
        try {
            conn = this.getConnection();
            String sql = "SELECT * FROM  DM_DEVICE_INFO WHERE DEVICE_ID = ? AND ENROLMENT_ID = ?";
            stmt = conn.prepareStatement(sql);
            stmt.setInt(1, deviceId);
            stmt.setInt(2, enrolmentId);
            rs = stmt.executeQuery();

            while (rs.next()) {
                map.put(rs.getString("KEY_FIELD"), rs.getString("VALUE_FIELD"));
            }
        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while fetching the properties of the registered devices.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
        }
        return map;
    }

    @Override
    public void deleteDeviceInformation(int deviceId, int enrollmentId) throws DeviceDetailsMgtDAOException {

        Connection conn;
        PreparedStatement stmt = null;
        try {
            conn = this.getConnection();
            String query = "DELETE FROM DM_DEVICE_DETAIL WHERE DEVICE_ID = ? AND ENROLMENT_ID = ?";
            stmt = conn.prepareStatement(query);
            stmt.setInt(1, deviceId);
            stmt.setInt(2, enrollmentId);
            stmt.executeUpdate();

        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while deleting the device information from the data base.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, null);
        }
    }

    @Override
    public void deleteDeviceProperties(int deviceId, int enrollmentId) throws DeviceDetailsMgtDAOException {

        Connection conn;
        PreparedStatement stmt = null;
        try {
            conn = this.getConnection();
            String query = "DELETE FROM DM_DEVICE_INFO WHERE DEVICE_ID = ? AND ENROLMENT_ID = ?";
            stmt = conn.prepareStatement(query);
            stmt.setInt(1, deviceId);
            stmt.setInt(2, enrollmentId);
            stmt.executeUpdate();

        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while deleting the device properties from the data base.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, null);
        }
    }

    @Override
    public void addDeviceLocation(DeviceLocation deviceLocation, int enrollmentId) throws DeviceDetailsMgtDAOException {

        Connection conn;
        PreparedStatement stmt = null;
        try {
            conn = this.getConnection();
            stmt = conn.prepareStatement("INSERT INTO DM_DEVICE_LOCATION (DEVICE_ID, LATITUDE, LONGITUDE, STREET1, " +
                    "STREET2, CITY, ZIP, STATE, COUNTRY, GEO_HASH, UPDATE_TIMESTAMP, ENROLMENT_ID) " +
                    "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
            stmt.setInt(1, deviceLocation.getDeviceId());
            stmt.setDouble(2, deviceLocation.getLatitude());
            stmt.setDouble(3, deviceLocation.getLongitude());
            stmt.setString(4, deviceLocation.getStreet1());
            stmt.setString(5, deviceLocation.getStreet2());
            stmt.setString(6, deviceLocation.getCity());
            stmt.setString(7, deviceLocation.getZip());
            stmt.setString(8, deviceLocation.getState());
            stmt.setString(9, deviceLocation.getCountry());
            stmt.setString(10, GeoHashGenerator.encodeGeohash(deviceLocation));
            stmt.setLong(11, System.currentTimeMillis());
            stmt.setInt(12, enrollmentId);
            stmt.execute();
        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while adding the device location to database.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, null);
        }
    }

    @Override
    public DeviceLocation getDeviceLocation(int deviceId, int enrollmentId) throws DeviceDetailsMgtDAOException {

        Connection conn;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        DeviceLocation location = new DeviceLocation();
        try {
            conn = this.getConnection();
            String sql = "SELECT * FROM  DM_DEVICE_LOCATION WHERE DEVICE_ID = ? AND ENROLMENT_ID = ?";
            stmt = conn.prepareStatement(sql);
            stmt.setInt(1, deviceId);
            stmt.setInt(2, enrollmentId);
            rs = stmt.executeQuery();

            while (rs.next()) {
                location.setDeviceId(deviceId);
                location.setLatitude(rs.getDouble("LATITUDE"));
                location.setLongitude(rs.getDouble("LONGITUDE"));
                location.setStreet1(rs.getString("STREET1"));
                location.setStreet2(rs.getString("STREET2"));
                location.setCity(rs.getString("CITY"));
                location.setZip(rs.getString("ZIP"));
                location.setState(rs.getString("STATE"));
                location.setCountry(rs.getString("COUNTRY"));
                location.setUpdatedTime(new java.util.Date(rs.getLong("UPDATE_TIMESTAMP")));
            }
            location.setDeviceId(deviceId);

            return location;
        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while fetching the location of the registered devices.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, rs);
        }
    }

    @Override
    public void deleteDeviceLocation(int deviceId, int enrollmentId) throws DeviceDetailsMgtDAOException {

        Connection conn;
        PreparedStatement stmt = null;
        try {
            conn = this.getConnection();
            String query = "DELETE FROM DM_DEVICE_LOCATION WHERE DEVICE_ID = ? AND ENROLMENT_ID = ?";
            stmt = conn.prepareStatement(query);
            stmt.setInt(1, deviceId);
            stmt.setInt(2, enrollmentId);
            stmt.executeUpdate();

        } catch (SQLException e) {
            throw new DeviceDetailsMgtDAOException("Error occurred while deleting the device location from the data base.", e);
        } finally {
            DeviceManagementDAOUtil.cleanupResources(stmt, null);
        }
    }

    private Connection getConnection() throws SQLException {
        return DeviceManagementDAOFactory.getConnection();
    }


//    @Override
//    public void addDeviceApplications(DeviceApplication deviceApplication) throws DeviceDetailsMgtDAOException {
//
//    }
//
//    @Override
//    public DeviceApplication getDeviceApplications(int deviceId) throws DeviceDetailsMgtDAOException {
//        return null;
//    }
//
//    @Override
//    public void deleteDeviceApplications(int deviceId) throws DeviceDetailsMgtDAOException {
//
//    }
}

