/*
Copyright(C) 2020 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA.
Version: 1.2
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <iomanip>
#ifdef _WIN32
#include <windows.h>
#pragma execution_character_set("utf-8")
#endif
//#define _DEBUG
class SETT
{
const std::string settings_fp = "settings";
const std::string translation_dp = "translations/";
bool veccmp(const std::vector<std::string> &arr, const std::string &val)
{
for (std::vector<std::string>::const_iterator it = arr.begin(); it != arr.end(); it++)
{
if (it->compare(val) == 0)
{
return true;
}
}
return false;
}
std::vector<std::string> cmd_arg = {
"SV_NEW",
"SV_DELETE"
};
std::map<std::string, std::string> sett = {
{"SV_DATABASE_PATH", "data"},
{"SV_LANGUAGE", "en"},
{"SV_TIMEZONE", "+0"},
{"SV_DATE_ORDER", "DMYhms"},
{"SV_CREDIT_COLOR", "1;32"},
{"SV_DEBIT_COLOR", "1;35"},
{"SV_ORDER_BY_DATE", "0"},
{"SV_DEBIT_FIRST", "0"},
{"SV_CREDIT_FIRST", "0"},
{"SV_ONLY_DEBIT", "0"},
{"SV_ONLY_CREDIT", "0"},
{"SV_NO_COLOR", "0"},
{"SV_NEW", "0"},
{"SV_DELETE", ""},
{"SV_START_DATE", ""},
{"SV_END_DATE", ""},
{"SV_FORMAT", "%d.%m.%Y %H:%M:%S"},
{"SV_ORDER", "cod-dat-nam-amo-ref-des-tzo"},
{"SV_CURRENCY", "$"},
{"SV_CURRENCY_SIDE", "1"},
{"T_CREDIT_OR_DEBIT", "Credit or Debit?(credit/debit): "},
{"T_CREDIT_OR_DEBIT_A", "credit"},
{"T_DAY", "Day:\t"},
{"T_MONTH", "Month:\t"},
{"T_YEAR", "Year:\t"},
{"T_HOURS", "Hours:\t"},
{"T_MINUTES", "Minutes:\t"},
{"T_SECONDS", "Seconds:\t"},
{"T_NAME", "Name:\t\t"},
{"T_AMOUNT", "Amount:\t\t"},
{"T_REF", "Ref:\t\t"},
{"T_DESCRIPTION", "Description:\t"},
{"T_TIMEZONE", "Timezone:\t"},
{"T_DATE", "Date:\t\t"},
{"T_BALANCE", "Balance: "},
{"T_CREDIT", "CREDIT"},
{"T_DEBIT", "DEBIT"}
};
public:
SETT();
std::string get(const std::string &key);
bool getb(const std::string& key);
bool add(std::string key, const std::string &val);
};
SETT::SETT()
{
// Read settings file
std::ifstream file(settings_fp);
if (!file.good())
{
return;
}
if (file.peek() == std::ifstream::traits_type::eof())
{
return;
}
std::string current_line;
while (std::getline(file, current_line))
{
if (current_line.empty()) continue;
std::string key;
std::string val;
int index = current_line.find("=");
if (index != -1)
{
key = current_line.substr(0, index);
val = current_line.substr(index + 1, current_line.size() - 1);
}
else
{
key = current_line;
val = "1";
}
for (unsigned int i = 0; i < key.size(); i++)
{
char c = key.at(i);
// abc --> ABC
if (c > 96 && c < 123)
{
c -= 32;
key.at(i) = c;
}
}
if (!veccmp(cmd_arg, key) && key.at(0) == 'S' && key.at(1) == 'V')
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
it->second = val;
}
}
}
// Read translations file
std::string langauge_fn = get("SV_LANGUAGE");
if (!langauge_fn.empty())
{
std::ifstream file0(translation_dp + langauge_fn);
if (!file0.good())
{
return;
}
if (file0.peek() == std::ifstream::traits_type::eof())
{
return;
}
while (std::getline(file0, current_line))
{
if (current_line.empty()) continue;
std::string key;
std::string val;
int index = current_line.find("=");
if (index != -1)
{
key = current_line.substr(0, index);
val = current_line.substr(index + 1, current_line.size() - 1);
}
else
{
key = current_line;
val = "1";
}
for (unsigned int i = 0; i < key.size(); i++)
{
char c = key.at(i);
// abc --> ABC
if (c > 96 && c < 123)
{
c -= 32;
key.at(i) = c;
}
}
if (!veccmp(cmd_arg, key) && key.at(0) == 'T')
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
it->second = val;
}
}
}
}
#ifdef _DEBUG
for (std::map<std::string, std::string>::iterator it = sett.begin(); it != sett.end(); it++)
{
std::cout << it->first << "=" << it->second << std::endl;
}
#endif
}
std::string SETT::get(const std::string &key)
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
return it->second;
}
return std::string("");
}
bool SETT::getb(const std::string& key)
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
return std::stoi(it->second) > 0;
}
return false;
}
bool SETT::add(std::string key, const std::string &val)
{
for (unsigned int i = 0; i < key.size(); i++)
{
char c = key.at(i);
if (c == '-')
{
key.at(i) = '_';
continue;
}
// abc --> ABC
if (c > 96 && c < 123)
{
c -= 32;
key.at(i) = c;
}
}
key = std::string("SV_") + key;
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
it->second = val;
return false;
}
return true;
}
struct transaction {
time_t date;
time_t date_current;
std::string reference_num;
int timezone;
bool credit_or_debit;
double amount;
std::string name;
std::string description;
unsigned int index;
};
std::vector<transaction> data; // All data.
std::vector<transaction> data0; // Data visible.
void read_database(const std::string &fn);
bool save_database(const std::string &fn);
int set_timezone(const std::string &tz);
std::string get_timezone(int timezone);
time_t date_to_seconds(const std::string& date)
{
int yy, month, dd, hh, mm, ss;
const char* datec = date.c_str();
sscanf(datec, "%d.%d.%d %d:%d:%d", &dd, &month, &yy, &hh, &mm, &ss);
struct tm tm;
tm.tm_year = yy - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = dd;
tm.tm_hour = hh;
tm.tm_min = mm;
tm.tm_sec = ss;
tm.tm_isdst = -1;
time_t t = mktime(&tm);
return t;
}
time_t date_to_seconds(const std::string& date, std::string format)
{
char u[6] = {0};
int i = 0;
for (std::string::iterator it = format.begin(); it != format.end(); it++)
{
if (*it == '%')
{
u[i] = *(it + 1);
*(it + 1) = 'd';
i++;
}
}
int y[6] = {0};
const char* datec = date.c_str();
sscanf(datec, format.c_str(), &y[0], &y[1], &y[2], &y[3], &y[4], &y[5]);
int yy, month, dd, hh, mm, ss;
for (i = 0; i < 6; i++)
{
if (u[i] == 'd')
{
dd = y[i];
continue;
}
if (u[i] == 'm')
{
month = y[i];
continue;
}
if (u[i] == 'Y')
{
yy = y[i];
continue;
}
if (u[i] == 'H')
{
hh = y[i];
continue;
}
if (u[i] == 'M')
{
mm = y[i];
continue;
}
if (u[i] == 'S')
{
ss = y[i];
}
}
struct tm tm;
tm.tm_year = yy - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = dd;
tm.tm_hour = hh;
tm.tm_min = mm;
tm.tm_sec = ss;
tm.tm_isdst = -1;
time_t t = mktime(&tm);
return t;
}
void get_word(std::string *val)
{
std::string usr_input;
std::getline(std::cin, usr_input);
if (usr_input.empty())
{
*val = "";
return;
}
int find_pos = usr_input.find(" ");
if (find_pos != -1)
{
usr_input = usr_input.substr(0, find_pos);
}
*val = usr_input;
}
int main(int argc, char *argv[])
{
#ifdef _WIN32
SetConsoleOutputCP(65001);
#endif
std::string fp;
std::vector<std::string> cmd_arg;
SETT sett;
if (argc > 1)
{
cmd_arg = std::vector<std::string>(argv, argv + argc);
cmd_arg.erase(cmd_arg.begin());
cmd_arg.shrink_to_fit();
for (std::vector<std::string>::iterator it = cmd_arg.begin(); it != cmd_arg.end(); it++)
{
std::string lhs;
std::string rhs;
int index = it->find('=');
if (index != -1)
{
lhs = it->substr(0, index);
rhs = it->substr(index + 1, it->size());
}
else
{
static bool prevent_double = false;
if (!prevent_double)
{
prevent_double = true;
std::vector<std::string>::iterator it0 = it;
for (; it0 != cmd_arg.end(); it0++)
{
int index0 = it0->find('=');
if (index0 != -1)
return 0;
}
}
lhs = *it;
rhs = "1";
}
sett.add(lhs, rhs);
}
}
int timezone = set_timezone(sett.get("SV_TIMEZONE"));
const std::string format = sett.get("SV_FORMAT");
const std::string order = sett.get("SV_ORDER");
fp = sett.get("SV_DATABASE_PATH");
read_database(fp);
// Order by date
if (!data.empty())
{
for (unsigned int i = 0; i < data.size() - 1; i++)
{
if (data.at(i).date > data.at(i + 1).date)
{
for (unsigned int u = i; u < data.size() - 1; u++)
{
if (data.at(u).date < data.at(u + 1).date) break;
std::vector<transaction>::iterator it = data.begin() + u;
data.insert(data.begin() + u + 2, *it);
data.erase(data.begin() + u);
}
if (i > 0) i -= 2;
else i--;
}
}
data0 = data;
}
if (argc > 1)
{
// SV_NEW - cmd arg
if (sett.getb("SV_NEW"))
{
transaction new_data;
for (unsigned int i = 0; i < 7; i++)
{
std::string usr_input;
char current[4];
current[0] = order.at(4 * i);
current[1] = order.at(4 * i + 1);
current[2] = order.at(4 * i + 2);
current[3] = '\0';
// credit or debit
if (strcmp(current, "cod") == 0)
{
std::cout << sett.get("T_CREDIT_OR_DEBIT");
get_word(&usr_input);
if (usr_input.empty())
return 0;
if (strcmp(usr_input.c_str(), sett.get("T_CREDIT_OR_DEBIT_A").c_str()) == 0)
{
usr_input = "1";
}
else
{
usr_input = "0";
}
new_data.credit_or_debit = std::stoi(usr_input.c_str()) > 0;
}
// date
if (strcmp(current, "dat") == 0)
{
std::string dmy = sett.get("SV_DATE_ORDER");
std::string dd, month, yy, mm, hh, ss;
for (unsigned int i = 0; i < 6; i++)
{
if (dmy.at(i) == 'D')
{
std::cout << sett.get("T_DAY");
get_word(&dd);
if (dd.empty())
return 0;
continue;
}
if (dmy.at(i) == 'M')
{
std::cout << sett.get("T_MONTH");
get_word(&month);
if (month.empty())
return 0;
continue;
}
if (dmy.at(i) == 'Y')
{
std::cout << sett.get("T_YEAR");
get_word(&yy);
if (yy.empty())
return 0;
continue;
}
if (dmy.at(i) == 'h')
{
std::cout << sett.get("T_HOURS");
get_word(&hh);
if (hh.empty())
hh = "0";
continue;
}
if (dmy.at(i) == 'm')
{
std::cout << sett.get("T_MINUTES");
get_word(&mm);
if (mm.empty())
mm = "0";
continue;
}
if (dmy.at(i) == 's')
{
std::cout << sett.get("T_SECONDS");
get_word(&ss);
if (ss.empty())
ss = "0";
continue;
}
}
std::string date = dd;
date += ".";
date += month;
date += ".";
date += yy;
date += " ";
date += hh;
date += ":";
date += mm;
date += ":";
date += ss;
new_data.date = date_to_seconds(date);
}
// name
if (strcmp(current, "nam") == 0)
{
std::cout << sett.get("T_NAME");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.name = usr_input;
}
// amount
if (strcmp(current, "amo") == 0)
{
std::cout << sett.get("T_AMOUNT");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.amount = std::atof(usr_input.c_str());
}
// reference
if (strcmp(current, "ref") == 0)
{
std::cout << sett.get("T_REF");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.reference_num = usr_input;
}
// description
if (strcmp(current, "des") == 0)
{
std::cout << sett.get("T_DESCRIPTION");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.description = usr_input;
}
// timezone
if (strcmp(current, "tzo") == 0)
{
std::cout << sett.get("T_TIMEZONE");
get_word(&usr_input);
if (usr_input.empty())
{
new_data.timezone = timezone;
}
else
{
new_data.timezone = set_timezone(usr_input);
}
new_data.date -= new_data.timezone;
}
std::cin.clear();
fflush(stdin);
}
new_data.date_current = std::time(nullptr);
#ifdef _DEBUG
std::cout << std::endl;
std::cout << "----- NEW DATA -----" << std::endl;
std::cout << "date: " << new_data.date << std::endl;
std::cout << "date_current: " << new_data.date_current << std::endl;
std::cout << "reference_num: " << new_data.reference_num << std::endl;
std::cout << "timezone: " << new_data.timezone << std::endl;
std::cout << "credit_or_debit: " << new_data.credit_or_debit << std::endl;
std::cout << "amount: " << new_data.amount << std::endl;
std::cout << "name: " << new_data.name << std::endl;
std::cout << "description: " << new_data.description << std::endl;
time_t tt = new_data.date_current + timezone;
std::cout << "ct: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
tt = new_data.date + new_data.timezone;
std::cout << "dt: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
return 0;
#endif
data.push_back(new_data);
save_database(fp);
return 0;
}
if (data0.empty())
{
return 0;
}
// SV_START_DATE - cmd arg
std::string start_date = sett.get("SV_START_DATE");
if (!start_date.empty())
{
time_t date = date_to_seconds(start_date, format.c_str());
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->date + it->timezone < date)
{
data0.erase(it);
it--;
}
}
}
// SV_END_DATE - cmd arg
std::string end_date = sett.get("SV_END_DATE");
if (!end_date.empty())
{
time_t date = date_to_seconds(end_date, format.c_str());
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->date + it->timezone > date)
{
data0.erase(it);
it--;
}
}
}
// SV_ORDER_BY_DATE - cmd arg
if (sett.getb("SV_ORDER_BY_DATE"))
{
for (unsigned int i = 0; i < data0.size() - 1; i++)
{
if (data0.at(i).date_current > data0.at(i + 1).date_current)
{
for (unsigned int u = i; u < data0.size() - 1; u++)
{
if (data0.at(u).date_current < data0.at(u + 1).date_current) break;
std::vector<transaction>::iterator it = data0.begin() + u;
data0.insert(data0.begin() + u + 2, *it);
data0.erase(data0.begin() + u);
}
if (i > 0) i -= 2;
else i--;
}
}
}
// SV_DEBIT_FIRST - cmd arg
if (sett.getb("SV_DEBIT_FIRST"))
{
std::vector<transaction> temp;
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 0)
{
transaction tra = *it;
data0.erase(it);
temp.push_back(tra);
it--;
}
}
data0.insert(data0.end(), temp.begin(), temp.end());
}
// SV_CREDIT_FIRST - cmd arg
else if (sett.getb("SV_CREDIT_FIRST"))
{
std::vector<transaction> temp;
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 1)
{
transaction tra = *it;
data0.erase(it);
temp.push_back(tra);
it--;
}
}
data0.insert(data0.end(), temp.begin(), temp.end());
}
// SV_ONLY_DEBIT - cmd arg
else if (sett.getb("SV_ONLY_DEBIT"))
{
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 1)
{
data0.erase(it);
it--;
}
}
}
// SV_ONLY_CREDIT - cmd arg
else if (sett.getb("SV_ONLY_CREDIT"))
{
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 0)
{
data0.erase(it);
it--;
}
}
}
// SV_DELETE - cmd arg
std::string delete_cmd = sett.get("SV_DELETE");
if (!delete_cmd.empty())
{
if (data0.empty())
{
return 0;
}
for (std::string::iterator it = delete_cmd.begin(); it != delete_cmd.end(); it++)
{
if (*it > 57 || *it < 48)
{
return 0;
}
}
unsigned int index = std::stoi(delete_cmd);
if (index < 1 || index > data0.size())
{
return 0;
}
transaction tra = data0.at(data0.size() - index);
std::vector<transaction>::const_iterator it;
it = data.begin() + tra.index;
data.erase(it);
save_database(fp);
return 0;
}
}
if (data0.empty())
{
return 0;
}
const std::string currency = sett.get("SV_CURRENCY");
double balance = 0;
bool currency_side = sett.getb("SV_CURRENCY_SIDE");
std::cout << std::fixed << std::setprecision(2);
unsigned int index = 0;
for (std::vector<transaction>::const_iterator it = data0.begin(); it != data0.end(); it++)
{
std::cout << "\n[ " << data0.size() - index++ << " ]" << std::endl;
for (unsigned int i = 0; i < 7; i++)
{
char current[4];
current[0] = order.at(4 * i);
current[1] = order.at(4 * i + 1);
current[2] = order.at(4 * i + 2);
current[3] = '\0';
// timezone
if (strcmp(current, "tzo") == 0)
{
continue;
}
// credit or debit
if (strcmp(current, "cod") == 0)
{
bool colored_text = sett.getb("SV_NO_COLOR");
if (it->credit_or_debit)
{
std::string text = sett.get("T_CREDIT");
if (colored_text)
{
std::cout << text << std::endl;
}
else
{
std::string color = "\033[";
color += sett.get("SV_CREDIT_COLOR");
color += "m";
std::cout << color << text << "\033[0m" << std::endl;
}
}
else
{
std::string text = sett.get("T_DEBIT");
if (colored_text)
{
std::cout << text << std::endl;
}
else
{
std::string color = "\033[";
color += sett.get("SV_DEBIT_COLOR");
color += "m";
std::cout << color << text << "\033[0m" << std::endl;
}
}
continue;
}
// date
if (strcmp(current, "dat") == 0)
{
std::cout << sett.get("T_DATE");
time_t date = it->date + it->timezone;
std::cout << std::put_time(gmtime(&date), format.c_str()) << std::endl;
if (it->credit_or_debit)
{
balance += it->amount;
}
else
{
balance -= it->amount;
}
continue;
}
// name
if (strcmp(current, "nam") == 0)
{
std::cout << sett.get("T_NAME") << it->name << std::endl;
continue;
}
// amount
if (strcmp(current, "amo") == 0)
{
std::cout << sett.get("T_AMOUNT");
if (currency_side)
{
std::cout << currency << it->amount << std::endl;
}
else
{
std::cout << it->amount << currency << std::endl;
}
continue;
}
// reference
if (strcmp(current, "ref") == 0)
{
std::cout << sett.get("T_REF");
std::cout << it->reference_num << std::endl;
continue;
}
// description
if (strcmp(current, "des") == 0)
{
std::cout << sett.get("T_DESCRIPTION") << it->description << std::endl;
}
}
}
std::cout << "\n" << sett.get("T_BALANCE");
if (currency_side)
{
std::cout << currency << balance << std::endl;
}
else
{
std::cout << balance << currency << std::endl;
}
return 0;
}
void read_database(const std::string &fn)
{
std::ifstream file(fn);
if (!file.good())
{
return;
}
if (file.peek() == std::ifstream::traits_type::eof())
{
return;
}
std::string current_line;
unsigned int index = 0;
while (std::getline(file, current_line))
{
data.push_back(transaction());
data.back().date = std::stoi(current_line.c_str());
std::getline(file, current_line);
data.back().date_current = std::stoi(current_line.c_str());
std::getline(file, current_line);
data.back().name = current_line;
std::getline(file, current_line);
data.back().amount = std::atof(current_line.c_str());
std::getline(file, current_line);
data.back().reference_num = current_line;
std::getline(file, current_line);
data.back().description = current_line;
std::getline(file, current_line);
int timezone = set_timezone(current_line);
data.back().timezone = timezone;
std::getline(file, current_line);
data.back().credit_or_debit = std::stoi(current_line.c_str()) > 0;
data.back().index = index;
index++;
}
}
bool save_database(const std::string &fn)
{
std::ofstream file(fn);
if (!file.good())
{
std::cout << "\n\nERROR! The database can not be saved." << std::endl;
return true;
}
for (std::vector<transaction>::const_iterator it = data.begin(); it != data.end(); it++)
{
file << it->date << "\n";
file << it->date_current << "\n";
file << it->name << "\n";
file << it->amount << "\n";
file << it->reference_num << "\n";
file << it->description << "\n";
file << get_timezone(it->timezone) << "\n";
file << it->credit_or_debit << "\n";
}
return false;
}
int set_timezone(const std::string& tz)
{
std::string sn(1, tz.at(1));
if (tz.size() > 2)
{
std::string c(1, tz.at(2));
sn += c;
}
int n = std::stoi(sn.c_str());
if (n < -12) n = -12;
else if (n > 12) n = 12;
int timezone;
timezone = 3600 * n;
if (tz.at(0) == '-')
{
timezone *= -1;
}
return timezone;
}
std::string get_timezone(int timezone)
{
std::string return_str;
if (timezone < 0)
{
timezone *= -1;
return_str = "-";
}
else
{
return_str = "+";
}
int n = timezone / 3600;
return_str += std::to_string(n);
return return_str;
}
/* Copyright(C) 2020 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA. Version: 1.2 */ #include <iostream> #include <fstream> #include <vector> #include <map> #include <iomanip> #ifdef _WIN32 #include <windows.h> #pragma execution_character_set("utf-8") #endif //#define _DEBUG class SETT { const std::string settings_fp = "settings"; const std::string translation_dp = "translations/"; bool veccmp(const std::vector<std::string> &arr, const std::string &val) { for (std::vector<std::string>::const_iterator it = arr.begin(); it != arr.end(); it++) { if (it->compare(val) == 0) { return true; } } return false; } std::vector<std::string> cmd_arg = { "SV_NEW", "SV_DELETE" }; std::map<std::string, std::string> sett = { {"SV_DATABASE_PATH", "data"}, {"SV_LANGUAGE", "en"}, {"SV_TIMEZONE", "+0"}, {"SV_DATE_ORDER", "DMYhms"}, {"SV_CREDIT_COLOR", "1;32"}, {"SV_DEBIT_COLOR", "1;35"}, {"SV_ORDER_BY_DATE", "0"}, {"SV_DEBIT_FIRST", "0"}, {"SV_CREDIT_FIRST", "0"}, {"SV_ONLY_DEBIT", "0"}, {"SV_ONLY_CREDIT", "0"}, {"SV_NO_COLOR", "0"}, {"SV_NEW", "0"}, {"SV_DELETE", ""}, {"SV_START_DATE", ""}, {"SV_END_DATE", ""}, {"SV_FORMAT", "%d.%m.%Y %H:%M:%S"}, {"SV_ORDER", "cod-dat-nam-amo-ref-des-tzo"}, {"SV_CURRENCY", "$"}, {"SV_CURRENCY_SIDE", "1"}, {"T_CREDIT_OR_DEBIT", "Credit or Debit?(credit/debit): "}, {"T_CREDIT_OR_DEBIT_A", "credit"}, {"T_DAY", "Day:\t"}, {"T_MONTH", "Month:\t"}, {"T_YEAR", "Year:\t"}, {"T_HOURS", "Hours:\t"}, {"T_MINUTES", "Minutes:\t"}, {"T_SECONDS", "Seconds:\t"}, {"T_NAME", "Name:\t\t"}, {"T_AMOUNT", "Amount:\t\t"}, {"T_REF", "Ref:\t\t"}, {"T_DESCRIPTION", "Description:\t"}, {"T_TIMEZONE", "Timezone:\t"}, {"T_DATE", "Date:\t\t"}, {"T_BALANCE", "Balance: "}, {"T_CREDIT", "CREDIT"}, {"T_DEBIT", "DEBIT"} }; public: SETT(); std::string get(const std::string &key); bool getb(const std::string& key); bool add(std::string key, const std::string &val); }; SETT::SETT() { // Read settings file std::ifstream file(settings_fp); if (!file.good()) { return; } if (file.peek() == std::ifstream::traits_type::eof()) { return; } std::string current_line; while (std::getline(file, current_line)) { if (current_line.empty()) continue; std::string key; std::string val; int index = current_line.find("="); if (index != -1) { key = current_line.substr(0, index); val = current_line.substr(index + 1, current_line.size() - 1); } else { key = current_line; val = "1"; } for (unsigned int i = 0; i < key.size(); i++) { char c = key.at(i); // abc --> ABC if (c > 96 && c < 123) { c -= 32; key.at(i) = c; } } if (!veccmp(cmd_arg, key) && key.at(0) == 'S' && key.at(1) == 'V') { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { it->second = val; } } } // Read translations file std::string langauge_fn = get("SV_LANGUAGE"); if (!langauge_fn.empty()) { std::ifstream file0(translation_dp + langauge_fn); if (!file0.good()) { return; } if (file0.peek() == std::ifstream::traits_type::eof()) { return; } while (std::getline(file0, current_line)) { if (current_line.empty()) continue; std::string key; std::string val; int index = current_line.find("="); if (index != -1) { key = current_line.substr(0, index); val = current_line.substr(index + 1, current_line.size() - 1); } else { key = current_line; val = "1"; } for (unsigned int i = 0; i < key.size(); i++) { char c = key.at(i); // abc --> ABC if (c > 96 && c < 123) { c -= 32; key.at(i) = c; } } if (!veccmp(cmd_arg, key) && key.at(0) == 'T') { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { it->second = val; } } } } #ifdef _DEBUG for (std::map<std::string, std::string>::iterator it = sett.begin(); it != sett.end(); it++) { std::cout << it->first << "=" << it->second << std::endl; } #endif } std::string SETT::get(const std::string &key) { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { return it->second; } return std::string(""); } bool SETT::getb(const std::string& key) { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { return std::stoi(it->second) > 0; } return false; } bool SETT::add(std::string key, const std::string &val) { for (unsigned int i = 0; i < key.size(); i++) { char c = key.at(i); if (c == '-') { key.at(i) = '_'; continue; } // abc --> ABC if (c > 96 && c < 123) { c -= 32; key.at(i) = c; } } key = std::string("SV_") + key; std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { it->second = val; return false; } return true; } struct transaction { time_t date; time_t date_current; std::string reference_num; int timezone; bool credit_or_debit; double amount; std::string name; std::string description; unsigned int index; }; std::vector<transaction> data; // All data. std::vector<transaction> data0; // Data visible. void read_database(const std::string &fn); bool save_database(const std::string &fn); int set_timezone(const std::string &tz); std::string get_timezone(int timezone); time_t date_to_seconds(const std::string& date) { int yy, month, dd, hh, mm, ss; const char* datec = date.c_str(); sscanf(datec, "%d.%d.%d %d:%d:%d", &dd, &month, &yy, &hh, &mm, &ss); struct tm tm; tm.tm_year = yy - 1900; tm.tm_mon = month - 1; tm.tm_mday = dd; tm.tm_hour = hh; tm.tm_min = mm; tm.tm_sec = ss; tm.tm_isdst = -1; time_t t = mktime(&tm); return t; } time_t date_to_seconds(const std::string& date, std::string format) { char u[6] = {0}; int i = 0; for (std::string::iterator it = format.begin(); it != format.end(); it++) { if (*it == '%') { u[i] = *(it + 1); *(it + 1) = 'd'; i++; } } int y[6] = {0}; const char* datec = date.c_str(); sscanf(datec, format.c_str(), &y[0], &y[1], &y[2], &y[3], &y[4], &y[5]); int yy, month, dd, hh, mm, ss; for (i = 0; i < 6; i++) { if (u[i] == 'd') { dd = y[i]; continue; } if (u[i] == 'm') { month = y[i]; continue; } if (u[i] == 'Y') { yy = y[i]; continue; } if (u[i] == 'H') { hh = y[i]; continue; } if (u[i] == 'M') { mm = y[i]; continue; } if (u[i] == 'S') { ss = y[i]; } } struct tm tm; tm.tm_year = yy - 1900; tm.tm_mon = month - 1; tm.tm_mday = dd; tm.tm_hour = hh; tm.tm_min = mm; tm.tm_sec = ss; tm.tm_isdst = -1; time_t t = mktime(&tm); return t; } void get_word(std::string *val) { std::string usr_input; std::getline(std::cin, usr_input); if (usr_input.empty()) { *val = ""; return; } int find_pos = usr_input.find(" "); if (find_pos != -1) { usr_input = usr_input.substr(0, find_pos); } *val = usr_input; } int main(int argc, char *argv[]) { #ifdef _WIN32 SetConsoleOutputCP(65001); #endif std::string fp; std::vector<std::string> cmd_arg; SETT sett; if (argc > 1) { cmd_arg = std::vector<std::string>(argv, argv + argc); cmd_arg.erase(cmd_arg.begin()); cmd_arg.shrink_to_fit(); for (std::vector<std::string>::iterator it = cmd_arg.begin(); it != cmd_arg.end(); it++) { std::string lhs; std::string rhs; int index = it->find('='); if (index != -1) { lhs = it->substr(0, index); rhs = it->substr(index + 1, it->size()); } else { static bool prevent_double = false; if (!prevent_double) { prevent_double = true; std::vector<std::string>::iterator it0 = it; for (; it0 != cmd_arg.end(); it0++) { int index0 = it0->find('='); if (index0 != -1) return 0; } } lhs = *it; rhs = "1"; } sett.add(lhs, rhs); } } int timezone = set_timezone(sett.get("SV_TIMEZONE")); const std::string format = sett.get("SV_FORMAT"); const std::string order = sett.get("SV_ORDER"); fp = sett.get("SV_DATABASE_PATH"); read_database(fp); // Order by date if (!data.empty()) { for (unsigned int i = 0; i < data.size() - 1; i++) { if (data.at(i).date > data.at(i + 1).date) { for (unsigned int u = i; u < data.size() - 1; u++) { if (data.at(u).date < data.at(u + 1).date) break; std::vector<transaction>::iterator it = data.begin() + u; data.insert(data.begin() + u + 2, *it); data.erase(data.begin() + u); } if (i > 0) i -= 2; else i--; } } data0 = data; } if (argc > 1) { // SV_NEW - cmd arg if (sett.getb("SV_NEW")) { transaction new_data; for (unsigned int i = 0; i < 7; i++) { std::string usr_input; char current[4]; current[0] = order.at(4 * i); current[1] = order.at(4 * i + 1); current[2] = order.at(4 * i + 2); current[3] = '\0'; // credit or debit if (strcmp(current, "cod") == 0) { std::cout << sett.get("T_CREDIT_OR_DEBIT"); get_word(&usr_input); if (usr_input.empty()) return 0; if (strcmp(usr_input.c_str(), sett.get("T_CREDIT_OR_DEBIT_A").c_str()) == 0) { usr_input = "1"; } else { usr_input = "0"; } new_data.credit_or_debit = std::stoi(usr_input.c_str()) > 0; } // date if (strcmp(current, "dat") == 0) { std::string dmy = sett.get("SV_DATE_ORDER"); std::string dd, month, yy, mm, hh, ss; for (unsigned int i = 0; i < 6; i++) { if (dmy.at(i) == 'D') { std::cout << sett.get("T_DAY"); get_word(&dd); if (dd.empty()) return 0; continue; } if (dmy.at(i) == 'M') { std::cout << sett.get("T_MONTH"); get_word(&month); if (month.empty()) return 0; continue; } if (dmy.at(i) == 'Y') { std::cout << sett.get("T_YEAR"); get_word(&yy); if (yy.empty()) return 0; continue; } if (dmy.at(i) == 'h') { std::cout << sett.get("T_HOURS"); get_word(&hh); if (hh.empty()) hh = "0"; continue; } if (dmy.at(i) == 'm') { std::cout << sett.get("T_MINUTES"); get_word(&mm); if (mm.empty()) mm = "0"; continue; } if (dmy.at(i) == 's') { std::cout << sett.get("T_SECONDS"); get_word(&ss); if (ss.empty()) ss = "0"; continue; } } std::string date = dd; date += "."; date += month; date += "."; date += yy; date += " "; date += hh; date += ":"; date += mm; date += ":"; date += ss; new_data.date = date_to_seconds(date); } // name if (strcmp(current, "nam") == 0) { std::cout << sett.get("T_NAME"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.name = usr_input; } // amount if (strcmp(current, "amo") == 0) { std::cout << sett.get("T_AMOUNT"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.amount = std::atof(usr_input.c_str()); } // reference if (strcmp(current, "ref") == 0) { std::cout << sett.get("T_REF"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.reference_num = usr_input; } // description if (strcmp(current, "des") == 0) { std::cout << sett.get("T_DESCRIPTION"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.description = usr_input; } // timezone if (strcmp(current, "tzo") == 0) { std::cout << sett.get("T_TIMEZONE"); get_word(&usr_input); if (usr_input.empty()) { new_data.timezone = timezone; } else { new_data.timezone = set_timezone(usr_input); } new_data.date -= new_data.timezone; } std::cin.clear(); fflush(stdin); } new_data.date_current = std::time(nullptr); #ifdef _DEBUG std::cout << std::endl; std::cout << "----- NEW DATA -----" << std::endl; std::cout << "date: " << new_data.date << std::endl; std::cout << "date_current: " << new_data.date_current << std::endl; std::cout << "reference_num: " << new_data.reference_num << std::endl; std::cout << "timezone: " << new_data.timezone << std::endl; std::cout << "credit_or_debit: " << new_data.credit_or_debit << std::endl; std::cout << "amount: " << new_data.amount << std::endl; std::cout << "name: " << new_data.name << std::endl; std::cout << "description: " << new_data.description << std::endl; time_t tt = new_data.date_current + timezone; std::cout << "ct: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl; tt = new_data.date + new_data.timezone; std::cout << "dt: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl; return 0; #endif data.push_back(new_data); save_database(fp); return 0; } if (data0.empty()) { return 0; } // SV_START_DATE - cmd arg std::string start_date = sett.get("SV_START_DATE"); if (!start_date.empty()) { time_t date = date_to_seconds(start_date, format.c_str()); for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->date + it->timezone < date) { data0.erase(it); it--; } } } // SV_END_DATE - cmd arg std::string end_date = sett.get("SV_END_DATE"); if (!end_date.empty()) { time_t date = date_to_seconds(end_date, format.c_str()); for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->date + it->timezone > date) { data0.erase(it); it--; } } } // SV_ORDER_BY_DATE - cmd arg if (sett.getb("SV_ORDER_BY_DATE")) { for (unsigned int i = 0; i < data0.size() - 1; i++) { if (data0.at(i).date_current > data0.at(i + 1).date_current) { for (unsigned int u = i; u < data0.size() - 1; u++) { if (data0.at(u).date_current < data0.at(u + 1).date_current) break; std::vector<transaction>::iterator it = data0.begin() + u; data0.insert(data0.begin() + u + 2, *it); data0.erase(data0.begin() + u); } if (i > 0) i -= 2; else i--; } } } // SV_DEBIT_FIRST - cmd arg if (sett.getb("SV_DEBIT_FIRST")) { std::vector<transaction> temp; for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 0) { transaction tra = *it; data0.erase(it); temp.push_back(tra); it--; } } data0.insert(data0.end(), temp.begin(), temp.end()); } // SV_CREDIT_FIRST - cmd arg else if (sett.getb("SV_CREDIT_FIRST")) { std::vector<transaction> temp; for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 1) { transaction tra = *it; data0.erase(it); temp.push_back(tra); it--; } } data0.insert(data0.end(), temp.begin(), temp.end()); } // SV_ONLY_DEBIT - cmd arg else if (sett.getb("SV_ONLY_DEBIT")) { for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 1) { data0.erase(it); it--; } } } // SV_ONLY_CREDIT - cmd arg else if (sett.getb("SV_ONLY_CREDIT")) { for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 0) { data0.erase(it); it--; } } } // SV_DELETE - cmd arg std::string delete_cmd = sett.get("SV_DELETE"); if (!delete_cmd.empty()) { if (data0.empty()) { return 0; } for (std::string::iterator it = delete_cmd.begin(); it != delete_cmd.end(); it++) { if (*it > 57 || *it < 48) { return 0; } } unsigned int index = std::stoi(delete_cmd); if (index < 1 || index > data0.size()) { return 0; } transaction tra = data0.at(data0.size() - index); std::vector<transaction>::const_iterator it; it = data.begin() + tra.index; data.erase(it); save_database(fp); return 0; } } if (data0.empty()) { return 0; } const std::string currency = sett.get("SV_CURRENCY"); double balance = 0; bool currency_side = sett.getb("SV_CURRENCY_SIDE"); std::cout << std::fixed << std::setprecision(2); unsigned int index = 0; for (std::vector<transaction>::const_iterator it = data0.begin(); it != data0.end(); it++) { std::cout << "\n[ " << data0.size() - index++ << " ]" << std::endl; for (unsigned int i = 0; i < 7; i++) { char current[4]; current[0] = order.at(4 * i); current[1] = order.at(4 * i + 1); current[2] = order.at(4 * i + 2); current[3] = '\0'; // timezone if (strcmp(current, "tzo") == 0) { continue; } // credit or debit if (strcmp(current, "cod") == 0) { bool colored_text = sett.getb("SV_NO_COLOR"); if (it->credit_or_debit) { std::string text = sett.get("T_CREDIT"); if (colored_text) { std::cout << text << std::endl; } else { std::string color = "\033["; color += sett.get("SV_CREDIT_COLOR"); color += "m"; std::cout << color << text << "\033[0m" << std::endl; } } else { std::string text = sett.get("T_DEBIT"); if (colored_text) { std::cout << text << std::endl; } else { std::string color = "\033["; color += sett.get("SV_DEBIT_COLOR"); color += "m"; std::cout << color << text << "\033[0m" << std::endl; } } continue; } // date if (strcmp(current, "dat") == 0) { std::cout << sett.get("T_DATE"); time_t date = it->date + it->timezone; std::cout << std::put_time(gmtime(&date), format.c_str()) << std::endl; if (it->credit_or_debit) { balance += it->amount; } else { balance -= it->amount; } continue; } // name if (strcmp(current, "nam") == 0) { std::cout << sett.get("T_NAME") << it->name << std::endl; continue; } // amount if (strcmp(current, "amo") == 0) { std::cout << sett.get("T_AMOUNT"); if (currency_side) { std::cout << currency << it->amount << std::endl; } else { std::cout << it->amount << currency << std::endl; } continue; } // reference if (strcmp(current, "ref") == 0) { std::cout << sett.get("T_REF"); std::cout << it->reference_num << std::endl; continue; } // description if (strcmp(current, "des") == 0) { std::cout << sett.get("T_DESCRIPTION") << it->description << std::endl; } } } std::cout << "\n" << sett.get("T_BALANCE"); if (currency_side) { std::cout << currency << balance << std::endl; } else { std::cout << balance << currency << std::endl; } return 0; } void read_database(const std::string &fn) { std::ifstream file(fn); if (!file.good()) { return; } if (file.peek() == std::ifstream::traits_type::eof()) { return; } std::string current_line; unsigned int index = 0; while (std::getline(file, current_line)) { data.push_back(transaction()); data.back().date = std::stoi(current_line.c_str()); std::getline(file, current_line); data.back().date_current = std::stoi(current_line.c_str()); std::getline(file, current_line); data.back().name = current_line; std::getline(file, current_line); data.back().amount = std::atof(current_line.c_str()); std::getline(file, current_line); data.back().reference_num = current_line; std::getline(file, current_line); data.back().description = current_line; std::getline(file, current_line); int timezone = set_timezone(current_line); data.back().timezone = timezone; std::getline(file, current_line); data.back().credit_or_debit = std::stoi(current_line.c_str()) > 0; data.back().index = index; index++; } } bool save_database(const std::string &fn) { std::ofstream file(fn); if (!file.good()) { std::cout << "\n\nERROR! The database can not be saved." << std::endl; return true; } for (std::vector<transaction>::const_iterator it = data.begin(); it != data.end(); it++) { file << it->date << "\n"; file << it->date_current << "\n"; file << it->name << "\n"; file << it->amount << "\n"; file << it->reference_num << "\n"; file << it->description << "\n"; file << get_timezone(it->timezone) << "\n"; file << it->credit_or_debit << "\n"; } return false; } int set_timezone(const std::string& tz) { std::string sn(1, tz.at(1)); if (tz.size() > 2) { std::string c(1, tz.at(2)); sn += c; } int n = std::stoi(sn.c_str()); if (n < -12) n = -12; else if (n > 12) n = 12; int timezone; timezone = 3600 * n; if (tz.at(0) == '-') { timezone *= -1; } return timezone; } std::string get_timezone(int timezone) { std::string return_str; if (timezone < 0) { timezone *= -1; return_str = "-"; } else { return_str = "+"; } int n = timezone / 3600; return_str += std::to_string(n); return return_str; }
/*
	Copyright(C) 2020 Lari Varjonen <[email protected]>

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	version 2 as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110 - 1301, USA.

	Version: 1.2
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <iomanip>

#ifdef _WIN32
#include <windows.h>
#pragma execution_character_set("utf-8")
#endif

//#define _DEBUG



class SETT
{
	const std::string settings_fp = "settings";
	const std::string translation_dp = "translations/";

	bool veccmp(const std::vector<std::string> &arr, const std::string &val)
	{
		for (std::vector<std::string>::const_iterator it = arr.begin(); it != arr.end(); it++)
		{
			if (it->compare(val) == 0)
			{
				return true;
			}
		}
		return false;
	}

	std::vector<std::string> cmd_arg = {
		"SV_NEW",
		"SV_DELETE"
	};

	std::map<std::string, std::string> sett = {
		{"SV_DATABASE_PATH",    "data"},
		{"SV_LANGUAGE",         "en"},
		{"SV_TIMEZONE",         "+0"},
		{"SV_DATE_ORDER",       "DMYhms"},
		{"SV_CREDIT_COLOR",     "1;32"},
		{"SV_DEBIT_COLOR",      "1;35"},
		{"SV_ORDER_BY_DATE",    "0"},
		{"SV_DEBIT_FIRST",      "0"},
		{"SV_CREDIT_FIRST",     "0"},
		{"SV_ONLY_DEBIT",       "0"},
		{"SV_ONLY_CREDIT",      "0"},
		{"SV_NO_COLOR",         "0"},
		{"SV_NEW",              "0"},
		{"SV_DELETE",           ""},
		{"SV_START_DATE",       ""},
		{"SV_END_DATE",         ""},
		{"SV_FORMAT",           "%d.%m.%Y %H:%M:%S"},
		{"SV_ORDER",            "cod-dat-nam-amo-ref-des-tzo"},
		{"SV_CURRENCY",         "$"},
		{"SV_CURRENCY_SIDE",    "1"},

		{"T_CREDIT_OR_DEBIT",   "Credit or Debit?(credit/debit): "},
		{"T_CREDIT_OR_DEBIT_A", "credit"},
		{"T_DAY",               "Day:\t"},
		{"T_MONTH",             "Month:\t"},
		{"T_YEAR",              "Year:\t"},
		{"T_HOURS",             "Hours:\t"},
		{"T_MINUTES",           "Minutes:\t"},
		{"T_SECONDS",           "Seconds:\t"},
		{"T_NAME",              "Name:\t\t"},
		{"T_AMOUNT",            "Amount:\t\t"},
		{"T_REF",               "Ref:\t\t"},
		{"T_DESCRIPTION",       "Description:\t"},
		{"T_TIMEZONE",          "Timezone:\t"},
		{"T_DATE",              "Date:\t\t"},
		{"T_BALANCE",           "Balance: "},
		{"T_CREDIT",            "CREDIT"},
		{"T_DEBIT",             "DEBIT"}
	};

	public:
		SETT();
		std::string get(const std::string &key);
		bool getb(const std::string& key);
		bool add(std::string key, const std::string &val);
};

SETT::SETT()
{
	// Read settings file
	std::ifstream file(settings_fp);
	if (!file.good())
	{
		return;
	}
	if (file.peek() == std::ifstream::traits_type::eof())
	{
		return;
	}

	std::string current_line;
	while (std::getline(file, current_line))
	{
		if (current_line.empty()) continue;

		std::string key;
		std::string val;

		int index = current_line.find("=");
		if (index != -1)
		{
			key = current_line.substr(0, index);
			val = current_line.substr(index + 1, current_line.size() - 1);
		}
		else
		{
			key = current_line;
			val = "1";
		}

		for (unsigned int i = 0; i < key.size(); i++)
		{
			char c = key.at(i);
			// abc --> ABC
			if (c > 96 && c < 123)
			{
				c -= 32;
				key.at(i) = c;
			}
		}

		if (!veccmp(cmd_arg, key) && key.at(0) == 'S' && key.at(1) == 'V')
		{
			std::map<std::string, std::string>::iterator it = sett.find(key);
			if (it != sett.end())
			{
				it->second = val;
			}
		}
	}

	// Read translations file
	std::string langauge_fn = get("SV_LANGUAGE");
	if (!langauge_fn.empty())
	{
		std::ifstream file0(translation_dp + langauge_fn);
		if (!file0.good())
		{
			return;
		}
		if (file0.peek() == std::ifstream::traits_type::eof())
		{
			return;
		}

		while (std::getline(file0, current_line))
		{
			if (current_line.empty()) continue;

			std::string key;
			std::string val;

			int index = current_line.find("=");
			if (index != -1)
			{
				key = current_line.substr(0, index);
				val = current_line.substr(index + 1, current_line.size() - 1);
			}
			else
			{
				key = current_line;
				val = "1";
			}

			for (unsigned int i = 0; i < key.size(); i++)
			{
				char c = key.at(i);
				// abc --> ABC
				if (c > 96 && c < 123)
				{
					c -= 32;
					key.at(i) = c;
				}
			}

			if (!veccmp(cmd_arg, key) && key.at(0) == 'T')
			{
				std::map<std::string, std::string>::iterator it = sett.find(key);
				if (it != sett.end())
				{
					it->second = val;
				}
			}
		}
	}

#ifdef _DEBUG
	for (std::map<std::string, std::string>::iterator it = sett.begin(); it != sett.end(); it++)
	{
		std::cout << it->first << "=" << it->second << std::endl;
	}
#endif
}

std::string SETT::get(const std::string &key)
{
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		return it->second;
	}
	return std::string("");
}

bool SETT::getb(const std::string& key)
{
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		return std::stoi(it->second) > 0;
	}
	return false;
}

bool SETT::add(std::string key, const std::string &val)
{
	for (unsigned int i = 0; i < key.size(); i++)
	{
		char c = key.at(i);
		if (c == '-')
		{
			key.at(i) = '_';
			continue;
		}
		// abc --> ABC
		if (c > 96 && c < 123)
		{
			c -= 32;
			key.at(i) = c;
		}
	}
	key = std::string("SV_") + key;
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		it->second = val;
		return false;
	}
	return true;
}



struct transaction {
	time_t date;
	time_t date_current;
	std::string reference_num;
	int timezone;
	bool credit_or_debit;
	double amount;
	std::string name;
	std::string description;
	unsigned int index;
};
std::vector<transaction> data; // All data.
std::vector<transaction> data0; // Data visible.

void read_database(const std::string &fn);
bool save_database(const std::string &fn);
int set_timezone(const std::string &tz);
std::string get_timezone(int timezone);

time_t date_to_seconds(const std::string& date)
{
	int yy, month, dd, hh, mm, ss;
	const char* datec = date.c_str();
	sscanf(datec, "%d.%d.%d %d:%d:%d", &dd, &month, &yy, &hh, &mm, &ss);
	
	struct tm tm;
	tm.tm_year = yy - 1900;
	tm.tm_mon = month - 1;
	tm.tm_mday = dd;
	tm.tm_hour = hh;
	tm.tm_min = mm;
	tm.tm_sec = ss;
	tm.tm_isdst = -1;

	time_t t = mktime(&tm);
	return t;
}

time_t date_to_seconds(const std::string& date, std::string format)
{
	char u[6] = {0};
	int i = 0;
	for (std::string::iterator it = format.begin(); it != format.end(); it++)
	{
		if (*it == '%')
		{
			u[i] = *(it + 1);
			*(it + 1) = 'd';
			i++;
		}
	}

	int y[6] = {0};
	const char* datec = date.c_str();
	sscanf(datec, format.c_str(), &y[0], &y[1], &y[2], &y[3], &y[4], &y[5]);

	int yy, month, dd, hh, mm, ss;
	for (i = 0; i < 6; i++)
	{
		if (u[i] == 'd')
		{
			dd = y[i];
			continue;
		}
		if (u[i] == 'm')
		{
			month = y[i];
			continue;
		}
		if (u[i] == 'Y')
		{
			yy = y[i];
			continue;
		}
		if (u[i] == 'H')
		{
			hh = y[i];
			continue;
		}
		if (u[i] == 'M')
		{
			mm = y[i];
			continue;
		}
		if (u[i] == 'S')
		{
			ss = y[i];
		}
	}

	struct tm tm;
	tm.tm_year = yy - 1900;
	tm.tm_mon = month - 1;
	tm.tm_mday = dd;
	tm.tm_hour = hh;
	tm.tm_min = mm;
	tm.tm_sec = ss;
	tm.tm_isdst = -1;

	time_t t = mktime(&tm);
	return t;
}

void get_word(std::string *val)
{
	std::string usr_input;
	std::getline(std::cin, usr_input);
	if (usr_input.empty())
	{
		*val = "";
		return;
	}
	int find_pos = usr_input.find(" ");
	if (find_pos != -1)
	{
		usr_input = usr_input.substr(0, find_pos);
	}
	*val = usr_input;
}



int main(int argc, char *argv[])
{
#ifdef _WIN32
	SetConsoleOutputCP(65001);
#endif

	std::string fp;
	std::vector<std::string> cmd_arg;
	SETT sett;

	if (argc > 1)
	{
		cmd_arg = std::vector<std::string>(argv, argv + argc);
		cmd_arg.erase(cmd_arg.begin());
		cmd_arg.shrink_to_fit();

		for (std::vector<std::string>::iterator it = cmd_arg.begin(); it != cmd_arg.end(); it++)
		{
			std::string lhs;
			std::string rhs;
			int index = it->find('=');
			if (index != -1)
			{
				lhs = it->substr(0, index);
				rhs = it->substr(index + 1, it->size());
			}
			else
			{
				static bool prevent_double = false;
				if (!prevent_double)
				{
					prevent_double = true;
					std::vector<std::string>::iterator it0 = it;
					for (; it0 != cmd_arg.end(); it0++)
					{
						int index0 = it0->find('=');
						if (index0 != -1)
							return 0;
					}
				}
				lhs = *it;
				rhs = "1";
			}
			sett.add(lhs, rhs);
		}
	}

	int timezone = set_timezone(sett.get("SV_TIMEZONE"));
	const std::string format = sett.get("SV_FORMAT");
	const std::string order = sett.get("SV_ORDER");
	
	fp = sett.get("SV_DATABASE_PATH");
	read_database(fp);

	// Order by date
	if (!data.empty())
	{
		for (unsigned int i = 0; i < data.size() - 1; i++)
		{
			if (data.at(i).date > data.at(i + 1).date)
			{
				for (unsigned int u = i; u < data.size() - 1; u++)
				{
					if (data.at(u).date < data.at(u + 1).date) break;

					std::vector<transaction>::iterator it = data.begin() + u;
					data.insert(data.begin() + u + 2, *it);
					data.erase(data.begin() + u);
				}
				if (i > 0) i -= 2;
				else i--;
			}
		}
		data0 = data;
	}

	if (argc > 1)
	{
		// SV_NEW - cmd arg
		if (sett.getb("SV_NEW"))
		{
			transaction new_data;
			for (unsigned int i = 0; i < 7; i++)
			{
				std::string usr_input;

				char current[4];
				current[0] = order.at(4 * i);
				current[1] = order.at(4 * i + 1);
				current[2] = order.at(4 * i + 2);
				current[3] = '\0';

				// credit or debit
				if (strcmp(current, "cod") == 0)
				{
					std::cout << sett.get("T_CREDIT_OR_DEBIT");
					get_word(&usr_input);
					if (usr_input.empty())
						return 0;
					if (strcmp(usr_input.c_str(), sett.get("T_CREDIT_OR_DEBIT_A").c_str()) == 0)
					{
						usr_input = "1";
					}
					else
					{
						usr_input = "0";
					}
					new_data.credit_or_debit = std::stoi(usr_input.c_str()) > 0;
				}
				// date
				if (strcmp(current, "dat") == 0)
				{
					std::string dmy = sett.get("SV_DATE_ORDER");
					std::string dd, month, yy, mm, hh, ss;
					for (unsigned int i = 0; i < 6; i++)
					{
						if (dmy.at(i) == 'D')
						{
							std::cout << sett.get("T_DAY");
							get_word(&dd);
							if (dd.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'M')
						{
							std::cout << sett.get("T_MONTH");
							get_word(&month);
							if (month.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'Y')
						{
							std::cout << sett.get("T_YEAR");
							get_word(&yy);
							if (yy.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'h')
						{
							std::cout << sett.get("T_HOURS");
							get_word(&hh);
							if (hh.empty())
								hh = "0";
							continue;
						}
						if (dmy.at(i) == 'm')
						{
							std::cout << sett.get("T_MINUTES");
							get_word(&mm);
							if (mm.empty())
								mm = "0";
							continue;
						}
						if (dmy.at(i) == 's')
						{
							std::cout << sett.get("T_SECONDS");
							get_word(&ss);
							if (ss.empty())
								ss = "0";
							continue;
						}
					}
					std::string date = dd;
					date += ".";
					date += month;
					date += ".";
					date += yy;
					date += " ";
					date += hh;
					date += ":";
					date += mm;
					date += ":";
					date += ss;
					new_data.date = date_to_seconds(date);
				}
				// name
				if (strcmp(current, "nam") == 0)
				{
					std::cout << sett.get("T_NAME");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.name = usr_input;
				}
				// amount
				if (strcmp(current, "amo") == 0)
				{
					std::cout << sett.get("T_AMOUNT");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.amount = std::atof(usr_input.c_str());
				}
				// reference
				if (strcmp(current, "ref") == 0)
				{
					std::cout << sett.get("T_REF");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.reference_num = usr_input;
				}
				// description
				if (strcmp(current, "des") == 0)
				{
					std::cout << sett.get("T_DESCRIPTION");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.description = usr_input;
				}
				// timezone
				if (strcmp(current, "tzo") == 0)
				{
					std::cout << sett.get("T_TIMEZONE");
					get_word(&usr_input);
					if (usr_input.empty())
					{
						new_data.timezone = timezone;
					}
					else
					{
						new_data.timezone = set_timezone(usr_input);
					}
					new_data.date -= new_data.timezone;
				}
				std::cin.clear();
				fflush(stdin);
			}
			new_data.date_current = std::time(nullptr);
#ifdef _DEBUG
			std::cout << std::endl;
			std::cout << "----- NEW DATA -----" << std::endl;
			std::cout << "date:            " << new_data.date << std::endl;
			std::cout << "date_current:    " << new_data.date_current << std::endl;
			std::cout << "reference_num:   " << new_data.reference_num << std::endl;
			std::cout << "timezone:        " << new_data.timezone << std::endl;
			std::cout << "credit_or_debit: " << new_data.credit_or_debit << std::endl;
			std::cout << "amount:          " << new_data.amount << std::endl;
			std::cout << "name:            " << new_data.name << std::endl;
			std::cout << "description:     " << new_data.description << std::endl;
			time_t tt = new_data.date_current + timezone;
			std::cout << "ct: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
			tt = new_data.date + new_data.timezone;
			std::cout << "dt: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
			return 0;
#endif
			data.push_back(new_data);

			save_database(fp);
			return 0;
		}

		if (data0.empty())
		{
			return 0;
		}

		// SV_START_DATE - cmd arg
		std::string start_date = sett.get("SV_START_DATE");
		if (!start_date.empty())
		{
			time_t date = date_to_seconds(start_date, format.c_str());
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->date + it->timezone < date)
				{
					data0.erase(it);
					it--;
				}
			}
		}
		// SV_END_DATE - cmd arg
		std::string end_date = sett.get("SV_END_DATE");
		if (!end_date.empty())
		{
			time_t date = date_to_seconds(end_date, format.c_str());
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->date + it->timezone > date)
				{
					data0.erase(it);
					it--;
				}
			}
		}

		// SV_ORDER_BY_DATE - cmd arg
		if (sett.getb("SV_ORDER_BY_DATE"))
		{
			for (unsigned int i = 0; i < data0.size() - 1; i++)
			{
				if (data0.at(i).date_current > data0.at(i + 1).date_current)
				{
					for (unsigned int u = i; u < data0.size() - 1; u++)
					{
						if (data0.at(u).date_current < data0.at(u + 1).date_current) break;

						std::vector<transaction>::iterator it = data0.begin() + u;
						data0.insert(data0.begin() + u + 2, *it);
						data0.erase(data0.begin() + u);
					}
					if (i > 0) i -= 2;
					else i--;
				}
			}
		}

		// SV_DEBIT_FIRST - cmd arg
		if (sett.getb("SV_DEBIT_FIRST"))
		{
			std::vector<transaction> temp;
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 0)
				{
					transaction tra = *it;
					data0.erase(it);
					temp.push_back(tra);
					it--;
				}
			}
			data0.insert(data0.end(), temp.begin(), temp.end());
		}
		// SV_CREDIT_FIRST - cmd arg
		else if (sett.getb("SV_CREDIT_FIRST"))
		{
			std::vector<transaction> temp;
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 1)
				{
					transaction tra = *it;
					data0.erase(it);
					temp.push_back(tra);
					it--;
				}
			}
			data0.insert(data0.end(), temp.begin(), temp.end());
		}
		// SV_ONLY_DEBIT - cmd arg
		else if (sett.getb("SV_ONLY_DEBIT"))
		{
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 1)
				{
					data0.erase(it);
					it--;
				}
			}
		}
		// SV_ONLY_CREDIT - cmd arg
		else if (sett.getb("SV_ONLY_CREDIT"))
		{
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 0)
				{
					data0.erase(it);
					it--;
				}
			}
		}

		// SV_DELETE - cmd arg
		std::string delete_cmd = sett.get("SV_DELETE");
		if (!delete_cmd.empty())
		{
			if (data0.empty())
			{
				return 0;
			}
			for (std::string::iterator it = delete_cmd.begin(); it != delete_cmd.end(); it++)
			{
				if (*it > 57 || *it < 48)
				{
					return 0;
				}
			}
			unsigned int index = std::stoi(delete_cmd);
			if (index < 1 || index > data0.size())
			{
				return 0;
			}

			transaction tra = data0.at(data0.size() - index);

			std::vector<transaction>::const_iterator it;
			it = data.begin() + tra.index;
			data.erase(it);

			save_database(fp);
			return 0;
		}
	}

	if (data0.empty())
	{
		return 0;
	}

	const std::string currency = sett.get("SV_CURRENCY");
	double balance = 0;
	bool currency_side = sett.getb("SV_CURRENCY_SIDE");

	std::cout << std::fixed << std::setprecision(2);

	unsigned int index = 0;
	for (std::vector<transaction>::const_iterator it = data0.begin(); it != data0.end(); it++)
	{
		std::cout << "\n[ " << data0.size() - index++  << " ]" << std::endl;
		for (unsigned int i = 0; i < 7; i++)
		{
			char current[4];
			current[0] = order.at(4 * i);
			current[1] = order.at(4 * i + 1);
			current[2] = order.at(4 * i + 2);
			current[3] = '\0';

			// timezone
			if (strcmp(current, "tzo") == 0)
			{
				continue;
			}

			// credit or debit
			if (strcmp(current, "cod") == 0)
			{
				bool colored_text = sett.getb("SV_NO_COLOR");
				if (it->credit_or_debit)
				{
					std::string text = sett.get("T_CREDIT");
					if (colored_text)
					{
						std::cout << text << std::endl;
					}
					else
					{
						std::string color = "\033[";
						color += sett.get("SV_CREDIT_COLOR");
						color += "m";
						std::cout << color << text << "\033[0m" << std::endl;
					}
				}
				else
				{
					std::string text = sett.get("T_DEBIT");
					if (colored_text)
					{
						std::cout << text << std::endl;
					}
					else
					{
						std::string color = "\033[";
						color += sett.get("SV_DEBIT_COLOR");
						color += "m";
						std::cout << color << text << "\033[0m" << std::endl;
					}
				}
				continue;
			}
			// date
			if (strcmp(current, "dat") == 0)
			{
				std::cout << sett.get("T_DATE");
				time_t date = it->date + it->timezone;
				std::cout << std::put_time(gmtime(&date), format.c_str()) << std::endl;
				if (it->credit_or_debit)
				{
					balance += it->amount;
				}
				else
				{
					balance -= it->amount;
				}
				continue;
			}
			// name
			if (strcmp(current, "nam") == 0)
			{
				std::cout << sett.get("T_NAME") << it->name << std::endl;
				continue;
			}
			// amount
			if (strcmp(current, "amo") == 0)
			{
				std::cout << sett.get("T_AMOUNT");
				if (currency_side)
				{
					std::cout << currency << it->amount << std::endl;
				}
				else
				{
					std::cout << it->amount << currency << std::endl;
				}
				continue;
			}
			// reference
			if (strcmp(current, "ref") == 0)
			{
				std::cout << sett.get("T_REF");
				std::cout << it->reference_num << std::endl;
				continue;
			}
			// description
			if (strcmp(current, "des") == 0)
			{
				std::cout << sett.get("T_DESCRIPTION") << it->description << std::endl;
			}
		}
	}

	std::cout << "\n" << sett.get("T_BALANCE");
	if (currency_side)
	{
		std::cout << currency << balance << std::endl;
	}
	else
	{
		std::cout << balance << currency << std::endl;
	}

	return 0;
}

void read_database(const std::string &fn)
{
	std::ifstream file(fn);

	if (!file.good())
	{
		return;
	}
	if (file.peek() == std::ifstream::traits_type::eof())
	{
		return;
	}

	std::string current_line;
	unsigned int index = 0;
	while (std::getline(file, current_line))
	{
		data.push_back(transaction());
		data.back().date = std::stoi(current_line.c_str());

		std::getline(file, current_line);
		data.back().date_current = std::stoi(current_line.c_str());

		std::getline(file, current_line);
		data.back().name = current_line;

		std::getline(file, current_line);
		data.back().amount = std::atof(current_line.c_str());

		std::getline(file, current_line);
		data.back().reference_num = current_line;

		std::getline(file, current_line);
		data.back().description = current_line;
		
		std::getline(file, current_line);
		int timezone = set_timezone(current_line);
		data.back().timezone = timezone;

		std::getline(file, current_line);
		data.back().credit_or_debit = std::stoi(current_line.c_str()) > 0;

		data.back().index = index;
		index++;
	}
}

bool save_database(const std::string &fn)
{
	std::ofstream file(fn);
	if (!file.good())
	{
		std::cout << "\n\nERROR! The database can not be saved." << std::endl;
		return true;
	}
	for (std::vector<transaction>::const_iterator it = data.begin(); it != data.end(); it++)
	{
		file << it->date << "\n";
		file << it->date_current << "\n";
		file << it->name << "\n";
		file << it->amount << "\n";
		file << it->reference_num << "\n";
		file << it->description << "\n";
		file << get_timezone(it->timezone) << "\n";
		file << it->credit_or_debit << "\n";
	}
	return false;
}

int set_timezone(const std::string& tz)
{
	std::string sn(1, tz.at(1));
	if (tz.size() > 2)
	{
		std::string c(1, tz.at(2));
		sn += c;
	}
	
	int n = std::stoi(sn.c_str());
	if (n < -12) n = -12;
	else if (n > 12) n = 12;
	
	int timezone;
	timezone = 3600 * n;
	if (tz.at(0) == '-')
	{
		timezone *= -1;
	}
	return timezone;
}

std::string get_timezone(int timezone)
{
	std::string return_str;
	if (timezone < 0)
	{
		timezone *= -1;
		return_str = "-";
	}
	else
	{
		return_str = "+";
	}
	int n = timezone / 3600;
	return_str += std::to_string(n);
	return return_str;
}