/* CS107
 * Lecture 10
 *
 * This file shows some of the pitfalls with performing
 * float arithmetic in the context of a bank balance and
 * continually updating this bank balance. This program
 * expects 2 command line arguments, the first of which
 * is the initial balance to use, and the second is the
 * amount to deposit (if positive) or withdraw (if negative)
 * each iteration.  This program prints out the results
 * of continually making the specified transaction starting
 * from this balance.  If fewer than 2 command line arguments
 * are provided, the program terminates with an error message. 
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <error.h>
#include <float.h>
#include <unistd.h>
#include <stdbool.h>

// This constant represents the number of iterations to update the balance
#define NUM_TRANSACTIONS 1000


/* This function prints a welcome message for the program with an overview
 * of the balance and depositing/withdrawing.
 */
void print_welcome() {
    printf("Welcome to Lisa's (soon-to-be-bankrupt) Bank!\n");
    printf("We'll create a fake bank account with the amount you specify\n");
    printf("as the first parameter, and repeatedly transact the amount\n");
    printf("in the second parameter (e.g. deposit if positive, withdraw\n");
    printf("if negative), reporting the total amount transacted and the\n");
    printf("remaining balance at each step.\n\n");
}

/* This function converts the provided string argument into a float, and
 * returns the float.  If the string cannot be parsed, this function throws
 * an error using the error() function and terminates the program.
 */
float parse_float(char *arg) {
    char *end;
    float parsed = strtof(arg, &end);
    if (*end != '\0' || errno) {
        error(1, errno, "invalid number '%s'", arg);
    }
    return parsed;
}
    
/*
 * Return "deposit" or withdraw" based on transaction amount.
 * -----------------------------
 * Why is there no malloc in this function?
 * Because we are returning string literals, which are created
 * at compile-time in read-only memory (the data segment).
 * String literals are located in neither the stack nor the heap.
 */
char *get_transac_str(float transaction_amount) {
    char *transac_str;
    if (transaction_amount > 0) {
        transac_str = "deposited";
    } else { // yes, weirdly, considers zero case
        transac_str = "withdrawn";
    }
    return transac_str;
}

/*
 * Return "+", "-", or "n/a" based on whether the new balance is
 * (respectively) greater than, smaller than, or unchanged from
 * the old balance.
 * -----------------------------
 * Why is there no malloc in this function?
 * Because we are returning string literals, which are created
 * at compile-time in read-only memory (the data segment).
 * String literals are located in neither the stack nor the heap.
 */
char *get_balance_str(float new_balance, float balance) {
    char *balance_str = "n/a";
    if (new_balance > balance) {
        balance_str = "+";
    } else if (new_balance < balance) {
        balance_str = "-";
    }
    return balance_str;
}

/* This function continually adds transaction_amount to balance
 * NUM_TRANSACTIONS times, at each step printing out the new balance, the
 * total amount deposited/withdrawn so far, and whether the balance
 * changed as a result of the transaction.  It waits for 1 second
 * in between transactions.
 */
void print_bank_info(float balance, float transaction_amount) {
    printf("Starting balance: $%.2f\n", balance);
    float amount_transacted = 0;
    for (int i = 0; i < NUM_TRANSACTIONS; i++) {
        // Update the balance and total amount transacted
        float new_balance = balance + transaction_amount;
        amount_transacted -= transaction_amount;
        
        // print out update information
        char *transac_str = get_transac_str(transaction_amount);
        char *balance_str = get_balance_str(new_balance, balance);
        float abs_transac = amount_transacted;
        if (abs_transac < 0) abs_transac = -1*abs_transac;
        printf("%2i: Balance = $%.2f, Total %s: $%.2f\n",
                i + 1, new_balance, transac_str, abs_transac);
        printf("\t\tchange: (%s)\n", balance_str);

        // update balance
        balance = new_balance;
        
        sleep(1);
    }

    printf("Done.\n");
}


int main(int argc, char *argv[]) {
    print_welcome();

    if (argc < 3) {
        error(1, 0, "Please enter a balance followed by a transaction amount.");
    }

    float initial_balance = parse_float(argv[1]);
    float transaction_amount = parse_float(argv[2]);
    print_bank_info(initial_balance, transaction_amount);
 
    return 0;
}
