#include <Wire.h>
#include <PCF8574.h>

// Global values
const float STEP = 0.001;
volatile bool StateA, StateB; // initialized at startup by reading the legs.
volatile float DISTANCE = 0; // at startup 0

volatile byte newState, oldState; // initialized at startup by reading the legs.
volatile char ABprev;
const float increment[16] = {0,-STEP,STEP,0, STEP,0,0,-STEP, -STEP,0,0,STEP, 0,STEP,-STEP,0};
volatile int incStreak = 0; // used for step increasing during several consecutive scrolls of the second encoder
volatile float incStep; // save previous step to detect when increase the streak

// Expanders: PCF8574AP(https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf)
// Default pins A4-SDA, A5-SCL
// Correct adresses are 0x38 - 0x3F
PCF8574 CURR_PCF;
// // --- Lamps 1, 2 ---
// // PCF8574 PCF0(0b0100011);
// PCF8574 PCF0(0x39); // bin: 0b00111001
// // PCF8574 PCF0(0xB9);
// // --- Lamps 3, 4 ---
// // PCF8574 PCF1(0b0100101);
// PCF8574 PCF1(0x3D); // bin: 0b00111101
// // PCF8574 PCF1(0xBD);
// // --- Lamps 5, 6 ---
// // PCF8574 PCF2(0b0100110);
// PCF8574 PCF2(0x3F); // bin: 0b00111111
// // PCF8574 PCF2(0xBF);
// --- Lamps 7, 8 ---
// PCF8574 PCF3(0b0100111);
PCF8574 PCF3(0x3B); // bin: 0b00111011
// PCF8574 PCF3(0xBB);


void safeDistanceIncrease(float increaseStep){
  // Do not increase or decrease DISTANCE above or below 9999.999
  // because of 8 lamp limit
  if (abs(DISTANCE + increaseStep) > 9999.999){
    DISTANCE = 9999.999 * (pow(-1, (DISTANCE < 0)));
  } else {
    DISTANCE += increaseStep;
  }
}


// Interrupt on channel A of the rotary encoder
void interruptChannelA() {
  // change the channel state (to avoid digitalRead())
  StateA = !StateA;
  // adjust the coordinate
  if (StateA != StateB){
    safeDistanceIncrease(STEP);
  } else {
    safeDistanceIncrease(-STEP);
  }
}


// Interrupt on channel B of the rotary encoder
void interruptChannelB() {
  // change the channel state (to avoid digitalRead())
  StateB = !StateB;
  // adjust the coordinate
  if (StateA == StateB){
    safeDistanceIncrease(STEP);
  } else {
    safeDistanceIncrease(-STEP);
  }
}


ISR (PCINT0_vect) { // D8 or D9 or D10 have changed
  // Detect changes on lowest 3 bits of PINB. 7 is 0b111
  newState = PINB & 7;
  // if pin D10 has changed, state 100
  if ((newState ^ oldState) & 4) {
    DISTANCE = 0;
  // if pin D8 or D9 has changed
  } else {
    // Detect changes on lowest 2 bits of PINB. 3 is 0b11
    char AB = PINB & 3;
    // if inc direction do not changes, increase streak by 1, else reset
    if (incStep == increment[AB+ABprev*4]){
      incStreak++;
    } else {
      incStreak = 0;
    }
    incStep = increment[AB+ABprev*4];
    // Changing DISTANCE with new increase step values. 
    // All limits in states and multiplying of step are random numbers and can be adjusted
    if (10 > incStreak){
      safeDistanceIncrease(incStep);
    } else if (20 > incStreak){ // 10, 20, 30 are random nums
      safeDistanceIncrease(incStep * 10);
    } else if (30 > incStreak){
      safeDistanceIncrease(incStep * 100);
    } else if (40 > incStreak){
      safeDistanceIncrease(incStep * 1000);
    } else if (50 > incStreak){
      safeDistanceIncrease(incStep * 10000);
    } else if (60 > incStreak){
      safeDistanceIncrease(incStep * 100000);
    } else {
      safeDistanceIncrease(incStep * 1000000);
    }
    ABprev = AB;
  }
  oldState = newState;

}


void setupPcfs() {
  Serial.println("PCFs setupes started");

  // // Check for correct address of connected PCF
  // if (!PCF0.begin()) {
  //   Serial.println("PCF0 Chip not responding.");
  // }
  // if (!PCF0.isConnected()) {
  //   Serial.println("PCF0 could not initialize... => not connected");
  //   while (1);
  // }
  // // Write 0 to all 8 pins for correct work
  // PCF0.write8(0);

  // if (!PCF1.begin()) {
  //   Serial.println("PCF1 Chip not responding.");
  // }
  // if (!PCF1.isConnected()) {
  //   Serial.println("PCF1 could not initialize... => not connected");
  //   while (1);
  // }
  // PCF1.write8(0);

  // if (!PCF2.begin()) {
  //   Serial.println("PCF2 Chip not responding.");
  // }
  // if (!PCF2.isConnected()) {
  //   Serial.println("PCF2 could not initialize... => not connected");
  //   while (1);
  // }
  // PCF2.write8(0);

  if (!PCF3.begin()) {
    Serial.println("PCF3 Chip not responding.");
  }
  if (!PCF3.isConnected()) {
    Serial.println("PCF3 could not initialize... => not connected");
    while (1);
  }
  for (uint8_t i = 0; i < 3; i++) {
    PCF3.write8(170);
    delay(200);
    PCF3.write8(85);
    delay(200);
  }

  PCF3.write(0, 1);
  delay(125);

  for (int i = 0; i < 7; i++) {
    PCF3.shiftLeft();
    delay(125);
  }

  for (int i = 0; i < 7; i++) {
    PCF3.shiftRight();
    delay(125);
  }

  for (int i = 1; i < 7; i++) {
    PCF3.write(i, 1);
    delay(125);
  }
  // PCF3.write8(0);

  Serial.println("PCFs setupes were finished.");
}


void setup() {
  Serial.begin(115200);

  Serial.println("Setup started");

  setupPcfs();

  // Serial.begin(9600);
  pinMode(3, INPUT); // A
  pinMode(2, INPUT); // B

  // Got from here -- https://habr.com/ru/articles/340448/
  PCICR |= (1 << PCIE0);  // interrupt will be fired on any change on pins d8, d9 or d10
  PCMSK0 |= 7;
  oldState = PINB & 7;
  ABprev = PINB & 3; // get first state of d8 and d9

  // Enable interrupt A
  attachInterrupt(digitalPinToInterrupt(3), interruptChannelA, CHANGE);
  // Enable interrupt B
  attachInterrupt(digitalPinToInterrupt(2), interruptChannelB, CHANGE);
  // Initialization of global variables
  StateA = digitalRead(3);
  StateB = digitalRead(2);

  Serial.println("Setup finished");
}


void writeDecoder(int decoder_i, int p1, int p2, int p4, int p8){
  switch (decoder_i){
    // case 0:
    // case 1:
    //   PCF0.write(0 + 4 * (decoder_i % 2), p1); // 1
    //   PCF0.write(1 + 4 * (decoder_i % 2), p2); // 2
    //   PCF0.write(2 + 4 * (decoder_i % 2), p4); // 4
    //   PCF0.write(3 + 4 * (decoder_i % 2), p8); // 8
    //   break;
    // case 2:
    // case 3:
    //   PCF1.write(0 + 4 * (decoder_i % 2), p1); // 1
    //   PCF1.write(1 + 4 * (decoder_i % 2), p2); // 2
    //   PCF1.write(2 + 4 * (decoder_i % 2), p4); // 4
    //   PCF1.write(3 + 4 * (decoder_i % 2), p8); // 8
    //   break;
    // case 4:
    // case 5:
    //   PCF2.write(0 + 4 * (decoder_i % 2), p1); // 1
    //   PCF2.write(1 + 4 * (decoder_i % 2), p2); // 2
    //   PCF2.write(2 + 4 * (decoder_i % 2), p4); // 4
    //   PCF2.write(3 + 4 * (decoder_i % 2), p8); // 8
    //   break;
    case 6:
    case 7:
      PCF3.write(0 + 4 * (decoder_i % 2), p1); // 1
      PCF3.write(1 + 4 * (decoder_i % 2), p2); // 2
      PCF3.write(2 + 4 * (decoder_i % 2), p4); // 4
      PCF3.write(3 + 4 * (decoder_i % 2), p8); // 8
      break;
  }
  // if ((decoder_i == 6) || (decoder_i == 7)){
  //   PCF3.write(0 + 4 * (decoder_i % 2), p1); // 1
  //   PCF3.write(1 + 4 * (decoder_i % 2), p2); // 2
  //   PCF3.write(2 + 4 * (decoder_i % 2), p4); // 4
  //   PCF3.write(3 + 4 * (decoder_i % 2), p8); // 8
  // }
  // CURR_PCF.write(0 + 4 * (decoder_i % 2), p1); // 1
  // CURR_PCF.write(1 + 4 * (decoder_i % 2), p2); // 2
  // CURR_PCF.write(2 + 4 * (decoder_i % 2), p4); // 4
  // CURR_PCF.write(3 + 4 * (decoder_i % 2), p8); // 8
}


void loop() {
  float tempDistance = DISTANCE;

  char tempString[9];

  dtostrf(tempDistance, 9, 3, tempString);

  Serial.print("number:");
  Serial.println(tempString);

  int pcf_i = 0;
  int decoder_i = 1;
  bool isMinus = false;

  // PCF8574 CURR_PCF;

  // iterate through all chars of distanse
  // Start with first character to omit sign of number and show it last
  // Last character of char tempString[9]; dtostrf(distance, 9, 3, tempString); is '&'. I do not why
  for (int char_i = 1; char_i < strlen(tempString); char_i++) {

    if (pcf_i < 3){
      if (tempString[char_i] == '.'){
        decoder_i--;
      }

      decoder_i++;

      if (decoder_i % 2 == 0){
        pcf_i++;
      }
      // Serial.println("Its work");
      continue;
    }
    
    // switch (pcf_i){
    //   // case 0:
    //   //   CURR_PCF = PCF0;
    //   //   break;
    //   // case 1:
    //   //   CURR_PCF = PCF1;
    //   //   break;
    //   // case 2:
    //   //   CURR_PCF = PCF2;
    //   //   break;
    //   case 3:
    //     CURR_PCF = PCF3;
    //     break;
    // }

    // '.' case is skipped because dot showed always on indecators
    // 1, 2, 4, 8
    switch (tempString[char_i]){
      case ' ':
      case '0':
        // writeDecoder(decoder_i, 0, 1, 0, 1);
        writeDecoder(decoder_i, 0, 1, 0, 1);
        break;
      case '-':
        isMinus = true;
        writeDecoder(decoder_i, 0, 1, 0, 1);
        break;
      case '1':
        writeDecoder(decoder_i, 1, 0, 0, 1);
        break;
      case '2':
        writeDecoder(decoder_i, 0, 1, 0, 0);
        break;
      case '3':
        writeDecoder(decoder_i, 1, 1, 0, 0);
        break;
      case '4':
        writeDecoder(decoder_i, 0, 0, 1, 0);
        break;
      case '5':
        writeDecoder(decoder_i, 1, 0, 1, 0);
        break;
      case '6':
        writeDecoder(decoder_i, 0, 1, 1, 0);
        break;
      case '7':
        writeDecoder(decoder_i, 1, 1, 1, 0);
        break;
      case '8':
        writeDecoder(decoder_i, 0, 0, 0, 1);
        break;
      case '9':
        writeDecoder(decoder_i, 1, 0, 0, 1);
        break;
      case '.':
        // Do not increase decoder index, because dot is skipped and always showed
        decoder_i--;
        // break;
    }

    decoder_i++;

    if (decoder_i % 2 == 0){
      pcf_i++;
    }
  }
  // if (isMinus || tempString[0] == '-'){
  //   PCF0.write(0, LOW); // 1
  //   PCF0.write(1, HIGH); // 2
  // } else {
  //   PCF0.write(0, HIGH); // 1
  //   PCF0.write(1, LOW); // 2
  // }

  delay(60);
}
PCF8574ABreakout
PCF8574ABreakout
PCF8574ABreakout
PCF8574ABreakout