import 'package:dashboard/hal/X9C10X.dart';
import 'package:logging/logging.dart';
import 'package:dashboard/hal/ads1256.dart';
import 'package:dart_periphery/dart_periphery.dart';

class HeartBeatMice {
	Ads1256 adc1 = Ads1256(tag: "adc1", 
		spiBus: 1, 
		spiChip: 0, 
		gpioChip: 1, 
		pinDrdy: 202, 
		pinCS: 13, 
		isCSActiveHigh: false, 
		pinReset: 20);
  Ads1256 adc2 = Ads1256(tag: "adc2", 
		spiBus: 1, 
		spiChip: 0, 
		gpioChip: 1, 
		pinDrdy: 6, 
		pinCS: 21, 
		isCSActiveHigh: false, 
		/*pinReset: 20*/);
  late List<X9c10x> gain;
  late List<X9c10x> offset;
  Logger log = Logger("[HeartBeatMice]");

  HeartBeatMice(){
    var inc =  GPIO(205, GPIOdirection.gpioDirOutHigh, 1);
    var ud = GPIO(204, GPIOdirection.gpioDirOutHigh, 1);
    gain = List<X9c10x>.generate(16, (i){
        return X9c10x(
            id: i,
            ohm: 104000,
            inc:inc,
            ud: ud,
            onSelect: _selectGain,
            onRelease: _releaseGain,
            );
        }, growable: false);
    offset = List<X9c10x>.generate(16, (i){
        return X9c10x(
            id: i,
            ohm: 104000,
            inc:inc,
            ud: ud,
            onSelect: _selectOffset,
            onRelease: _releaseOffset,
            );
        }, growable: false);
  }
  

  void init(){
    adc1.begin(Ads1256.DRATE_500SPS, Ads1256.GAIN_1, true);
    adc1.ioDir = [Ads1256.IO_DIR_OUT, Ads1256.IO_DIR_OUT, Ads1256.IO_DIR_OUT, Ads1256.IO_DIR_OUT];
    adc1.dIO = [false, false, false, false]; //selec A B C NC
    log.info("    adcon   : ${adc1.controlRegisterNCS.toRadixString(16)}");

    adc2.begin(Ads1256.DRATE_500SPS, Ads1256.GAIN_1, true);
    adc2.ioDir = [Ads1256.IO_DIR_OUT, Ads1256.IO_DIR_OUT, Ads1256.IO_DIR_OUT, Ads1256.IO_DIR_OUT];
    adc2.dIO = [false, false, false, false]; //mux 0 1 2 3
    log.info("    adcon   : ${adc2.controlRegisterNCS.toRadixString(16)}");
  }



  Future<double> readAnalog(int no) async {
    //TODO: check kebenaran
    var adc = switch (no) {
      1 || 2 || 3 || 4 || 9 || 10 || 11 || 12 =>adc2,
      5 || 6 || 7 || 8 || 13 || 14 || 15 || 16 || _ =>adc1, };
    //TODO: check kebenaran
    var ain = switch (no) {
      1 || 13=>Ads1256.MUXP_AIN0,
      2 || 14=>Ads1256.MUXP_AIN1,
      3 || 15=>Ads1256.MUXP_AIN2,
      4 || 8=>Ads1256.MUXP_AIN3,
      7 || 12=>Ads1256.MUXP_AIN4,
      6 || 11=>Ads1256.MUXP_AIN5,
      5 || 10=>Ads1256.MUXP_AIN6,
      9 || 16=>Ads1256.MUXP_AIN7,
      _=>Ads1256.MUXP_AINCOM
    };
    log.info('readAnalog() <$no>');
    log.info('> adc : <${adc.log.name}> | ain : <${ain.toRadixString(16)}> ');
    var r = adc.analogRead(ain, Ads1256.MUXN_AINCOM).toVolt();
    log.info('> $r');
    return r;
  }

  void _signalMux(int n, bool val) {
    log.info('_signalMux() <$n> <$val>');
    int i = switch (n) {
          1 => 0,
          2 => 1,
          3 => 2,
          4 => 3,
          _ => -1
        };
    log.info('  >mux : $i');
    if (i == -1){
        return;
      }
    var adc = adc2;
    List<bool> io = adc.dIO;
    log.info('  >current <$io>');
    //io[i] = val; //SIG
    io[i] = val; //EN
    adc.dIO = io;
    log.info('  >confirm <${adc.dIO}>');
  }
  void _channelMux(int v) {
    int val = (v%8 == 0 ? 8: v%8).toUnsigned(8)-1;
    var adc = adc1;
    log.info('_channelMux() <$v>');
    log.info('  >val : <$val>');
    log.info('  >current <${adc.dIO}>');
    adc.dIO = val.toListBoolbit();
    log.info('  >confirm <${adc.dIO}>');

  }
  int _routeMux(an) => switch(an){
    1 ||2 ||3 ||4 => 3, 
    9 ||10 ||11 ||12 => 2,
    5 ||6 ||7 ||8 => 4, 
    13 ||14 ||15 => 1, 
    _=>1 
  };
  void _selectOffset(int an){
    log.info('_selectOffset() <${an.toRadixString(16)}>');
    int n = _routeMux(an+1);
    if (n == -1){
        return;
      }
    _channelMux(((an+1)*2-1)); // - 1 for offset
    _signalMux(n, true);
  }
  void _releaseOffset(int an){
    log.info('_releaseGain() <${an.toRadixString(16)}>');
    int n = _routeMux(an+1);
    _signalMux(n, false);
  }
  void _selectGain(int an){
    log.info('_selectGain() <${an.toRadixString(16)}>');
    int n = _routeMux(an+1);
    if (n == -1){
        return;
      }
    _channelMux(((an+1)*2)); // - 0 for gain
    _signalMux(n, true);
  }
  void _releaseGain(int an){
    log.info('_releaseGain() <${an.toRadixString(16)}>');
    int n = _routeMux(an+1);
    _signalMux(n, false);
  }

  void dispose(){ 
      adc1.dispose();
      adc2.dispose();
      for (var g in gain) {
        g.dispose();
      }
      for (var a in offset) {
        a.dispose();
      }
  }
}
