package com.algobase.share.bluetooth;

import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

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

import android.util.SparseArray;

import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;

import android.content.Context;
import android.content.Intent;
import android.content.DialogInterface;

import android.view.View;
import android.view.Display;
import android.view.WindowManager;
import android.view.View.OnTouchListener;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Gravity;

import android.app.Activity;

import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.Point;

import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.ProgressBar;
import android.widget.ListView;
import android.widget.Button;
import android.widget.CheckBox;


import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;

import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanSettings;
import android.bluetooth.le.ScanRecord;

import android.bluetooth.le.BluetoothLeScanner;


import com.algobase.share.dialog.*;
import com.algobase.share.system.*;

/*
import androidx.fragment.app.FragmentActivity;
*/


public class BluetoothDialog {

  static String HEART_RATE_SERVICE = AllGattServices.HEART_RATE;
  static String CYCLING_POWER_SERVICE = AllGattServices.CYCLING_POWER;
  static String CYCLING_SPEED_AND_CADENCE = AllGattServices.CYCLING_SPEED_AND_CADENCE;
  static String ENVIRONMENTAL_SENSING = AllGattServices.ENVIRONMENTAL_SENSING;
  static String FITNESS_MACHINE = AllGattServices.FITNESS_MACHINE;


  static int[] TEXTCOLOR_LIGHT = { 0xffbbbbbb, // light grey
                                   0xff666666, // dark grey
                                   0xff008015  // dark green
                                  };

  static int[] TEXTCOLOR_DARK = { 0xff666666,
                                  0xffbbbbbb,
                                  0xff40a060  //  green
                                 };

  static int[] TYPEFACE = { Typeface.NORMAL,
                            Typeface.BOLD,
                            Typeface.BOLD
                          };
                          

  public void  connectHandler(String type,String name,String addr) {}
  public void  disconnectHandler(String type,String addr) {}
  public void  flagHandler(String type, String what, boolean value) {}
  public void  showHandler(String type) {}
  public void  timeoutHandler(String type) {}

  public void  longClickHandler(String type,String name,String addr) {}

  public void  titleClickHandler(String type,String name,String addr) {}

  public void  writeLog(String txt) {}
  public void  showToast(String txt) {}


  boolean dark_mode = false;

  int[] TEXTCOLOR = TEXTCOLOR_LIGHT;

  Activity activity;
  File device_file;
  MyDialog dialog;

  LinearLayout main_layout;
  LinearLayout connect_layout;
  LinearLayout list_layout;

  ScrollView scroll_view;

  ProgressBar progress;
  TextView progress_text;

  TextView tv_msg;

  TextView tv_name;
  TextView tv_addr;
  TextView tv_info;

  CheckBox cb_auto;
  CheckBox cb_filter;

/*
  View title_progress_view;
*/

  Handler handler;
  Runnable countdown_runnable;

  BluetoothManager btManager;
  BluetoothAdapter btAdapter;
  BluetoothLeScanner btLeScanner;

  ScanCallback btScanCallback;

  ArrayList<BluetoothDevice> btDeviceList;

  ArrayList<TextView> btViewList;

  int titleIconRes = 0;
  int titleIconPadding = 0;

  int titleProgressRes = 0;
  int titleButtonRes = 0;

  String type = "all";  // all,hrt,pwr,cad,tmp

  String connect_name = "";
  String connect_addr = "";

  String message_text = "";
  int message_color = Color.BLACK;

  String battery_text = "";
  String value_text = "";

  String title = "";

  boolean auto_connect = false;
  boolean show_auto_connect = false;

  boolean apply_filter = false;
  boolean show_filter = false;

  boolean initial_scan = false;

  boolean disconnect_before_scan = false;

  int scan_timeout = 12;

  int connect_state = 0; // 0: disconnected, 1: connected (no data), 2: data
  

  String string_scanning_for_sensors = "Scanning for sensors  ...";
  String string_no_devices_detected = "No devices detected.";

  String string_no_other_devices_detected= "No other devices detected.";
  String string_search_more_devices = "Deactivate filter to search for more devices.";
  String string_cancel = "Cancel";
  String string_devices = "BT Devices";
  String string_heartrate = "Heartrate";
  String string_power = "Power";
  String string_cadence = "Cadence";
  String string_temperature = "Temperature";
  String string_connect = "Connect";
  String string_disconnect = "Disconnect";
  String string_not_connected = "Not connected.";
  String string_no_data = "Non-active / no data.";
  String string_done = "Done";

  String getDeviceName(BluetoothDevice device) {
     String name = device.getName();
     if (name == null) name = device.getAddress();
     if (name == null) name = "Unknown Device";
     return name;
  }
 

  public BluetoothDialog(Activity act)
  { 
    activity = act;
    type = "all";
    handler = new Handler();
    btDeviceList = new ArrayList<BluetoothDevice>();
    btViewList = new ArrayList<TextView>();
    int s = MyDialog.getStyle();
    if (s == MyDialog.STYLE_HOLO_DARK || s==MyDialog.STYLE_HOLO_DARK){
      dark_mode = true;
      TEXTCOLOR = TEXTCOLOR_DARK;
    }
    else
    { dark_mode = false;
      TEXTCOLOR = TEXTCOLOR_LIGHT;
    }
  }

   public void setLanguage(String lang) { 
     if (lang.equals("Deutsch")) {
       string_scanning_for_sensors = "Suche nach Sensoren  ...";
       string_no_devices_detected = "Keine Sensoren gefunden.";
       string_no_other_devices_detected = "Keine weiteren Sensoren gefunden.";
       string_search_more_devices = "Deaktiviere den Filter, um nach weiteren Devices zu suchen.";
       string_cancel = "Abbrechen";
       string_devices = "BT Sensoren";
       string_heartrate = "Herzfrequenz";
       string_power = "Leistung";
       string_cadence = "Trittfrequenz";
       string_temperature = "Temperatur";
       string_connect = "Verbinden";
       string_disconnect = "Trennen";
       //string_done = "Fertig";
       string_not_connected = "Nicht verbunden.";
       string_no_data = "Inaktiv / keine Daten.";
     }
   }


   public void setType(String t) { type = t; }

   public void setShowFilter(boolean b) { show_filter = b; }
   public void setShowAutoConnect(boolean b) { show_auto_connect = b; }

   public void setDisconnectBeforeScan(boolean b) { disconnect_before_scan = b;}
   public void setInitialScan(boolean b) { initial_scan = b; }
   public void setAutoConnect(boolean b) { auto_connect = b; }
   public void setFilter(boolean b)      { apply_filter = b; }

   public void setDeviceFile(File file)  { device_file = file; }

   public void setMessage(String msg, int clr)
   { message_text = msg; 
     message_color = clr; 
    }


/*
   public void setInfo(String info, int clr)  
   { info_text= info; 
     info_color= clr; 
    }
*/

   public void setConnectName(String name)    { connect_name = name;   }
   public void setConnectAddress(String addr) { connect_addr = addr;   }
   public void setConnectState(int state)     { connect_state = state; }

   public void setBatteryLevel(String level)  { 
     if (level.equals("")) 
       battery_text = "";
     else
       battery_text = "Battery  " + level + " %";
   }

   public void setValue(String value)  { 
     if (value.equals("")) 
       value_text = "";
     else
       value_text = "Value  " + value;
   }

   public void setTitleIcon(int res) { titleIconRes = res; }
   public void setTitleIconPadding(int pad) { titleIconPadding = pad; }

   public void setTitleProgress(int res) { titleProgressRes = res; }

   public void setTitleButton(int res) { titleButtonRes = res; }

   public String  getType() { return type; }
   public boolean getAutoConnect()  { return auto_connect; }
   public int     getConnectState() { return connect_state; }

   public MyDialog getDialog() { return dialog; }



   int DipToPixels(float dp)
   { float dpi = activity.getResources().getDisplayMetrics().densityDpi;
     return (int)(0.5 + dp * (dpi/160f));
    }

   void show_progress(boolean b) {
     if (b)
     { progress.setVisibility(View.VISIBLE);
       progress_text.setVisibility(View.VISIBLE);
       //title_progress_view.setVisibility(View.VISIBLE);
      }
     else
     { progress.setVisibility(View.GONE);
       progress_text.setText("");
       progress_text.setVisibility(View.GONE);
       //title_progress_view.setVisibility(View.GONE);
      }
   }

   public 
   void show_message(String msg, int clr) {
     show_message(msg,clr,"",0x000000,0); 
   }

   void show_message(final String msg1, final int clr1, 
                     final String msg2, final int clr2, int timeout)
   { 
     tv_msg.setText(msg1);
     tv_msg.setTextColor(clr1);

     tv_msg.setVisibility(View.VISIBLE);

     if (timeout > 0)
     { show_progress(true);
       handler.postDelayed(new Runnable() {
          public void run() { 
           tv_msg.setText(msg2);
           tv_msg.setTextColor(clr2);
           show_progress(false);
           timeoutHandler(type);
          }
       }, timeout);
      }
   }


   void update_info()
   { 
/*
     if (connect_state == -1) 
     { tv_info.setVisibility(View.GONE);
       return;
      }
*/
     tv_info.setTextColor(TEXTCOLOR[connect_state]);
     tv_info.setVisibility(View.VISIBLE);

     if (connect_state == 0)
       tv_info.setText(string_not_connected);
     else
     if (connect_state == 1)
     { //tv_info.setTextColor(Color.RED);
       tv_info.setText(string_no_data);
      }
     else
     if (connect_state == 2) 
     { if (battery_text != "")
         setInfoLine(tv_info,battery_text);
       else
         setInfoLine(tv_info,value_text);
      }
  }

  public void connect() {
    String timeout_msg = "Connection Timeout";
/*
    if (auto_connect) {
      timeout_msg += "\n\n";
      timeout_msg += "You might try to deactivate 'Auto Connect'";
    }
*/
    show_message("Connecting  ...", 0xff555555, 
                 timeout_msg, Color.RED, 1000*scan_timeout);

    connectHandler(type,connect_name,connect_addr);
  }

  public void disconnect() {

     disconnectHandler(type,"");

     if (device_file != null) device_file.delete();

     if (connect_state == 0)
     { connect_addr = "";
       connect_name = "";
       update();
     }

   }

   void stop_scan()
   { Button but = dialog.getPositiveButton();
     but.setText("Scan");

     if (dark_mode)
       but.setTextColor(0xffeeeeee);
     else
       but.setTextColor(0xff333333);

     handler.removeCallbacksAndMessages(null);
     btLeScanner.stopScan(btScanCallback); 

     show_progress(false);

     if (btDeviceList.size() == 0) {
       if (connect_state == 2)
         show_message(string_no_other_devices_detected,Color.RED);
       else
       { String msg = string_no_devices_detected;
/*
         if (apply_filter) 
         { msg += "\n\n";
           msg += string_search_more_devices;
         }
*/
         if (dark_mode)
           show_message(msg,0xffffffaa);
         else
           show_message(msg,Color.RED);
        }
     }
   }


   void start_scan()
   {
     if (disconnect_before_scan) disconnect();

     countdown_runnable = new Runnable() { 
         @Override
         public void run() 
         { String txt = progress_text.getText().toString(); 
           int x = 0;
           try { x = Integer.parseInt(txt); } catch (Exception ex) {}
           if (x > 0) {
             progress_text.setText(""+(x-1));
             handler.postDelayed(countdown_runnable,1000);
           }
           else
             progress_text.setText("");
         }
      };

     message_text = "";

     if (dark_mode)
       show_message(string_scanning_for_sensors, 0xffeeeeee);
     else
       show_message(string_scanning_for_sensors, 0xff555555);

     show_progress(true);

     btDeviceList.clear();
     list_layout.removeAllViews();


     final List<ScanFilter> filters =  new ArrayList<ScanFilter>();

     ScanFilter.Builder builder = new ScanFilter.Builder();

     ParcelUuid mask = 
              ParcelUuid.fromString("0000ffff-0000-0000-0000-000000000000");

     if (type.equals("hrt") || type.equals("all"))
     { ParcelUuid uuid = ParcelUuid.fromString(HEART_RATE_SERVICE);
       //builder.setServiceUuid(uuid);
       builder.setServiceUuid(uuid,mask);
       filters.add(builder.build());
      }

     if (type.equals("pwr") || type.equals("all")) 
     { ParcelUuid uuid = ParcelUuid.fromString(CYCLING_POWER_SERVICE);
       //builder.setServiceUuid(uuid);
       builder.setServiceUuid(uuid,mask);
       filters.add(builder.build());
      }

     if (type.equals("cad") || type.equals("all")) 
     { ParcelUuid uuid = ParcelUuid.fromString(CYCLING_SPEED_AND_CADENCE);
       //builder.setServiceUuid(uuid);
       builder.setServiceUuid(uuid,mask);
       filters.add(builder.build());
      }

     if (type.equals("tmp") || type.equals("all")) 
     { ParcelUuid uuid = ParcelUuid.fromString(ENVIRONMENTAL_SENSING);
       builder.setServiceUuid(uuid);
       filters.add(builder.build());
      }

     if (type.equals("fit") || type.equals("all")) 
     { ParcelUuid uuid = ParcelUuid.fromString(FITNESS_MACHINE);
       builder.setServiceUuid(uuid);
       filters.add(builder.build());
      }


     ScanSettings.Builder settings_builder = new ScanSettings.Builder();
     //settings_builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
     //settings_builder.setScanMode(ScanSettings.SCAN_MODE_BALANCED);
     settings_builder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
     final ScanSettings settings = settings_builder.build();

     apply_filter = cb_filter.isChecked();

     progress_text.setText(String.format("%d",scan_timeout));

     show_progress(true);

     Button but = dialog.getPositiveButton();
     but.setText("Stop");

     but.setTextColor(Color.RED);
     //but.setTextColor(0xffaa0000);

     new MyThread() {
       public void run() {
         sleep(1000);
         if (apply_filter)
           btLeScanner.startScan(filters,settings,btScanCallback);
         else
           btLeScanner.startScan(btScanCallback);

         handler.postDelayed(new Runnable() {
            public void run() { stop_scan(); } 
         }, 1000*scan_timeout);
      }
   }.start();


     handler.postDelayed(countdown_runnable,1000);
   }

  void setInfoLine(TextView tv, String txt)
  { if (txt.equals("")) 
    { tv.setVisibility(View.GONE);
      return;
    }
    tv.setVisibility(View.VISIBLE);
    tv.setTextColor(TEXTCOLOR[connect_state]);
    tv.setText(txt);
   }

  public void update()
  { update_layout();
    handler.removeCallbacksAndMessages(null);
    show_progress(false);
  }

  public void update_layout()
  { 
    //list_layout.removeAllViews();

    if (message_text.equals(""))
      tv_msg.setVisibility(View.GONE);
    else
    { tv_msg.setVisibility(View.VISIBLE);
      tv_msg.setText(message_text);
      tv_msg.setTextColor(message_color);
      message_text = "";
    }

    tv_name.setText(connect_name);
    tv_addr.setText(connect_addr);

    update_info();

    Button but_pos = dialog.getPositiveButton();
    Button but_neg = dialog.getNegativeButton();

    if (connect_addr.equals(""))
      connect_layout.setVisibility(View.GONE);
    else
      connect_layout.setVisibility(View.VISIBLE);

    if (connect_state < 1)
      but_neg.setText(string_done);
    else
      but_neg.setText(string_disconnect);

    if (connect_state != -1)
    { tv_name.setTextColor(TEXTCOLOR[connect_state]);
      tv_addr.setTextColor(TEXTCOLOR[connect_state]);
      tv_name.setTypeface(null,TYPEFACE[connect_state]);
     }
 }


  String parseAdvertisingData(byte[] bytes)
  {
    // return 128-bit service uuid 

    String uuid = "0000xxxx-0000-0000-0000-000000000000";

    int pos = 0;

    while (pos < bytes.length && bytes[pos] > 0)
    { int length = bytes[pos++];
      // read next AD Structure starting at pos (lengh bytes)

      //byte type = bytes[pos];
      int type = bytes[pos] & 0xff;

      String bstr = "";
      for(int i=1; i < length; i++) {
        bstr += String.format("%02X ", bytes[pos+i]);
      }

      String what = "unknown";
      if (type == 0x01) what = "Flags";

      if (type == 0x02 || type == 0x03) what = "16-bit Service UUID";
      if (type == 0x04 || type == 0x05) what = "32-bit Service UUID";
      if (type == 0x06 || type == 0x07) what = "128-bit Service UUID";

      if (type == 0x08) what = "Shortened Name";
      if (type == 0x09) what = "Device Name";
      if (type == 0x0a) what = "Tx Power Level";
      if (type == 0x16) what = "Service Data";
      if (type == 0x21) what = "Service Data UUID";
      if (type == 0x19) what = "Apperance Data";
      if (type == 0xff) what = "Manufacturer Data";

      writeLog("");
      writeLog(String.format("type: %02x %s",type,what));

      if (type >= 0x02 && type <= 0x07) {
        // service uuid

        writeLog(String.format("%02x%02x",bytes[pos+length-3],
                                          bytes[pos+length-4]));

        writeLog(String.format("%02x%02x",bytes[pos+length-1],
                                          bytes[pos+length-2]));

        uuid = String.format("0000%02x%02x-0000-0000-0000-000000000000",
                             bytes[pos+length-3], bytes[pos+length-4]);
      }
      else
      if (type == 0x08 || type == 0x09) {
        String name = new String(bytes,pos+1,length-1);
        writeLog(name);
      }
      else
        writeLog(bstr);

      pos += length;
    }

    return uuid;
  }


  void add_device_line(String name, String addr, int rssi)
  { 
    final LinearLayout device_line = new LinearLayout(activity);
    device_line.setOrientation(LinearLayout.VERTICAL);
    device_line.setPadding(20,20,25,20);
    device_line.setClickable(true);

    final TextView tv = dialog.newTextView();
    tv.setSingleLine(true);
    tv.setTextSize(18);
    tv.setText(name);
    if (dark_mode)
      tv.setTextColor(0xffeeeeee);
    else
      tv.setTextColor(0xff000000);
    device_line.addView(tv);
    ((LayoutParams)tv.getLayoutParams()).weight = 1;


    final LinearLayout addr_line = new LinearLayout(activity);
    addr_line.setOrientation(LinearLayout.HORIZONTAL);

    final TextView tv_addr = dialog.newTextView();
    tv_addr.setSingleLine(true);
    tv_addr.setTextSize(15);
    tv_addr.setText(addr);

    if (dark_mode)
      tv_addr.setTextColor(0xffaaaaaa);
    else
      tv_addr.setTextColor(0xff777777);

    addr_line.addView(tv_addr);
    ((LayoutParams)tv_addr.getLayoutParams()).weight = 1;

    final TextView tv_rssi = dialog.newTextView();
    tv_rssi.setSingleLine(true);
    tv_rssi.setTextSize(15);
    tv_rssi.setText("" + rssi + " dBm");

    if (dark_mode)
      tv_rssi.setTextColor(0xff999999);
    else
      tv_rssi.setTextColor(0xff777777);

    addr_line.addView(tv_rssi);
    ((LayoutParams)tv_rssi.getLayoutParams()).width = DipToPixels(80);

    device_line.addView(addr_line);

    list_layout.addView(device_line);

    View line = new View(activity);
    line.setBackgroundColor(0xffc0c0c0);
    list_layout.addView(line);
    line.getLayoutParams().height = 1;

    //device_line.setTag(device);
    device_line.setTag(name + "|" + addr);
    tv_rssi.setTag(name);
    btViewList.add(tv_rssi);


    device_line.setOnTouchListener(new OnTouchListener() 
    {
      @Override
      public boolean onTouch(View v, MotionEvent event)
      { 
        int view_h = scroll_view.getHeight();
        int child_h = scroll_view.getChildAt(0).getHeight();
        if (child_h > view_h) return false;

        if (event.getAction() == MotionEvent.ACTION_DOWN)
        { //tv.setBackgroundColor(0xff000077);
          device_line.setBackgroundColor(0xff000077);
          tv.setTextColor(0xffffffff);
          tv_rssi.setTextColor(0xffffffff);
         }
        else 
        if (event.getAction() == MotionEvent.ACTION_UP) 
        { //tv.setBackgroundColor(0xfffffff);
          device_line.setBackgroundColor(0xfffffff);
          tv.setTextColor(0xff000000);
          tv_rssi.setTextColor(0xff000000);
         }

        return false;
      }
   });

    device_line.setOnClickListener(new View.OnClickListener()
    { public void onClick(View v) { 

         stop_scan();

         // reset connection data
         connect_addr = "";
         connect_name = "";
         connect_state = 0;
         update();


         auto_connect = cb_auto.isChecked(); 
         flagHandler(type,"auto_connect",auto_connect);

         apply_filter = cb_filter.isChecked(); 
         flagHandler(type,"filter",apply_filter);

         list_layout.removeAllViews();
         //battery_level = "";
         battery_text = "";
         value_text = "";

         update_info();

         show_message("Connecting  ...", 0xff555555, 
                      "Connection Timeout", Color.RED, 1000*scan_timeout);

         String tag = (String)v.getTag();
         String[] A = tag.split("\\|");
         connectHandler(type,A[0],A[1]);
      }
    });

    device_line.setOnLongClickListener(new View.OnLongClickListener() { 
       public boolean onLongClick(View v) { 
         String tag = (String)v.getTag();
         String[] A = tag.split("\\|");
         longClickHandler(type,A[0],A[1]);
         return true;
       }
     });

  }

     
      
  public void read_device_file()
  { ArrayList<String> list = new ArrayList<String>();
    stop_scan();

    message_text = "";
    show_message("", 0xff555555);
    btDeviceList.clear();
    list_layout.removeAllViews();

    if (device_file == null) return;

    try {
     BufferedReader reader = new BufferedReader(new FileReader(device_file));
     String line;
     while ((line = reader.readLine()) != null) {
       list.add(line);
     }
    } catch (IOException ex) {}

    for(String line:list) {
      if (!line.startsWith(type)) continue;
      String[] A = line.split("\\|");
      if (A.length < 3) continue;
      String type = A[0];
      String addr = A[1];
      String name = A[2];

      writeLog("line = " + line);
      writeLog("type = " + type);
      writeLog("addr = " + addr);
      writeLog("name = " + name);

      add_device_line(name,addr,0);
    }
  }


  public void show()
  {
    WindowManager wm = 
      (WindowManager)activity.getSystemService(activity.WINDOW_SERVICE);

    Display display = wm.getDefaultDisplay();
    Point pt = new Point();
    display.getSize(pt);
    int screen_width = pt.x;
    int screen_height = pt.y;

    btManager = 
       (BluetoothManager)activity.getSystemService(Context.BLUETOOTH_SERVICE);

    btAdapter = btManager.getAdapter();

    btDeviceList = new ArrayList<BluetoothDevice>();

    if (btAdapter == null) {
      showToast("BT Adapter not inialized.");
      return;
    }

    if (!btAdapter.isEnabled())
    { Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
      activity.startActivity(intent);
      return;
     }


    dialog = new MyDialog(activity);
    dialog.setAutoDismiss(false);

    
/*
    if (titleProgressRes != 0)
    { LayoutInflater inflater = 
      (LayoutInflater)activity.getSystemService(activity.LAYOUT_INFLATER_SERVICE);
      title_progress_view= inflater.inflate(titleProgressRes,null);
      dialog.setTitleViewRight(title_progress_view);
     }
*/

    if (type.equals("all")) title = string_devices;
    if (type.equals("hrt")) title = string_heartrate;
    if (type.equals("pwr")) title = string_power;
    if (type.equals("cad")) title = string_cadence;
    if (type.equals("tmp")) title = string_temperature;
    if (type.equals("fit")) title = "Fitness Machine";

    dialog.setTitle(title);
 
    if (titleIconRes != 0) {
       dialog.setTitleButtonPadding(titleIconPadding);
       dialog.setTitleButtonLeft(titleIconRes,
          new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface d, int which) {
                 titleClickHandler(type,connect_name,connect_addr);
              }
       });
    }

    if (titleButtonRes != 0) {
       dialog.setTitleButton(titleButtonRes,
          new DialogInterface.OnClickListener() {
             public void onClick(DialogInterface d, int which) {

                final String[] dev_name = new String[16];
                final String[] dev_addr = new String[16];

                MyPopupMenu menu = new MyPopupMenu(activity) {
                  @Override
                  public void callMenuAction(View view, int pos,int i) {
                     if (i == -1) return;
                     connect_name = dev_name[i];
                     connect_addr = dev_addr[i];
                     connect();
                  }
                };

                menu.setAnchorView(dialog.getTitleButtonRight());
                menu.setWidth(225);

                menu.add("Connections",0,0xff0000a0,-1);
  
                if (device_file != null)
                { try {
                    BufferedReader reader = 
                             new BufferedReader(new FileReader(device_file));
                    int i = 0;
                    String line;
                    while ((line = reader.readLine()) != null) {
                      String[] A = line.split("\\|");
                      if (!A[0].equals(type)) continue;
                      dev_name[i] = A[2];
                      dev_addr[i] = A[1];
                      menu.add(A[2],0,i);
                      i++;
                    }
                  } catch (IOException ex) {}
                }

                menu.show(-150,-DipToPixels(15));
             }
       });
    }

/*
    dialog.setOnTitleClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
           }
     });
*/


     RelativeLayout view = new RelativeLayout(activity);
     view.setGravity(Gravity.CENTER);

     scroll_view = new ScrollView(activity);

     list_layout = new LinearLayout(activity);
     list_layout.setOrientation(LinearLayout.VERTICAL);

     scroll_view.addView(list_layout);
     view.addView(scroll_view);
     scroll_view.getLayoutParams().width = LayoutParams.MATCH_PARENT;
     scroll_view.getLayoutParams().height = LayoutParams.MATCH_PARENT;

     progress_text = dialog.newTextView();
     progress_text.setGravity(Gravity.CENTER);
     progress_text.setTextSize(20);
     progress_text.setTextColor(0xffaaaaaa);
     progress_text.setText("");
     progress_text.setVisibility(View.GONE);

     view.addView(progress_text);
     ((RelativeLayout.LayoutParams)progress_text.getLayoutParams()).topMargin =
                                                  DipToPixels(25);
     progress_text.getLayoutParams().width = LayoutParams.MATCH_PARENT;
     progress_text.getLayoutParams().height = LayoutParams.MATCH_PARENT;
     progress_text.setVisibility(View.GONE);

     progress = new ProgressBar(activity);
     LinearLayout progress_view = new LinearLayout(activity);
     progress_view.setOrientation(LinearLayout.VERTICAL);
     progress_view.setGravity(Gravity.CENTER);
     progress_view.addView(progress);

     ((LayoutParams)progress.getLayoutParams()).topMargin = DipToPixels(25);
     progress.getLayoutParams().width = DipToPixels(75);
     progress.getLayoutParams().height = DipToPixels(75);
     progress.setVisibility(View.GONE);

     view.addView(progress_view);
     progress_view.getLayoutParams().width = LayoutParams.MATCH_PARENT;
     progress_view.getLayoutParams().height = LayoutParams.MATCH_PARENT;


     tv_msg = dialog.newTextView();
     tv_msg.setPadding(20,25,25,25);
     tv_msg.setTextSize(18);
     tv_msg.setTextColor(Color.RED);
     tv_msg.setVisibility(View.GONE);

     view.addView(tv_msg);

     main_layout = new LinearLayout(activity);
     main_layout.setOrientation(LinearLayout.VERTICAL);
     main_layout.setPadding(5,5,5,10);


     tv_name = dialog.newTextView();
     tv_name.setText(connect_name);
     tv_name.setSingleLine(true);
     //tv_name.setText(connect_name + "\n" + connect_addr);
     tv_name.setTextSize(18);
     tv_name.setPadding(20,10,0,0);
/*
     tv_name.setOnClickListener(new View.OnClickListener() {
         public void onClick(View v) { 
           showToast(connect_name);
         }
     });
*/
     tv_addr = dialog.newTextView();
     tv_addr.setText(connect_addr);
     tv_addr.setTextSize(17);
     tv_addr.setSingleLine(true);
     tv_addr.setPadding(20,0,0,0);

     tv_info = dialog.newTextView();
     tv_info.setTextSize(17);
     tv_info.setSingleLine(true);
     tv_info.setPadding(20,0,0,0);

     update_info();

     if (connect_state != -1)
     { tv_name.setTextColor(TEXTCOLOR[connect_state]);
       tv_addr.setTextColor(TEXTCOLOR[connect_state]);
       tv_name.setTypeface(null,TYPEFACE[connect_state]);
      }

     connect_layout = new LinearLayout(activity);
     connect_layout.setOrientation(LinearLayout.VERTICAL);
     connect_layout.addView(tv_name);
     connect_layout.addView(tv_addr);
     connect_layout.addView(tv_info);
     View sep = new View(activity);
     sep.setBackgroundColor(0xff888888);
     connect_layout.addView(sep);
     sep.getLayoutParams().height = 2;
     ((LayoutParams)sep.getLayoutParams()).topMargin = 20;

     if (connect_addr.equals("")) connect_layout.setVisibility(view.GONE);

     connect_layout.setOnClickListener(new View.OnClickListener() {
         public void onClick(View v) { 
           showToast(connect_name);
         }
     });

     main_layout.addView(connect_layout);

   //cb_auto = dialog.newCheckBox();

     cb_auto = new CheckBox(dialog.getContext());
     cb_auto.setTextSize(17);
     cb_auto.setText("  Auto Connect");
     cb_auto.setChecked(auto_connect);

   //cb_filter = dialog.newCheckBox();

     cb_filter = new CheckBox(dialog.getContext());
     cb_filter.setTextSize(17);
     cb_filter.setText("  Filter  (" + title + ")");
     cb_filter.setChecked(apply_filter);

     main_layout.addView(view);
     ((LayoutParams)view.getLayoutParams()).weight = 1;

     if (show_filter || show_auto_connect)
     { sep = new View(activity);
       sep.setBackgroundColor(0xffc0c0c0);
       main_layout.addView(sep);
       sep.getLayoutParams().height = 2;
       ((LayoutParams)sep.getLayoutParams()).bottomMargin = 7;
      }

     if (show_auto_connect) {
        main_layout.addView(cb_auto);
        ((LayoutParams)cb_auto.getLayoutParams()).topMargin = 5;
     }

     if (show_filter) { 
       main_layout.addView(cb_filter);
       ((LayoutParams)cb_filter.getLayoutParams()).topMargin = 5;
     }

     dialog.setView(main_layout);
     //main_layout.getLayoutParams().height = DipToPixels(350);
     main_layout.getLayoutParams().height = (int)(0.5f*screen_height);

/*
     main_layout.getLayoutParams().height = LayoutParams.MATCH_PARENT;
    ((LayoutParams)main_layout.getLayoutParams()).weight = 1;
*/

     btLeScanner = btAdapter.getBluetoothLeScanner();

     btScanCallback = new ScanCallback() {

      @Override
      public void onBatchScanResults(List<ScanResult> results) {
        writeLog("onBatchScanResults: " + results.size() );
       }

      @Override
      public void onScanResult(int callbackType, final ScanResult result)
      { 
        BluetoothDevice device = result.getDevice(); 

        String addr = device.getAddress();
        String name = device.getName();

        if (name == null) return;
/*
        ParcelUuid[] parcel_uuids = device.getUuids();
        showToast(parcel_uuids[0].toString());
*/
        int rssi = result.getRssi();

        if (btDeviceList.contains(device)) 
        { // device already known: update rssi value 
          for(TextView tv : btViewList) {
            if (((String)tv.getTag()).equals(name))
              tv.setText("" + rssi + " dBm");
          }
          return;
        }

        btDeviceList.add(device);

        tv_msg.setVisibility(View.GONE);

        writeLog("");
        writeLog("Scan Result");

        writeLog(name);
        writeLog(addr);

        ScanRecord record = result.getScanRecord();

      //showToast(record.getDeviceName());

        byte[] bytes = record.getBytes();
        String bstr = "";
        if (bytes != null) {
          for(byte b : bytes) bstr += String.format("%02X ", b);
        }
        writeLog(bstr);

        String uuid = parseAdvertisingData(bytes);

        writeLog("");
        writeLog("UUID = " + uuid);
        writeLog("RSSI = " + rssi + " dBm");

        int txPower = -69;

        if (Build.VERSION.SDK_INT >= 26)
        { txPower = result.getTxPower();
          if (txPower == ScanResult.TX_POWER_NOT_PRESENT)
          { writeLog("TX_POWER NOT PRESENT");
            txPower = -69;
           }
        }
        writeLog("txPower = " + txPower);

        int N = 2;
        double dist = Math.pow(10,(txPower-rssi)/(10.0*N));
        writeLog("DIST = " + dist + " m");


        writeLog("Manufacturer Data");

        SparseArray<byte[]> manuData = record.getManufacturerSpecificData();

        for(int i=0; i<manuData.size(); i++)
        { int manuId = manuData.keyAt(i);
          writeLog("manuId = " + manuId);

          byte[] manu_bytes = record.getManufacturerSpecificData(manuId);

          if (manu_bytes == null) continue;

          bstr = "";
          for(byte b : manu_bytes) bstr += String.format("%02X ", b);
          writeLog(bstr);

          if (name.equals("ThermoBeacon") && manu_bytes.length == 18) 
          { float bat = (255*manu_bytes[9] + manu_bytes[10])/34.0f;
            float tmp = (255*manu_bytes[11] + manu_bytes[12])/16.0f;
            float hum = (255*manu_bytes[13] + manu_bytes[14])/16.0f;
            String txt = 
              String.format("tmp: %.1f  hum: %.1f  bat: %.0f %%",tmp,hum,bat);
            showToast(txt);
            writeLog(txt);
            writeLog("");
          }
        }

        writeLog("");

        add_device_line(name,addr,rssi);
      }

      @Override
      public void onScanFailed(int errorCode) { 
         writeLog("SCAN FAILED: error code = " + errorCode);
         showToast("BT Scan Failed: err = " + errorCode);
      }

   };


    dialog.setPositiveButton("Scan", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface diag, int which) {
              String label = dialog.getPositiveButton().getText().toString();
              if (label.equals("Stop"))
                stop_scan();
              else
                start_scan();
            }
     });


     String label = string_done;

     if (!connect_name.equals("")) {
       if (connect_state == 0)
        label = string_connect;
       else
        label = string_disconnect;
     }
     
     dialog.setNegativeButton(label,new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface diag, int which) {
               handler.removeCallbacksAndMessages(null);
               Button but = dialog.getNegativeButton();
               String label = but.getText().toString();

               auto_connect = cb_auto.isChecked();
               flagHandler(type,"auto_connect",auto_connect);

               apply_filter = cb_filter.isChecked();
               flagHandler(type,"filter",apply_filter);

               if (label.equals(string_done))
               { connectHandler(type,null,null);
                 dialog.dismiss();
                }

               if (label.equals(string_connect)) connect();

               if (label.equals(string_disconnect)) disconnect();

            }
       });


      dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {

               auto_connect = cb_auto.isChecked();
               flagHandler(type,"auto_connect",auto_connect);

               apply_filter = cb_filter.isChecked();
               flagHandler(type,"filter",apply_filter);

               handler.removeCallbacksAndMessages(null);
               dialog.dismiss();
            }
        });


     dialog.setOnShowListener(new DialogInterface.OnShowListener() {
           @Override
           public void onShow(DialogInterface diag) {
             //title_progress_view.setVisibility(View.GONE);
             if (initial_scan && connect_name.equals("")) start_scan();
             showHandler(type);
           }
     });

     dialog.show();
   }

   public void dismiss() {
      dialog.dismiss();
   }

   public boolean isShowing() {
      return dialog.isShowing();
   }

}

