package com.algobase.share1.ant;

import java.math.BigDecimal;
import java.util.EnumSet;
import java.util.ArrayList;

import android.content.Context;


import com.dsi.ant.plugins.pluginlib.version.PluginLibVersionInfo;

import com.dsi.ant.plugins.antplus.pcc.*;
import com.dsi.ant.plugins.antplus.pcc.defines.*;

import com.dsi.ant.plugins.antplus.pccbase.*;
import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.*;
import com.dsi.ant.plugins.antplus.pccbase.AsyncScanController.*;
import com.dsi.ant.plugins.antplus.pccbase.AntPlusLegacyCommonPcc.*;
import com.dsi.ant.plugins.antplus.pccbase.AntPlusCommonPcc.*;


import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.*;
import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc.IHeartRateDataReceiver;



public class AntHR 
{
  static int instance_count = 0;

  Context ctxt;
  int instance;

  AntPlusHeartRatePcc hrPcc = null;
  AsyncScanController<AntPlusHeartRatePcc> hrScanController = null;
  ArrayList<AsyncScanResultDeviceInfo> hrDeviceList;

  long  hrate_battery_sec = 0;
  long  battery_interval_sec = 300; // 5 min 

  public void handleDeviceMessage(String dev, int id, String msg, String val) {}
  public void handleHeartRateEvent(int id, long t,int hrate, long beats) {}
  public void handleRRIntervalEvent(int id, long t, float rr ) {}

  public void writeLog(String txt) {}

  public AntHR(Context context) 
  { ctxt = context; 
    instance = ++instance_count;
    writeLog("Ant Constructor: ant = " + instance);
    hrDeviceList = new ArrayList<AsyncScanResultDeviceInfo>();
   }



 public int getNumberOfHeartRateDevices() { return hrDeviceList.size(); }

 public void disconnectHeartRateDevice(String deviceNumber)
 { if (hrPcc != null) 
   { int deviceNum = hrPcc.getAntDeviceNumber();
     hrPcc.releaseAccess();
     hrPcc = null;
     handleDeviceMessage("hrate",deviceNum,"disconnected", "");
    }
 }



 public void updateStatus()
 { 
   if (hrPcc != null)
   {  // connected
      int deviceNum = hrPcc.getAntDeviceNumber();
      DeviceState devState= hrPcc.getCurrentDeviceState();
      handleDeviceMessage("hrate",deviceNum,"connected",devState.toString());
    }
   else
   { if (hrScanController == null) {
       handleDeviceMessage("hrate",0,"stopped","");
     }
     else
      { 
        startHeartRateScanning();
/*
        handleDeviceMessage("hrate",0,"scanning","");
        for (int j = 0 ; j < hrDeviceList.size(); j++) 
        { AsyncScanResultDeviceInfo d = hrDeviceList.get(j);
          int devNum = d.getAntDeviceNumber();
          writeLog("HR Device: id = " + devNum + "  ant = " + instance);
          handleDeviceMessage("hrate",devNum,"device","");
         }
*/
       }
    }
}



// connect to heartrate device

  private void connectToHeartRateDevice(final AsyncScanResultDeviceInfo deviceInfo)
  {
    hrate_battery_sec = 0;

    if (deviceInfo.isAlreadyConnected())  return;

    // access result receiver

    IPluginAccessResultReceiver<AntPlusHeartRatePcc> accessResultReceiver =
         new IPluginAccessResultReceiver<AntPlusHeartRatePcc>() {

      @Override
      public void onResultReceived(AntPlusHeartRatePcc result, 
                                   RequestAccessResult resultCode,
                                   DeviceState initialDeviceState)
      {
        // possible resultCodes
        // SUCCESS
        // CHANNEL_NOT_AVAILABLE
        // ADAPTER_NOT_DETECTED
        // DEPENDENY_NOT_INSTALLED 
        // BAD_PARAMS
        // SEARCH_TIMEOUT
        // OTHER_FAILURE

        // initialDeviceState:
        // CLOSED,DEAD,PROCESSING_REQUEST,SEARCHING,TRACKING,UNRECOGINZED

        writeLog("HR connect: " + resultCode.toString());


        if (resultCode != RequestAccessResult.SUCCESS) 
        { hrPcc = null;
          int devNum = deviceInfo.getAntDeviceNumber();
          handleDeviceMessage("hrate",devNum,"connect_failed", 
                                              resultCode.toString());
          return;
         }


        hrPcc = result;

        final String deviceName = result.getDeviceName();
        final int deviceNum = result.getAntDeviceNumber();

        writeLog("DeviceNumber: " + deviceNum);
        writeLog("DeviceState: " + initialDeviceState);

        handleDeviceMessage("hrate",deviceNum,"connected",
                                               initialDeviceState.toString());


     // subscribe to heart rate events

        hrPcc.subscribeHeartRateDataEvent(new IHeartRateDataReceiver() {
          @Override
          public void onNewHeartRateData(long timeStamp, 
                                         EnumSet<EventFlag> eventFlags,
                                         int heartRate, 
                                         long beatCount,
                                         BigDecimal eventTime,
                                         DataState dataState) {

            //double s = eventTime.doubleValue()/1000;
            //writeLog(String.format("Heartbeat: %.3f",s));
            handleHeartRateEvent(deviceNum,timeStamp,heartRate,beatCount);
           }
        });


        hrPcc.subscribeCalculatedRrIntervalEvent(
           new ICalculatedRrIntervalReceiver() {
           @Override
           public void onNewCalculatedRrInterval(long timeStamp, 
                                             EnumSet<EventFlag> eventFlags, 
                                             BigDecimal rrInterval,
                                             RrFlag rrFlag)
          { float t = rrInterval.floatValue();

            // rrFlag 
            // DATA_SOURCE_AVERAGED, DATA_SOURCE_CACHED, DATA_SOURCE_PAGE4,
            // HEART_RATE_ZERO_DETECTED, UNRECOGNIZED

            if (rrFlag == RrFlag.HEART_RATE_ZERO_DETECTED)
            { writeLog("onNewCalculatedRRInterval:  HEART_RATE_ZERO_DETECTED");
              return;
            }
            
            handleRRIntervalEvent(deviceNum,timeStamp,t);
          }
      });


/*
      hrPcc.subscribePage4AddtDataEvent(new IPage4AddtDataReceiver() {
        @Override
        public void onNewPage4AddtData(long timeStamp, 
                           EnumSet<EventFlag> eventFlags,
                           int manufacturerSpecificByte,
                           BigDecimal timestampOfPreviousToLastHeartBeatEvent)
        {
          double t = timestampOfPreviousToLastHeartBeatEvent.doubleValue();
         }
      });
*/


      hrPcc.subscribeCumulativeOperatingTimeEvent(
        new ICumulativeOperatingTimeReceiver() {
        @Override
        public void onNewCumulativeOperatingTime( long timeStamp,
                                              EnumSet<EventFlag> eventFlags,
                                              long sec)
        { 
          if (sec < hrate_battery_sec + battery_interval_sec) return;
          hrate_battery_sec = sec;

          long h = sec/3600;
          long m = (sec%3600)/60;
          long s = sec%60;
          String msg = String.format("%d:%02d",h,m);
          handleDeviceMessage("hrate",deviceNum,"battery", msg);
         }
      });


      hrPcc.subscribeManufacturerAndSerialEvent(
        new IManufacturerAndSerialReceiver() {
          @Override
          public void onNewManufacturerAndSerial(long timeStamp, 
                                             EnumSet<EventFlag> eventFlags,
                                             int manufacturerID,
                                             int serialNumber)
          { }
      });


      hrPcc.subscribeVersionAndModelEvent(new IVersionAndModelReceiver() {
         @Override
        public void onNewVersionAndModel(long timeStamp, 
                                         EnumSet<EventFlag> eventFlags,
                                         int hardwareVersion,
                                         int softwareVersion, 
                                         int modelNumber)
          { }
      });

     }
   };


    // device state change receiver
    IDeviceStateChangeReceiver deviceStateChangeReceiver = 
        new IDeviceStateChangeReceiver() {                    

          @Override
          public void onDeviceStateChange(DeviceState newState)
          { int devNum = hrPcc.getAntDeviceNumber();
            writeLog("HR (" + devNum + ") state: " + newState);

            //new State: enum 
            // CLOSED,DEAD,PROCESSING_REQUEST,SEARCHING,TRACKING,UNRECOGINZED

            if (newState == DeviceState.DEAD)
            { if (hrPcc != null) hrPcc.releaseAccess();
              hrPcc = null;
             }

            handleDeviceMessage("hrate",devNum,"state",newState.toString());
           }
      };

    // request access to device

    try {
      hrScanController.requestDeviceAccess(deviceInfo, 
                                           accessResultReceiver,
                                           deviceStateChangeReceiver);
     } catch (Exception e) { writeLog(e.toString()); }

/*
    int deviceNum = deviceInfo.getAntDeviceNumber();
    hrPcc.requestAccess(ctxt,deviceNum,0,accessResultReceiver,
                                         deviceStateChangeReceiver);
*/

 }

 public void connectToHeartRateDevice(String deviceString)
 { int devNum = Integer.parseInt(deviceString);
   connectToHeartRateDevice(devNum);
  }

 public void connectToHeartRateDevice(int devNum)
 { AsyncScanResultDeviceInfo dev = null;
   for (int j = 0 ; j < hrDeviceList.size(); j++) 
   { AsyncScanResultDeviceInfo d = hrDeviceList.get(j);
     if (d.getAntDeviceNumber() == devNum) dev = d;
    }
   if (dev != null) connectToHeartRateDevice(dev);
  }


  public void startHeartRateScanning()
  { 
    writeLog("Start HR Scanning: ant = " + instance);

    if (hrScanController != null)
    { // stopHeartRateScanning();
      disconnectHeartRateDevice("");
      hrScanController.closeScanController();
      hrScanController = null;
    }

    hrDeviceList.clear();

    // request for asynchronous scan receiver

    IAsyncScanResultReceiver receiver = 
       new IAsyncScanResultReceiver() {

          @Override
          public void onSearchStopped(RequestAccessResult reason)
          { writeLog("HR Search Stop : " + reason.toString());
            handleDeviceMessage("hrate",0,"stopped", reason.toString());
            hrScanController = null;
           }
          
          @Override
          public void onSearchResult(AsyncScanResultDeviceInfo deviceFound)
          {
            int deviceNum = deviceFound.getAntDeviceNumber();
            String deviceName = deviceFound.getDeviceDisplayName();

            // check for duplicates
            int count = 0;
            for(AsyncScanResultDeviceInfo inf: hrDeviceList) {  
              if (inf.getAntDeviceNumber() == deviceNum) count++;
             }

            if (count > 0) {
              //writeLog(deviceName + "  already scanned.");
            }
            else
            { writeLog("New HR Device: " + deviceNum);
              hrDeviceList.add(deviceFound);
              handleDeviceMessage("hrate",deviceNum,"device","");
             }

          }
      };


    hrScanController = null;
    String err = null;

    try {
      hrScanController = 
         AntPlusHeartRatePcc.requestAsyncScanController(ctxt,0,receiver);
    } catch (Exception e) { err = e.toString(); }

    if (hrScanController == null) 
    { writeLog("hrScanController: " + err);
      handleDeviceMessage("hrate",0,"error",err);
    }
    else
      handleDeviceMessage("hrate",instance, "scanning", "");
  }

  public void stopHeartRateScanning()
  { writeLog("Stop HR Scanning");

    // close possible connection 
    disconnectHeartRateDevice("");

    // stop scanning 
    if (hrScanController != null) hrScanController.closeScanController();
    hrScanController = null;
    hrDeviceList.clear();
  }


  public boolean isHeartRateScanning() { 
    return hrScanController != null; 
  }
}
