#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Arduino.h>  
#include <avr/wdt.h>




// LCD Settings (I2C address 0x27, size 16x2)
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Keypad Settings
const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {',', '0', '#', 'D'}
};

byte rowPins[ROWS] = {2, 3, 4, 5};
byte colPins[COLS] = {6, 7, 8, 9};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

struct FractionConversion {
  const char* fraction;
  double inches;
  double mm;
};
//convertion
FractionConversion fraction_conversion[] = {
  {"1/64", 0.015625, 0.3969}, {"1/32", 0.03125, 0.7938}, {"3/64", 0.046875, 1.1906}, {"1/16", 0.0625, 1.5875}, {"5/64", 0.078125, 1.9844}, {"3/32", 0.09375, 2.3813}, 
{"7/64", 0.109375, 2.7781}, {"1/8", 0.125, 3.175}, {"9/64", 0.140625, 3.5719}, {"5/32", 0.15625, 3.9688}, {"11/64", 0.171875, 4.3655}, {"3/16", 0.1875, 4.7625}, {"13/64", 0.203125, 5.1594}, 
{"7/32", 0.21875, 5.5563}, {"15/64", 0.234375, 5.9531}, {"1/4", 0.25, 6.35}, {"17/64", 0.265625, 6.7469}, {"9/32", 0.28125, 7.1438}, {"19/64", 0.296875, 7.54}, {"5/16", 0.3125, 7.9375}, 
{"21/64", 0.328125, 8.3344}, {"11/32", 0.34375, 8.7313}, {"23/64", 0.359375, 9.1282}, {"3/8", 0.375, 9.525}, {"25/64", 0.390625, 9.9219}, {"13/32", 0.40625, 10.3188}, {"27/64", 0.421875, 10.7157}, 
{"7/16", 0.4375, 11.1125}, {"29/64", 0.453125, 11.5094}, {"15/32", 0.46875, 11.9063}, {"31/64", 0.484375, 12.3032}, {"1/2", 0.5, 12.7}, {"33/64", 0.515625, 13.096}, {"17/32", 0.53125, 13.493}, 
{"35/64", 0.546875, 13.89}, {"9/16", 0.5625, 14.287}, {"37/64", 0.578125, 14.684}, {"19/32", 0.59375, 15.081}, {"39/64", 0.609375, 15.478}, {"5/8", 0.625, 15.875}, 
{"41/64", 0.640625, 16.271}, {"21/32", 0.65625, 16.668}, {"43/64", 0.671875, 17.065}, {"11/16", 0.6875, 17.462}, {"45/64", 0.703125, 17.859}, {"23/32", 0.71875, 18.256}, {"47/64", 0.734375, 18.653}, 
{"3/4", 0.75, 19.05}, {"49/64", 0.765625, 19.447}, {"25/32", 0.78125, 19.843}, 
{"51/64", 0.796875, 20.24}, {"13/16", 0.8125, 20.6375}, {"53/64", 0.828125, 21.0345}, {"27/32", 0.84375, 21.431}, {"55/64", 0.859375, 21.8282}, {"7/8", 0.875, 22.2251}, {"57/64", 0.890625, 22.6220}, 
{"29/32", 0.90625, 23.0188}, {"59/64", 0.921875, 23.4157}, {"15/16", 0.9375, 23.8126}, {"61/64", 0.953125, 24.2095}, {"31/32", 0.96875, 24.6063}, {"63/64", 0.984375, 25.0032}, {"1", 1.0, 25.4}
};
const int arraySize = sizeof(fraction_conversion) / sizeof(fraction_conversion[0]);

// Number array
int gearInventory[] = {
  127, 113, 109, 96, 90, 86, 85, 80, 79, 77, 76, 75, 73, 71, 70, 67, 65, 62, 61, 60, 59, 58, 56, 
  55, 54, 50, 49, 48, 47, 46, 45, 43, 41, 40, 38, 37, 36, 35, 34, 33, 32, 30, 28, 26, 25, 24, 23, 
  22, 20
};
const int gearInventoryLength = sizeof(gearInventory) / sizeof(gearInventory[0]);

// Swap function
void swapElements(int &a, int &b) {
  int temp = a;
  a = b;
  b = temp;
}

// Median-of-three pivot selection
int medianOfThree(int arr[], int low, int high) {
  int mid = low + (high - low) / 2;
  if (arr[low] > arr[mid]) swapElements(arr[low], arr[mid]);
  if (arr[low] > arr[high]) swapElements(arr[low], arr[high]);
  if (arr[mid] > arr[high]) swapElements(arr[mid], arr[high]);
  return arr[mid]; // Return pivot value instead of index
}

// Partition function for Quick Sort
int partition(int arr[], int low, int high) {
  int pivot = medianOfThree(arr, low, high);
  int i = low - 1;
  
  for (int j = low; j < high; j++) {
    if (arr[j] >= pivot) { // Sorting in descending order
      i++;
      swapElements(arr[i], arr[j]);
    }
  }
  swapElements(arr[i + 1], arr[high]);
  return i + 1;
}

// Quick Sort implementation
void quickSort(int arr[], int low, int high) {
  if (low < high) {
    int pi = partition(arr, low, high);
    quickSort(arr, low, pi - 1);
    quickSort(arr, pi + 1, high);
  }
}

// Function to read value from keypad
String getInputFromKeypad() {
  String input = "";
  char key;

  while (true) {
    key = keypad.getKey();
    if (key) {
      if (key == '#') break;
      if (key == '*') {
        input += '.';
        lcd.print(',');
      } else if (key == 'D') {
        if (input.length() > 0) {
          input.remove(input.length() - 1);
          lcd.clear();
          lcd.print("Input, ");
          lcd.print(input);
        }
      } else {
        input += key;
        lcd.print(key);
      }
    }
  }
  return input;
}

// Function to check conditions
bool checkConditions(float i, float j, float k, float l) {
  bool condition1 = ((i + l) / 2.0 < 4600 && (i + j) / 2.0 < 6400 && (k + l) / 2.0 < 5200);
  bool condition2 = ((i + l) / 2.0 > 0 && (i + j) / 2.0 > 0 && (k + l) / 2.0 > 0);
  return condition1 && condition2;
}

// Find combinations
void findCombination(float Z, float Zc) {
  float epsilon = 0.0001;
  double Sxesi = 2.4 * (Zc / Z);
  
  lcd.clear();
  lcd.print("Looking for 2...");
  delay(1000);

  // Search for 2 numbers
  for (int i = 0; i < gearInventoryLength; i++) {
    for (int j = 0; j < gearInventoryLength; j++) {
      if (i != j) {
        float ratio = static_cast<float>(gearInventory[i]) / static_cast<float>(gearInventory[j]);
        float difference = fabs(Sxesi - ratio);

        if (difference < epsilon) {
          lcd.clear();
          lcd.print("Found: ");
          lcd.setCursor(0, 1);
          lcd.print(gearInventory[i]);
          lcd.print("-");
          lcd.print(gearInventory[j]);
          delay(5000);
          return;
        }
      }
    }
  }

  // Search for 4 numbers
  lcd.clear();
  lcd.print("Looking for 4...");
  delay(1000);

  for (int i = 0; i < gearInventoryLength; i++) {
    for (int j = 0; j < gearInventoryLength; j++) {
      for (int k = 0; k < gearInventoryLength; k++) {
        for (int l = 0; l < gearInventoryLength; l++) {
          if (i != j && i != k && i != l && j != k && j != l && k != l) {
            float ratio = static_cast<float>(gearInventory[i]) / static_cast<float>(gearInventory[j]) *
                          static_cast<float>(gearInventory[k]) / static_cast<float>(gearInventory[l]);
            float difference = fabs(Sxesi - ratio);

            if (difference < epsilon && checkConditions(gearInventory[i], gearInventory[j], gearInventory[k], gearInventory[l])) {
              lcd.clear();
              lcd.print("Found: ");
              lcd.setCursor(0, 1);
              lcd.print(gearInventory[i]);
              lcd.print("-");
              lcd.print(gearInventory[j]);
              lcd.print("-");
              lcd.print(gearInventory[k]);
              lcd.print("-");
              lcd.print(gearInventory[l]);
              delay(5000);
              return;
            }
          }
        }
      }
    }
  }

  lcd.clear();
  lcd.print("Not found");
  delay(2000);
}

  

 

// Setup function
void setup() {
  lcd.begin(16, 2);
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("A = gears 2");
  lcd.setCursor(0, 1);
  lcd.print("B = angles");

  // Sort the array in descending order
  quickSort(gearInventory, 0, gearInventoryLength - 1);
}

// Main loop
void loop() {
  char key = keypad.getKey();

  if (key) {
    if (key == 'A') {
      lcd.clear();
      lcd.print("Enter Z:");
      String inputZ = getInputFromKeypad();
      float Z = inputZ.toFloat();
      
      lcd.clear();
      lcd.print("Enter Zc:");
      String inputZc = getInputFromKeypad();
      float Zc = inputZc.toFloat();

      lcd.clear();
      lcd.print("Processing...");
      findCombination(Z, Zc);
    }  if (key == 'C') {
  lcd.clear();
  lcd.print("Restarting...");
  delay(1000);

  wdt_enable(WDTO_15MS);
  while (1) {}
}
else if (key == 'B') {
    lcd.clear();
    lcd.print("Enter mm:");
    String inputMMStr = getInputFromKeypad();
    double inputMM = inputMMStr.toFloat();

    lcd.clear();
    lcd.print("Processing...");
    double minDiff = 1e10;  
    int closestIndex = -1;

for (int i = 0; i < arraySize; i++) {
    double diff = fabs(fraction_conversion[i].mm - inputMM); // Compare decimal, not fraction
    if (diff < minDiff) {
        minDiff = diff;
        closestIndex = i;
    }
}

    lcd.clear();
    if (closestIndex != -1) {
        lcd.setCursor(0, 0);
        lcd.print(fraction_conversion[closestIndex].fraction);
        lcd.print(" = ");
        lcd.setCursor(0, 1);
        lcd.print(fraction_conversion[closestIndex].mm, 4);
        lcd.print(" mm");
    } else {
        lcd.print("No match found.");
    }
}
}

      

   }