#include "headers/Park_time.h"

/*
initializes everything, id is auto incremented from what's stored in the db.
inmediately saves to db upon creation.
Also, this weird syntax is called an initializer list, and is the preffered
method of how to initialize members. It has a measurable performance increase
because it uses move semantics instead of copy semantics.
https://www.geeksforgeeks.org/when-do-we-use-initializer-list-in-c/
 */
Park_time::Park_time(int c_id, int s_id)
    : customer_id{c_id},
      spot_id{s_id},
      duration{0},
      start{high_resolution_clock::now()},
      id{auto_increment_db() + 1} {
    save_db();
}
/*
this one initializes with data from the database. should probably only be used in the query
functions.
 */
Park_time::Park_time(int id_, int customer_id_, int spot_id_, int start_, int duration_)
    : id{id_}, customer_id{customer_id_}, spot_id{spot_id_}, duration{duration_} {
    start = time_point<system_clock>(seconds(start_));
    end = time_point<system_clock>(seconds(start_ + duration_));
}

/*
simple checking if customer is clocking out at the right spot.
sets end(time of clocking out) and calculates the duration.
updates the info in the database.

 */
void Park_time::clock_out(int c_id, int s_id) {

    if (c_id != customer_id) {
        cout << "wrong customer id, you are at the wrong location";
        return;
    }
    if (s_id != spot_id) {
        cout << "Wrong spot id, you're at the wrong location";
        return;
    }

    if (!duration) {
        end = high_resolution_clock::now();
        duration = duration_cast<seconds>(end - start).count(); // use mins later
        update_db();

    } else {
        cout << "Already clocked out. Something is wrong \n";
    }
}

std::ostream& operator<<(std::ostream& os, const Park_time& pt) {
    std::time_t start_ = system_clock::to_time_t(pt.start);
    std::time_t end_ = system_clock::to_time_t(pt.end);
    os << "- - - - - - - - - - - - - - - - - - - -\n";
    os << "Customer # " << pt.customer_id << "at parking spot " << pt.spot_id << "\n";
    os << "Clocked in :" << std::ctime(&start_);
    os << "clocked out : " << std::ctime(&end_);
    os << "duration : " << pt.duration << "\n";
    os << "- - - - - - - - - - - - - - - - - - - -\n";
    return os;
}
// mostly a helper function to ease the conversion from timepoint to int
// for storing in the db
int Park_time::start_to_int() {
    auto start_to_epoch = start.time_since_epoch();
    auto start_value = duration_cast<seconds>(start_to_epoch);
    int start_seconds = start_value.count();
    return start_seconds;
}

// db funcs
// -----------------------------------------------------------------------------

void Park_time::save_db() {
    /*
    this creates a sql statement and then executes it
     */
    string statement{"insert into Park_time values ( , , , , , );"};
    statement.insert(41, "NULL");
    statement.insert(39, "NULL");
    statement.insert(37, to_string(start_to_int()));
    statement.insert(35, to_string(spot_id));
    statement.insert(33, to_string(customer_id));
    statement.insert(31, to_string(id));
    SQLite::Transaction transaction(data::db);
    data::db.exec(statement);
    transaction.commit();
}
// same as above
void Park_time::update_db() {
    string statement = "UPDATE Park_time SET end = , duration =  where id = '';";
    statement.insert(53, to_string(id));
    statement.insert(40, to_string(duration));
    statement.insert(27, to_string(start_to_int() + duration));
    data::db.exec(statement);
}

// to get id on first save to db
int Park_time::auto_increment_db() {
    SQLite::Statement max_id(data::db, "select max(id) from Park_time;");
    int id = 0;
    max_id.executeStep();
    id = max_id.getColumn(0);
    max_id.reset();
    return id;
}

//------------------ test function to help test this

void Wait(int sec)

{
    /*
a wait function where 1 sec represents 1 hour irl. It has been used for testing
purposes mostly. TODO: Needs to be removed at completion of project, or seperated in a test
cpp/header
 */
    std::this_thread::sleep_for(seconds{sec});
}