package com.eveningoutpost.dexdrip;

import android.annotation.TargetApi;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.InputFilter;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.activeandroid.query.Select;
import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice;
import com.eveningoutpost.dexdrip.Models.UserError.Log;
import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter;
import com.eveningoutpost.dexdrip.utils.AndroidBarcode;
import com.eveningoutpost.dexdrip.utils.ListActivityWithMenu;
import com.eveningoutpost.dexdrip.utils.LocationHelper;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;

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

import lecho.lib.hellocharts.util.ChartUtils;

@TargetApi(android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)
public class BluetoothScan extends ListActivityWithMenu {
    public static String menu_name = "Scan for BT";

    private final static String TAG = BluetoothScan.class.getSimpleName();
    private static final long SCAN_PERIOD = 30000;
    private boolean is_scanning;

    private Handler mHandler;
    private LeDeviceListAdapter mLeDeviceListAdapter;

    private ArrayList<BluetoothDevice> found_devices;
    private BluetoothAdapter bluetooth_adapter;
    private BluetoothLeScanner lollipopScanner;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bluetooth_scan);
        final BluetoothManager bluetooth_manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

        bluetooth_adapter = bluetooth_manager.getAdapter();
        mHandler = new Handler();

        if (bluetooth_adapter == null) {
            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        if (!bluetooth_manager.getAdapter().isEnabled()) {
            Toast.makeText(this, "Bluetooth is turned off on this device currently", Toast.LENGTH_LONG).show();
        } else {
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
                Toast.makeText(this, "The android version of this device is not compatible with Bluetooth Low Energy", Toast.LENGTH_LONG).show();
            }
        }
        // Will request that GPS be enabled for devices running Marshmallow or newer.
        LocationHelper.requestLocationForBluetooth(this);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            initializeScannerCallback();

        mLeDeviceListAdapter = new LeDeviceListAdapter();
        setListAdapter(mLeDeviceListAdapter);
    }

    @Override
    protected void onPause() {
        super.onPause();
        scanLeDevice(false);
        mLeDeviceListAdapter.clear();
        mLeDeviceListAdapter.notifyDataSetChanged();
    }

    @Override
    public String getMenuName() {
        return menu_name;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_bluetooth_scan, menu);
        if (!is_scanning) {
            menu.findItem(R.id.menu_stop).setVisible(false);
            menu.findItem(R.id.menu_scan).setVisible(true);
        } else {
            menu.findItem(R.id.menu_stop).setVisible(true);
            menu.findItem(R.id.menu_scan).setVisible(false);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_scan:
                BluetoothManager bluetooth_manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
                Toast.makeText(this, "Scanning", Toast.LENGTH_LONG).show();
                if(bluetooth_manager == null) {
                    Toast.makeText(this, "This device does not seem to support bluetooth", Toast.LENGTH_LONG).show();
                    return true;
                } else {
                    if(!bluetooth_manager.getAdapter().isEnabled()) {
                        Toast.makeText(this, "Bluetooth is turned off on this device currently", Toast.LENGTH_LONG).show();
                        return true;
                    } else {
                        if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2){
                            Toast.makeText(this, "The android version of this device is not compatible with Bluetooth Low Energy", Toast.LENGTH_LONG).show();
                            return true;
                        }
                    }
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    scanLeDeviceLollipop(true);
                } else {
                    scanLeDevice(true);
                }
                return true;
//            case R.id.menu_stop:
//                Intent tableIntent = new Intent(this, RawDataTable.class);
//                startActivity(tableIntent);
//                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @TargetApi(19)
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            Log.d(TAG,"Start scan 19");
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    is_scanning = false;
                    bluetooth_adapter.stopLeScan(mLeScanCallback);
                    invalidateOptionsMenu();
                }
            }, SCAN_PERIOD);

            is_scanning = true;
            bluetooth_adapter.startLeScan(mLeScanCallback);
        } else {
            is_scanning = false;
            if(bluetooth_adapter != null && bluetooth_adapter.isEnabled()) {
                bluetooth_adapter.stopLeScan(mLeScanCallback);
            }
        }
        invalidateOptionsMenu();
    }

    @TargetApi(21)
    private void initializeScannerCallback() {
        Log.d(TAG, "initializeScannerCallback");
        mScanCallback = new ScanCallback() {
            @Override
            public void onBatchScanResults(final List<ScanResult> results) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        for (ScanResult result: results) {
                            BluetoothDevice device = result.getDevice();
                            if (device.getName() != null && device.getName().length() > 0) {
                                mLeDeviceListAdapter.addDevice(device);
                            }
                        }
                        mLeDeviceListAdapter.notifyDataSetChanged();
                    }
                });
            }

            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                final BluetoothDevice device = result.getDevice();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (device.getName() != null && device.getName().length() > 0) {
                            mLeDeviceListAdapter.addDevice(device);
                            mLeDeviceListAdapter.notifyDataSetChanged();
                        }
                    }
                });
            }
        };
    }

    @TargetApi(21)
    private void scanLeDeviceLollipop(final boolean enable) {
        if (enable) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                lollipopScanner = bluetooth_adapter.getBluetoothLeScanner();
            }
            if(lollipopScanner != null) {
                Log.d(TAG, "Starting scanner 21");
                // Stops scanning after a pre-defined scan period.
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        is_scanning = false;
                        if(bluetooth_adapter != null && bluetooth_adapter.isEnabled()) {
                            lollipopScanner.stopScan(mScanCallback);
                        }
                        invalidateOptionsMenu();
                    }
                }, SCAN_PERIOD);
                ScanSettings settings = new ScanSettings.Builder()
                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                        .build();
                is_scanning = true;
                lollipopScanner.startScan(null, settings, mScanCallback);
            } else {
                try {
                    scanLeDevice(true);
                } catch (Exception e) {
                    Log.e(TAG, "Failed to scan for ble device", e);
                }
            }
        } else {
            is_scanning = false;
            if(bluetooth_adapter != null && bluetooth_adapter.isEnabled()) {
                lollipopScanner.stopScan(mScanCallback);
            }
        }
        invalidateOptionsMenu();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        if (scanResult == null || scanResult.getContents() == null) {
            return;
        }
        if (scanResult.getFormatName().equals("CODE_128")) {
            Log.d(TAG, "Setting serial number to: " + scanResult.getContents());
            prefs.edit().putString("share_key", scanResult.getContents()).apply();
            returnToHome();
        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Log.d(TAG, "Item Clicked");
        final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
        if (device == null || device.getName() == null) return;
        Toast.makeText(this, R.string.connecting_to_device, Toast.LENGTH_LONG).show();

        ActiveBluetoothDevice btDevice = new Select().from(ActiveBluetoothDevice.class)
                .orderBy("_ID desc")
                .executeSingle();

        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        prefs.edit().putString("last_connected_device_address", device.getAddress()).apply();
        if (btDevice == null) {
            ActiveBluetoothDevice newBtDevice = new ActiveBluetoothDevice();
            newBtDevice.name = device.getName();
            newBtDevice.address = device.getAddress();
            newBtDevice.save();
        } else {
            btDevice.name = device.getName();
            btDevice.address = device.getAddress();
            btDevice.save();
        }
        if(device.getName().toLowerCase().contains("dexcom")) {
            if(!CollectionServiceStarter.isBTShare(getApplicationContext())) {
                prefs.edit().putString("dex_collection_method", "DexcomShare").apply();
                prefs.edit().putBoolean("calibration_notifications", false).apply();
            }
            if(prefs.getString("share_key", "SM00000000").compareTo("SM00000000") == 0 || prefs.getString("share_key", "SM00000000").length() < 10) {
                requestSerialNumber(prefs);
            } else returnToHome();

        } else if(device.getName().toLowerCase().contains("bridge")) {
            if(!CollectionServiceStarter.isDexbridgeWixelorWifiandDexbridgeWixel(getApplicationContext()))
                prefs.edit().putString("dex_collection_method", "DexbridgeWixel").apply();
            if(prefs.getString("dex_txid", "00000").compareTo("00000") == 0 || prefs.getString("dex_txid", "00000").length() < 5) {
                requestTransmitterId(prefs);
            } else returnToHome();

        } else if(device.getName().toLowerCase().contains("drip")) {
            if (!
                    (CollectionServiceStarter.isBteWixelorWifiandBtWixel(getApplicationContext())
                    ) || CollectionServiceStarter.isLimitter(getApplicationContext())) {
                prefs.edit().putString("dex_collection_method", "BluetoothWixel").apply();
            }
            returnToHome();
        } else if(device.getName().toLowerCase().contains("limitter")) {
            if (!CollectionServiceStarter.isLimitter(getApplicationContext())) {
                prefs.edit().putString("dex_collection_method", "LimiTTer").apply();
            }
            returnToHome();
        } else {
            returnToHome();
        }
    }

    public void returnToHome() {
        if (is_scanning) {
            bluetooth_adapter.stopLeScan(mLeScanCallback);
            is_scanning = false;
        }
        Intent intent = new Intent(this, Home.class);
        CollectionServiceStarter.restartCollectionService(getApplicationContext());
        startActivity(intent);
        finish();
    }

    private class LeDeviceListAdapter extends BaseAdapter {
        private ArrayList<BluetoothDevice> mLeDevices;
        private LayoutInflater mInflator;

        public LeDeviceListAdapter() {
            super();
            mLeDevices = new ArrayList<>();
            mInflator = BluetoothScan.this.getLayoutInflater();
        }

        public void addDevice(BluetoothDevice device) {
            if(!mLeDevices.contains(device)) {
                mLeDevices.add(device);
                notifyDataSetChanged();
            }
        }

        public BluetoothDevice getDevice(int position) {
            return mLeDevices.get(position);
        }

        public void clear() {
            mLeDevices.clear();
            notifyDataSetChanged();
        }

        @Override
        public int getCount() {
            return mLeDevices.size();
        }

        @Override
        public Object getItem(int i) {
            return mLeDevices.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            ViewHolder viewHolder;
            // General ListView optimization code.
            if (view == null) {
                view = mInflator.inflate(R.layout.listitem_device, null);
                viewHolder = new ViewHolder();
                viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
                viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }

            BluetoothDevice device = mLeDevices.get(i);
            final String deviceName = device.getName();
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
            if(prefs.getString("last_connected_device_address", "").compareTo(device.getAddress()) == 0) {
                viewHolder.deviceName.setTextColor(ChartUtils.COLOR_BLUE);
                viewHolder.deviceAddress.setTextColor(ChartUtils.COLOR_BLUE);
            }
            viewHolder.deviceName.setText(deviceName);
            viewHolder.deviceAddress.setText(device.getAddress());
            return view;
        }
    }

    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (device.getName() != null && device.getName().length() > 0) {
                                mLeDeviceListAdapter.addDevice(device);
                                mLeDeviceListAdapter.notifyDataSetChanged();
                            }
                        }
                    });
                }
            };

    private ScanCallback mScanCallback;

    static class ViewHolder {
        TextView deviceName;
        TextView deviceAddress;
    }

    private void requestSerialNumber(final SharedPreferences prefs) {
        final Dialog dialog = new Dialog(BluetoothScan.this);
        dialog.setContentView(R.layout.dialog_single_text_field);
        Button saveButton = (Button) dialog.findViewById(R.id.saveButton);
        Button cancelButton = (Button) dialog.findViewById(R.id.cancelButton);
        final EditText serialNumberView = (EditText) dialog.findViewById(R.id.editTextField);
        serialNumberView.setHint("SM00000000");
        ((TextView) dialog.findViewById(R.id.instructionsTextField)).setText("Enter Your Dexcom Receiver Serial Number");

        dialog.findViewById(R.id.scannerButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AndroidBarcode(BluetoothScan.this).scan();
                dialog.dismiss();
            }
        });

        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!TextUtils.isEmpty(serialNumberView.getText()))
                    prefs.edit().putString("share_key", serialNumberView.getText().toString()).apply();
                dialog.dismiss();
                returnToHome();
            }
        });
        cancelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
        dialog.show();
    }

    private void requestTransmitterId(final SharedPreferences prefs) {
        final Dialog dialog = new Dialog(BluetoothScan.this);
        dialog.setContentView(R.layout.dialog_single_text_field);
        Button saveButton = (Button) dialog.findViewById(R.id.saveButton);
        Button cancelButton = (Button) dialog.findViewById(R.id.cancelButton);
        dialog.findViewById(R.id.scannerButton).setVisibility(View.GONE);
        final EditText serialNumberView = (EditText) dialog.findViewById(R.id.editTextField);
        serialNumberView.setHint("00000");
        serialNumberView.setFilters(new InputFilter[]{new InputFilter.AllCaps()});
        ((TextView) dialog.findViewById(R.id.instructionsTextField)).setText("Enter Your Dexcom Transmitter ID");

        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!TextUtils.isEmpty(serialNumberView.getText())) {
                    prefs.edit().putString("dex_txid", serialNumberView.getText().toString()).apply();
                }
                dialog.dismiss();
                returnToHome();
            }
        });
        cancelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
        dialog.show();
    }
}