#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_RECORDS  200
#define SERVICE_LEN   60
#define USER_LEN      60
#define PASS_LEN      60
#define DATA_FILE     "passwords.txt"
#define SHIFT          7

// Holds one credential record: service name, username, and password
typedef struct {
    char service[SERVICE_LEN];
    char user[USER_LEN];
    char pass[PASS_LEN];
} Entry;

void flush_input(void) { // clears leftover characters from the input buffer
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF);
}

void read_line(char *buf, int limit) { // reads one line from keyboard, removes the newline
    fgets(buf, limit, stdin);
    buf[strcspn(buf, "\n")] = '\0';
}

void encode(char *s) { // shifts every character forward by SHIFT to hide the text
    for (; *s; s++) *s = (char)(*s + SHIFT);
}

void decode(char *s) { // shifts every character back by SHIFT to restore the original text
    for (; *s; s++) *s = (char)(*s - SHIFT);
}

void print_divider(void) { // prints a horizontal line used when displaying entries
    printf("  +--------------------------------------------------+\n");
}

void print_entry(const Entry *e) { // prints one entry inside a box with all three fields
    print_divider();
    printf("  | Service  : %-38s |\n", e->service);
    printf("  | Username : %-38s |\n", e->user);
    printf("  | Password : %-38s |\n", e->pass);
}

int load_all(Entry entries[], int max) { // reads all entries from passwords.txt into the array, returns how many were loaded
    FILE *fp = fopen(DATA_FILE, "r");
    if (!fp) return 0;

    int count = 0;
    while (count < max &&
           fscanf(fp, "%59[^\n]\n%59[^\n]\n%59[^\n]\n",
                  entries[count].service,
                  entries[count].user,
                  entries[count].pass) == 3) {
        decode(entries[count].service);
        decode(entries[count].user);
        decode(entries[count].pass);
        count++;
    }

    fclose(fp);
    return count;
}

void save_all(Entry entries[], int count) { // encodes all entries and writes them back to passwords.txt
    FILE *fp = fopen(DATA_FILE, "w");
    if (!fp) { printf("  Error writing to file.\n"); return; }

    int i;
    for (i = 0; i < count; i++) {
        Entry tmp = entries[i];
        encode(tmp.service);
        encode(tmp.user);
        encode(tmp.pass);
        fprintf(fp, "%s\n%s\n%s\n", tmp.service, tmp.user, tmp.pass);
    }
    fclose(fp);
}

void add_entry(void) { // asks the user for a service, username and password then saves it encoded to the file
    FILE *fp = fopen(DATA_FILE, "a");
    if (!fp) { printf("  Error opening file.\n"); return; }

    Entry e;
    printf("\n  Enter service name : "); read_line(e.service, SERVICE_LEN);
    printf("  Enter username     : "); read_line(e.user,    USER_LEN);
    printf("  Enter password     : "); read_line(e.pass,    PASS_LEN);

    if (e.service[0] == '\0' || e.user[0] == '\0' || e.pass[0] == '\0') {
        printf("  Fields cannot be empty.\n");
        fclose(fp);
        return;
    }

    encode(e.service);
    encode(e.user);
    encode(e.pass);
    fprintf(fp, "%s\n%s\n%s\n", e.service, e.user, e.pass);
    fclose(fp);
    printf("  Entry saved.\n");
}

void list_entries(void) { // loads and prints all saved entries to the screen
    Entry records[MAX_RECORDS];
    int total = load_all(records, MAX_RECORDS);

    if (total == 0) { printf("  No entries stored yet.\n"); return; }

    printf("\n  All entries (%d total)\n", total);
    int i;
    for (i = 0; i < total; i++) {
        printf("  [%d]\n", i + 1);
        print_entry(&records[i]);
    }
    print_divider();
}

void search_entry(void) { // asks for a service name and prints every entry that matches
    char query[SERVICE_LEN];
    printf("\n  Enter service name to search: ");
    read_line(query, SERVICE_LEN);

    Entry records[MAX_RECORDS];
    int total = load_all(records, MAX_RECORDS);
    int hits = 0;
    int i;

    printf("\n  Results for \"%s\"\n", query);
    for (i = 0; i < total; i++) {
        if (strcmp(records[i].service, query) == 0) {
            print_entry(&records[i]);
            hits++;
        }
    }

    if (hits == 0)
        printf("  No entries found for \"%s\".\n", query);
    else
        print_divider();
}

void delete_entry(void) { // removes all entries matching the given service name and saves the rest
    char query[SERVICE_LEN];
    printf("\n  Enter service name to delete: ");
    read_line(query, SERVICE_LEN);

    Entry records[MAX_RECORDS];
    int total   = load_all(records, MAX_RECORDS);
    int removed = 0;
    int i;

    Entry kept[MAX_RECORDS];
    int keep_count = 0;

    for (i = 0; i < total; i++) {
        if (strcmp(records[i].service, query) == 0) {
            removed++;
        } else {
            kept[keep_count++] = records[i];
        }
    }

    if (removed == 0) {
        printf("  No entries found for \"%s\".\n", query);
        return;
    }

    save_all(kept, keep_count);
    printf("  Deleted %d entry/entries for \"%s\".\n", removed, query);
}

int main(void) {
    int choice;

    for (;;) {
        printf("\n");
        printf("  +--------------------------------+\n");
        printf("  |       PASSWORD MANAGER         |\n");
        printf("  +--------------------------------+\n");
        printf("  |  1. Add entry                  |\n");
        printf("  |  2. List all entries           |\n");
        printf("  |  3. Search by service          |\n");
        printf("  |  4. Delete entry               |\n");
        printf("  |  5. Exit                       |\n");
        printf("  +--------------------------------+\n");
        printf("  Choice: ");

        if (scanf("%d", &choice) != 1) {
            printf("  Please enter a number.\n");
            flush_input();
            continue;
        }
        flush_input();

        switch (choice) {
            case 1: add_entry();    break;
            case 2: list_entries(); break;
            case 3: search_entry(); break;
            case 4: delete_entry(); break;
            case 5: printf("  Goodbye.\n"); return 0;
            default: printf("  Invalid option, try again.\n");
        }
    }
}
