#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SoftwareSerial.h>
#include <Keypad.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1  // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  // Change to 0x3D if required

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
SoftwareSerial gsmSerial(7, 8); // RX, TX

// Icons for different signal levels
const uint8_t noSignalIcon[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};

const uint8_t weakSignalIcon[] PROGMEM = {
  0x00, 0x00, 0x00, 0x80,
  0x00, 0x00, 0x00, 0x80,
  0x00, 0x00, 0x00, 0x80,
  0x00, 0x00, 0x00, 0x80
};

const uint8_t mediumSignalIcon[] PROGMEM = {
  0x00, 0x00, 0x80, 0x80,
  0x00, 0x00, 0x80, 0x80,
  0x00, 0x00, 0x80, 0x80,
  0x00, 0x00, 0x80, 0x80
};

const uint8_t strongSignalIcon[] PROGMEM = {
  0x80, 0x80, 0x80, 0x80,
  0x80, 0x80, 0x80, 0x80,
  0x80, 0x80, 0x80, 0x80,
  0x80, 0x80, 0x80, 0x80
};

const uint8_t* signalIcons[] = {noSignalIcon, weakSignalIcon, mediumSignalIcon, strongSignalIcon};

// Keypad setup
const byte ROWS = 4; // Four rows
const byte COLS = 4; // Four columns
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};    // Connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 7, 8, 9};    // Connect to the column pinouts of the keypad

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

void setup() {
  // Initialize Serial Monitor
  Serial.begin(9600);

  // Initialize OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("NIVAN TECH");
  display.display();
  
  // Initialize GSM module
  gsmSerial.begin(9600);
  delay(1000);
  gsmSerial.println("AT");
  delay(1000);
  gsmSerial.println("AT+CMGF=1"); // Set SMS to text mode
  delay(1000);
}

void loop() {
  // Check GSM signal strength and display icon
  displaySignalStrength();

  char key = keypad.getKey();
  if (key) {
    handleKeypadInput(key);
  }

  delay(1000); // Wait 1 second before repeating loop
}

void handleKeypadInput(char key) {
  static String number = "";

  if (key >= '0' && key <= '9') {
    number += key;
    displayNumber(number);
  } else if (key == '*') {
    // Clear the number
    number = "";
    display.clearDisplay();
    display.display();
  } else if (key == '#') {
    // Send SMS with the number
    sendSMS(number.c_str(), "Hello from NIVAN TECH");
    number = "";
  } else if (key == 'A') {
    // Make a call to the number
    makeCall(number.c_str());
    number = "";
  }
}

void displayNumber(const String& number) {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("Number:");
  display.println(number);
  display.display();
}

void sendSMS(const char* number, const char* text) {
  gsmSerial.println("AT+CMGS=\"" + String(number) + "\""); // Replace with actual number
  delay(1000);
  gsmSerial.print(text);
  delay(1000);
  gsmSerial.write(26); // Send Ctrl+Z to send the SMS
  delay(5000);

  // Display message on OLED
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("SMS Sent!");
  display.display();
  delay(2000);
}

void makeCall(const char* number) {
  gsmSerial.println("ATD" + String(number) + ";"); // Dial the number
  delay(1000);

  // Display calling status on OLED
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("Calling...");
  display.display();

  delay(10000); // Wait for 10 seconds (simulating the call duration)

  // End the call
  gsmSerial.println("ATH");
  delay(1000);

  // Display call ended status on OLED
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("Call Ended");
  display.display();
  delay(2000);
}

void checkForIncomingCall() {
  if (gsmSerial.available()) {
    String response = gsmSerial.readString();

    // Look for the RING keyword in the response
    if (response.indexOf("RING") != -1) {
      // Display incoming call on OLED
      display.clearDisplay();
      display.setCursor(0, 0);
      display.println("Incoming Call");
      display.display();

      delay(10000); // Simulating answering the call and talking for 10 seconds

      // End the call
      gsmSerial.println("ATH");
      delay(1000);

      // Display call ended status on OLED
      display.clearDisplay();
      display.setCursor(0, 0);
      display.println("Call Ended");
      display.display();
      delay(2000);
    }
  }
}

void displaySignalStrength() {
  gsmSerial.println("AT+CSQ");
  delay(1000);
  
  if (gsmSerial.available()) {
    String response = gsmSerial.readString();
    int signalStrength = parseSignalStrength(response);
    
    int iconIndex = map(signalStrength, 0, 31, 0, 3);
    iconIndex = constrain(iconIndex, 0, 3);

    // Display signal strength icon on OLED
    display.clearDisplay();
    display.drawBitmap(0, 0, signalIcons[iconIndex], 8, 8, WHITE);
    display.display();
  }
}

int parseSignalStrength(String response) {
  int startIndex = response.indexOf("+CSQ: ");
  if (startIndex != -1) {
    int commaIndex = response.indexOf(",", startIndex);
    String signalValue = response.substring(startIndex + 6, commaIndex);
    return signalValue.toInt();
  }
  return 0;
}