<file_sep>#ifndef DATE_H
#define DATE_H

class Date {
public:
    int year;
    int month;
    int day;

    Date();
    Date(int, int, int);

    bool isLeap();
    static Date getCurrentDate();
};

std::ostream& operator<<(std::ostream&, const Date&);
std::istream& operator>>(std::istream&, Date&);

bool operator== (Date const&, Date const&);
bool operator!= (Date const&, Date const&);
bool operator> (Date const&, Date const&);
bool operator< (Date const&, Date const&);
bool operator>= (Date const&, Date const&);
bool operator<= (Date const&, Date const&);

#endif
<file_sep>#ifndef COMMANDS_H
#define COMMANDS_H

#include <string>
#include "User.hpp"
#include "DatabaseHelper.hpp"

class BaseCommand {
private:
    std::string name;
public:
    BaseCommand(std::string);

    std::string getName() const;

    virtual void execute() = 0;
};

class HelpCommand : public BaseCommand {
public:
    HelpCommand();

    void execute();
};

class ExitCommand : public BaseCommand {
public:
    ExitCommand();

    void execute();
};

class UserCommand : public BaseCommand {
protected:
    User& user;
    DatabaseHelper& dbh;
public:
    UserCommand(std::string, User&, DatabaseHelper&);

    User& getUser() const;

    virtual void execute() = 0;
};

class RegisterCommand : public UserCommand {
public:
    RegisterCommand(User&, DatabaseHelper&);

    void execute();
};

class LoginCommand : public UserCommand {
public:
    LoginCommand(User&, DatabaseHelper&);

    void execute();
};

class TravelCommand : public UserCommand {
public:
    TravelCommand(User&, DatabaseHelper&);

    void execute();
};

class BrowseCommand : public UserCommand {
public:
    BrowseCommand(User&, DatabaseHelper&);

    void execute();
};

class FriendCommand : public UserCommand {
public:
    FriendCommand(User&, DatabaseHelper&);

    void execute();
};

class ListCommand : public UserCommand {
public:
    ListCommand(User&, DatabaseHelper&);

    void execute();
};

class ListGradesCommand : public UserCommand {
public:
    ListGradesCommand(User&, DatabaseHelper&);

    void execute();
};


#endif
<file_sep>#ifndef ITEM_H
#define ITEM_H

#include <string>
#include "Date.hpp"

enum Metric {kg, l};

class Item {
private:
    std::string name;
    Date expirationDate;
    Date admissionDate;
    std::string manufacturerName;
    Metric metric;
    int quantity;
    std::string comment;
public:
    Item();
    Item(std::string, Date, Date, std::string, Metric, int, std::string);
    Item(std::string, Date, Date, std::string, Metric, int);
    Item(std::string, Date, std::string, Metric, int, std::string);
    Item(std::string, Date, std::string, Metric, int);

    std::string getName() const;
    Date getExpirationDate() const;
    Date getAdmissionDate() const;
    std::string getManufacturerName() const;
    Metric getMetric() const;
    int getQuantity() const;
    std::string getComment() const;

    void setName(std::string);
    void setExpirationDate(Date);
    void setAdmissionDate(Date);
    void setManufacturerName(std::string);
    void setMetric(Metric);
    void setQuantity(int);
    void setComment(std::string);

    void print();
    int updateQuantity(int);
    
    friend std::ostream& operator<<(std::ostream&, const Item&);
    friend std::istream& operator>>(std::istream&, Item&);
};

std::ostream& operator<<(std::ostream&, const Metric&);
std::istream& operator>>(std::istream&, Metric&);

#endif
<file_sep>#include <iostream>
#include <vector>
#include <algorithm>

#include "Commands.hpp"
#include "Date.hpp"
#include "Warehouse.hpp"
#include <ctime>
int main() {
    Warehouse warehouse;

    std::vector<BaseCommand*> commands;
    commands.push_back(new HelpCommand());
    commands.push_back(new ExitCommand());
    commands.push_back(new OpenCommand(warehouse));
    commands.push_back(new CloseCommand(warehouse));
    commands.push_back(new SaveCommand(warehouse));
    commands.push_back(new SaveAsCommand(warehouse));   
    commands.push_back(new PrintCommand(warehouse));
    commands.push_back(new AddCommand(warehouse));
    commands.push_back(new RemoveCommand(warehouse));
    commands.push_back(new LogCommand(warehouse));
    commands.push_back(new CleanCommand(warehouse));

    std::string command;

    while (true) {
        try {
            std::cout << "Enter command: ";
            std::cin >> command;

            std::vector<BaseCommand*>::iterator pos = std::find_if(commands.begin(), commands.end(),
                    [&command](const BaseCommand* x) { return x->getName() == command; });
        
            if (pos != commands.end()) {
                (*pos)->execute();
            } else {
                std::cout << "Invalid command" << std::endl;
            }
        } catch (const char* err) {
            std::cout << err << std::endl;
        }
    }

    return 0;
}
// test 2000-05-05 2000-05-05 test kg 10 ee<file_sep>#include <map>
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
 #include <sstream>

#include "Warehouse.hpp"
#include "Item.hpp"
#include "Date.hpp"

Warehouse::Warehouse() : spaces(100), spaceCapacity(10) {};
Warehouse::Warehouse(int spaces, int capacity) : spaces(spaces), spaceCapacity(spaceCapacity) {};

std::map<int, Item> Warehouse::getItems() const {
    return this->items;
}

void Warehouse::setItems(std::map<int, Item> items) {
    this->items = items;
}

void Warehouse::addItem(Item item) {
    int freeSpace = 1;

    for (auto& [space, it] : this->items) {
        if (space == freeSpace) {
            freeSpace++;
        }

        if (item.getName() == it.getName() && item.getExpirationDate() == it.getExpirationDate()) {
            int capacityLeft = this->spaceCapacity - it.getQuantity();

            if (capacityLeft >= item.getQuantity()) {
                it.updateQuantity(item.getQuantity());
                this->actions.push_back(WarehouseAction("add", item, Date::getCurrentDate(), space));

                return;
            } else {
                it.updateQuantity(capacityLeft);

                int intendedQuan = item.updateQuantity(-capacityLeft);
                item.setQuantity(capacityLeft);
                this->actions.push_back(WarehouseAction("add", item, Date::getCurrentDate(), space));
                
                item.setQuantity(intendedQuan);
            }
        }
    }

    int wholeQuantity = item.getQuantity();
    do {
        int quantity = std::min(this->spaceCapacity, item.getQuantity());

        item.setQuantity(quantity);
        wholeQuantity -= quantity;

        this->items.insert(std::pair<int, Item>(freeSpace, item));
        this->actions.push_back(WarehouseAction("add", item, Date::getCurrentDate(), freeSpace));
    } while (wholeQuantity > 0);
}

void Warehouse::removeItem(std::string name, int quantity) {
    std::vector<std::pair<int, Item>> items;

    for (auto it = this->items.begin(); it != this->items.end(); ++it) {
        if (it->second.getName() == name) {
            items.push_back((*it));
        }
    }

    int leftInStock = 0;
    for (auto& [space, item] : items) {
        leftInStock += item.getQuantity();
    }

    if (leftInStock < quantity) {
        std::string answer;
        
        do {
            std::cout << "There are " << leftInStock << " " << name << " left in stock. Remove all of them? (y/n)";
            std::cin >> answer;
        } while (answer != "y" && answer != "n");

        if (answer == "n") return;

        quantity = leftInStock;
    }

    sort(items.begin(), items.end(), [](const std::pair<int, Item>& a, const std::pair<int, Item>& b) -> bool { 
        return a.second.getExpirationDate() > b.second.getExpirationDate(); 
    });

    for (auto& [space, item] : items) {
        int oldQuantity = item.getQuantity();
        this->items[space].updateQuantity( - std::min(quantity, this->items[space].getQuantity()));
        quantity -= (oldQuantity - this->items[space].getQuantity());

        Item copy = item;
        copy.setQuantity(oldQuantity - this->items[space].getQuantity());
        this->actions.push_back(WarehouseAction("remove", copy, Date::getCurrentDate(), space));

        if (this->items[space].getQuantity() == 0) {
            this->items.erase(space);
        }
        if (quantity == 0) {
            break;
        }
    }
}

void Warehouse::clean() {
    Date currentDate = Date::getCurrentDate();

    for (auto it = this->items.begin(); it != items.end(); it++) {
        if (it->second.getExpirationDate() < currentDate) {
            this->actions.push_back(WarehouseAction("remove", it->second, Date::getCurrentDate(), it->first));

            it = this->items.erase(it);
        }
    }
}

void Warehouse::print() const {
    for (auto const& [space, item] : this->items) {
        int totalQuantity = 0;

        for (auto const& [key, value] : this->items) {
            if (value.getName() == item.getName()) {
                totalQuantity += value.getQuantity();
            }
        }
        std::cout << space << " -> ";
        std::cout << item.getName() << " " << item.getManufacturerName() << " Quanitity: " << item.getQuantity() << " Total in warehouse: " << totalQuantity << item.getMetric();
        std::cout << " Admissioned at: " << item.getAdmissionDate() << " Expires at: " << item.getExpirationDate() << ". Comment: " << item.getComment() << std::endl; 
    }
}

void Warehouse::toFile(std::ofstream& ofs) const {
    ofs << this->spaces << " " << this->spaceCapacity << std::endl;
    
    for (auto const& [space, item] : this->items) {
        ofs << space << " " << item << std::endl;
    }

    ofs << "|\n";
    
    for (auto const& action : this->actions) {
        ofs << action;
    }
}

void Warehouse::fromFile(std::ifstream& ifs) {
    ifs >> this->spaces >> this->spaceCapacity;

    std::string space;
    Item item;
    std::cout << "ee";
    while (ifs >> space) {
        std::cout << "Read: " << space;
        std::cout << (space == "|") << std::endl;
        if (space == "|") break;
        ifs >> item;

        this->items.insert(std::pair<int, Item>(std::stoi(space), item));
    }

    // get new line
    char c;
    ifs >> c;

    WarehouseAction action;
    std::string line;
    while(getline(ifs, line)) {
        std::istringstream stream;
        stream.str(line);
        stream >> action;
        this->actions.push_back(action);
    }
}

void Warehouse::log(Date start, Date end) const {
    std::vector<WarehouseAction> filtered;

    std::copy_if(this->actions.begin(), this->actions.end(), std::back_inserter(filtered), [&start, &end](WarehouseAction action) {
        return (action.getDate() > start && action.getDate() < end);
    });

    std::sort(filtered.begin(), filtered.end(), [](const WarehouseAction& a, const WarehouseAction& b) -> bool { 
        return a.getDate() > b.getDate(); 
    });

    for (auto& a : filtered) {
        std::cout << a;
    }
}

WarehouseAction::WarehouseAction() : action("null"), item(), date(), space(0) {};
WarehouseAction::WarehouseAction(std::string action, Item item, Date date, int space) : action(action), item(item), date(date), space(space) {};

Date WarehouseAction::getDate() const {
    return this->date;
}

void WarehouseAction::print() const {
    std::cout << this->date << " Action: " << this->action << " Space: " << this->space << " Item: " << this->item << std::endl;
}

std::ostream& operator<<(std::ostream& os, const WarehouseAction& action) {
    os << action.date << " " << action.action << " " << action.space << " " << action.item << std::endl;

    return os;
}

std::istream& operator>>(std::istream& is, WarehouseAction& action) {
    is >> action.date >> action.action >> action.space >> action.item;

    return is;
}<file_sep>#include <iostream>
#include <fstream>
#include <string>
#include <set>
#include <map>

#include "Commands.hpp"
#include "User.hpp"
#include "Travel.hpp"

BaseCommand::BaseCommand(std::string name) : name(name) {};
UserCommand::UserCommand(std::string name, User& user, DatabaseHelper& dbh) : BaseCommand(name), user(user), dbh(dbh) {};

HelpCommand::HelpCommand() : BaseCommand("help") {};
ExitCommand::ExitCommand() : BaseCommand("exit") {};

RegisterCommand::RegisterCommand(User& user, DatabaseHelper& dbh) : UserCommand("register", user, dbh) {};
LoginCommand::LoginCommand(User& user, DatabaseHelper& dbh) : UserCommand("login", user, dbh) {};
TravelCommand::TravelCommand(User& user, DatabaseHelper& dbh) : UserCommand("travel", user, dbh) {};
FriendCommand::FriendCommand(User& user, DatabaseHelper& dbh) : UserCommand("friend", user, dbh) {};
BrowseCommand::BrowseCommand(User& user, DatabaseHelper& dbh) : UserCommand("browse", user, dbh) {};
ListCommand::ListCommand(User& user, DatabaseHelper& dbh) : UserCommand("list", user, dbh) {};
ListGradesCommand::ListGradesCommand(User& user, DatabaseHelper& dbh) : UserCommand("list_grades", user, dbh) {};

std::string BaseCommand::getName() const {
    return this->name;
}

User& UserCommand::getUser() const {
    return this->user;
}

void HelpCommand::execute() {
    std::cout << "The following commands are supported:" << std::endl;
    std::cout << "help         prints this information" << std::endl;
    std::cout << "exit         exists the program" << std::endl;
    std::cout << "register     register new user" << std::endl;
    std::cout << "login        login to existing user" << std::endl;
    std::cout << "travel       record one of your travels." << std::endl;
    std::cout << "browse       list all of your friends' travels" << std::endl;
    std::cout << "list         list all destinations in the database" << std::endl;
    std::cout << "list_grades  list all destinations with grades from users and average grade" << std::endl;
}

void ExitCommand::execute() {
    exit(0);
}

void RegisterCommand::execute() {
    if ( ! this->user.getLoggedIn()) {
        bool taken = false;
        std::string username, password, email;
        
        do {
            std::cout << "Enter username: ";
            std::cin >> username;

        } while(this->dbh.userExists(username));

        std::cout << "Enter password: ";
        std::cin >> password;
        std::cout << "Enter email: ";
        std::cin >> email;

        this->user = User(username, password, email);
        this->dbh.recordUser(this->user);

        std::cout << "Registered successfully!";
    } else {
        throw "Currently logged in";
    }
}

void LoginCommand::execute() {
    if ( ! this->user.getLoggedIn()) {
        std::string username, password;
        
        std::cout << "Enter username: ";
        std::cin >> username;
        std::cout << "Enter password: ";
        std::cin >> password;

        this->user = this->dbh.getUser(username);

        if (this->user.checkCredentials(username, password)) {
            this->user.setLoggedIn(true);
        } else {
            this->user = User();

            throw "Invalid username or password";
        }

        std::cout << "Loged in!";
    } else {
        throw "Already logged in";
    }
}

void TravelCommand::execute() {
    if (this->user.getLoggedIn()) {
        Travel travel;

        std::cin >> travel;

        this->dbh.recordTravel(this->user, travel);
    } else {
        throw "Not logged in";
    }
}

void BrowseCommand::execute() {
    for (auto& fr : this->user.getFriends()) {
        std::vector<Travel> travels = this->dbh.getTravels(fr);

        for (auto& travel : travels) {
            std::cout << fr << " traveled to ";
            travel.print();
        }
    }
}

void FriendCommand::execute() {
    if (this->user.getLoggedIn()) {
        std::string name;
        
        std::cout << "Enter friend name";
        std::cin >> name;

        this->user.addFriend(name);
        this->dbh.addFriend(this->user, name);
    } else {
        throw "Not logged in";
    }
}

void ListCommand::execute() {
    if (this->user.getLoggedIn()) {
        std::vector<User> users = this->dbh.getUsers();
        std::set<std::string> destinations;

        for (auto& user : users) {
            std::vector<Travel> travels = this->dbh.getTravels(user.getUsername());

            for (auto& travel : travels) {
                destinations.insert(travel.getDestination());
            }
        }

        std::cout << "List of all destinations: " << std::endl;
        for (auto& destination : destinations) {
            std::cout << "- " << destination << std::endl;
        }
    } else {
        throw "Not logged in";
    }
}

void ListGradesCommand::execute() {
    if (this->user.getLoggedIn()) {
        std::vector<User> users = this->dbh.getUsers();
        std::map<std::string, std::pair<int, float>> destinations;

        for (auto& user : users) {
            std::vector<Travel> travels = this->dbh.getTravels(user.getUsername());


            for (auto& travel : travels) {
                std::cout << user.getUsername() << " gave " << travel.getDestination() << " grade " << travel.getGrade() << std::endl;

                if (destinations.find(travel.getDestination()) == destinations.end()) {
                    destinations.insert(
                        std::pair<std::string, std::pair<int, float>>(
                            travel.getDestination(), std::pair<int, float>(1, travel.getGrade())
                        )
                    );
                } else {
                    destinations[travel.getDestination()].first++;
                    destinations[travel.getDestination()].second += travel.getGrade();
                }
            }
        }

        std::cout << "Average grades: " << std::endl;
        for (auto& [destination, grades] : destinations) {
            std::cout << "- " << destination <<  " AVG: " << (grades.second / grades.first) << std::endl;
        }
    } else {
        throw "Not logged in";
    }
}
<file_sep>#ifndef WAREHOUSE_H
#define WAREHOUSE_H

#include <map>
#include <vector>

#include "Item.hpp"

class WarehouseAction {
private:
    /* A big improvement would be not storing the whole item but just some id
        but it was too much work to redo. Besides the whole project would be better as an actual database in the first place */
    std::string action;
    Item item;
    Date date;
    int space;
public:
    WarehouseAction();
    WarehouseAction(std::string, Item, Date, int);

    Date getDate() const;

    void print() const;
    friend std::ostream& operator<<(std::ostream&, const WarehouseAction&);
    friend std::istream& operator>>(std::istream&, WarehouseAction&);
};

class Warehouse {
private:
    int spaces;
    int spaceCapacity;

    /* 
        Could be the same with vector but the map helps a bit with keeping it sorted
        and mainly it can later be upgrated easier to track products in a more complex way rather than just a number
    */
    std::map<int, Item> items;
    std::vector<WarehouseAction> actions;
public:
    Warehouse(int, int);
    Warehouse();

    std::map<int, Item> getItems() const;
    void setItems(std::map<int, Item>);
    void addItem(Item);
    void removeItem(std::string, int);
    void clean();

    void log(Date, Date) const;
    void print() const;
    void toFile(std::ofstream&) const;
    void fromFile(std::ifstream&);
};

#endif
<file_sep>#ifndef USER_H
#define USER_H

#include <string>
#include <vector>

class User {
private:
    /* Would be better to identify with id insead of the username
        but it's not a real database */
    std::string username;
    std::string passwordHash;
    std::string email;
    bool loggedIn = false;

    std::vector<std::string> friends;
public:
    User();
    User(std::string, std::string, std::string);

    std::string getUsername() const;
    std::string getPasswordHash() const;
    std::string getEmail() const;
    std::vector<std::string> getFriends() const;
    bool getLoggedIn() const;

    void setLoggedIn(bool);

    void addFriend(std::string);
    bool checkCredentials(std::string, std::string);

    friend std::ostream& operator<<(std::ostream&, const User&);
    friend std::istream& operator>>(std::istream&, User&);
};

#endif
<file_sep>#ifndef TRAVEL_H
#define TRAVEL_H

#include <string>
#include <vector>

#include "Date.hpp"

class Travel {
private:
    std::string destination;
    Date from, to;
    float grade;
    std::string comment;
    std::vector<std::string> photos;
public:
    Travel();
    Travel(std::string, Date, Date, float, std::string, std::vector<std::string>);

    std::string getDestination() const;
    Date getStartDate() const;
    Date getEndDate() const;
    float getGrade() const;
    std::string getComment() const;
    std::vector<std::string> getPhotos() const;

    void setDestination(std::string);
    void setStartDate(Date);
    void setEndDate(Date);
    void setGrade(float);
    void setComment(std::string);
    void setPhotos(std::vector<std::string>);

    void print() const;
    void addPhoto(std::string);

    friend std::ostream& operator<<(std::ostream&, const Travel&);
    friend std::istream& operator>>(std::istream&, Travel&);
};

#endif
<file_sep>#ifndef DATABASE_HELPER_H
#define DATABASE_HELPER_H

#include <string>
#include <fstream>

#include "User.hpp"
#include "Travel.hpp"

class DatabaseHelper {
private:
    std::fstream users;
protected:
    static std::string dbName;
public:
    DatabaseHelper();

    bool userExists(std::string);
    User getUser(std::string);
    void recordUser(const User&);
    void recordTravel(const User&, const Travel&);
    std::vector<Travel> getTravels(std::string);
    std::vector<User> getUsers(); 
    void addFriend(const User&, std::string);
}; 

#endif<file_sep>#include <string>
#include <vector>
#include <sstream>
#include <iostream>

#include "Travel.hpp"

Travel::Travel() {}
Travel::Travel(std::string destination, Date from, Date to, float grade, std::string comment, std::vector<std::string> photos)
    : destination(destination), from(from), to(to), grade(grade), comment(comment), photos(photos) {
    
    if (from > to) {
        throw "Inavlid dates. Start date should be smaller than end date";
    }

    for (auto& photo : photos) {
        std::string::size_type i;

        for (i = 0; i < photo.size(); i++) {
            if (photo[i] == '.') {
                break;
            } else if ((photo[i] < 'a' || photo[i] > 'z') && (photo[i] < 'A' || photo[i] > 'Z') && (photo[i] != '_')) {
                throw "Invalid photo name";
            }
        }

        std::string extension = photo.substr(i + 1);

        if (extension != "jpeg" && extension != "png") {
            throw "Invalid photo name";
        }
    }

    if (grade < 1 || grade > 5) {
        throw "Invalid grade";
    }
}


std::string Travel::getDestination() const {
    return this->destination;
}

Date Travel::getStartDate() const {
    return this->from;
}

Date Travel::getEndDate() const {
    return this->to;
}

float Travel::getGrade() const {
    return this->grade;
}

std::string Travel::getComment() const {
    return this->comment;
}

std::vector<std::string> Travel::getPhotos() const {
    return this->photos;
}


void Travel::setDestination(std::string destination) {
    this->destination = destination;
}

void Travel::setStartDate(Date from) {
    this->from = from;
}

void Travel::setEndDate(Date to) {
    this->to = to;
}

void Travel::setGrade(float grade) {
    this->grade = grade;
}

void Travel::setComment(std::string comment) {
    this->comment = comment;
}

void Travel::setPhotos(std::vector<std::string> photos) {
    this->photos = photos;
}

void Travel::addPhoto(std::string photo) {
    this->photos.push_back(photo);
}

void Travel::print() const {
    std::cout << this->destination << " from " << this->from << " to " << this->to << " Rated: "
        << this->grade << " \"" << this->comment << "\" Photos:";
    
    for (auto& photo : photos) {
        std::cout << " " << photo;
    }

    std::cout << std::endl;
}

std::ostream& operator<<(std::ostream& os, const Travel& travel) {
    os << travel.destination << "|" << travel.from << "|" << travel.to
        << "|" << travel.grade << "|" << travel.comment << "|";

    for (auto& photo : travel.photos) {
        os << " " << photo;
    }

    os << "|";

    return os;
}

std::istream& operator>>(std::istream& is, Travel& travel) {
    std::string token;
    std::istringstream stream;

    std::getline(is, travel.destination, '|');
    std::getline(is, token, '|');
    stream.str(token);
    stream >> travel.from;
    stream.clear();

    std::getline(is, token, '|');
    stream.str(token);
    stream >> travel.to;
    stream.clear();

    std::getline(is, token, '|');
    stream.str(token);
    stream >> travel.grade;
    stream.clear();
    
    std::getline(is, travel.comment, '|');

    std::getline(is, token, '|');
    stream.str(token);
    std::string photo;

    while(stream >> photo) {
        travel.addPhoto(photo);
    }

    return is;
}
<file_sep>#ifndef COMMANDS_H
#define COMMANDS_H

#include <string>
#include "Warehouse.hpp"

class BaseCommand {
private:
    std::string name;
public:
    BaseCommand(std::string);

    std::string getName() const;

    virtual void execute() = 0;
};

class HelpCommand : public BaseCommand {
public:
    HelpCommand();

    void execute();
};

class ExitCommand : public BaseCommand {
public:
    ExitCommand();

    void execute();
};

class FileCommand : public BaseCommand {
private:
    Warehouse& warehouse;
protected:
    static std::string filename;
public:
    FileCommand(std::string, Warehouse&);

    Warehouse& getWarehouse() const;

    virtual void execute() = 0;
};

class OpenCommand : public FileCommand {
public:
    OpenCommand(Warehouse&);

    void execute();
};

class CloseCommand : public FileCommand {
public:
    CloseCommand(Warehouse&);

    void execute();
};

class SaveCommand : public FileCommand {
public:
    SaveCommand(Warehouse&);

    void execute();
};


class SaveAsCommand : public FileCommand {
public:
    SaveAsCommand(Warehouse&);

    void execute();
};

class PrintCommand : public FileCommand {
public:
    PrintCommand(Warehouse&);

    void execute();
};

class AddCommand : public FileCommand {
public:
    AddCommand(Warehouse&);

    void execute();
};

class RemoveCommand : public FileCommand {
public:
    RemoveCommand(Warehouse&);

    void execute();
};

class LogCommand : public FileCommand {
public:
    LogCommand(Warehouse&);

    void execute();
};

class CleanCommand : public FileCommand {
public:
    CleanCommand(Warehouse&);

    void execute();
};

#endif
<file_sep>#include <iostream>
#include <sstream>

#include "Item.hpp"

Item::Item()
    : name(""), expirationDate(), admissionDate(), manufacturerName(""), metric(kg), quantity(0), comment("") {};

Item::Item(std::string name, Date expirationDate, Date admissionDate, std::string manufacturerName, Metric metric, int quantity, std::string comment)
    : name(name), expirationDate(expirationDate), admissionDate(admissionDate), manufacturerName(manufacturerName), metric(metric), quantity(quantity), comment(comment) {};

Item::Item(std::string name, Date expirationDate, Date admissionDate, std::string manufacturerName, Metric metric, int quantity)
    : Item(name, expirationDate, admissionDate, manufacturerName, metric, quantity, "No comment") {};

Item::Item(std::string name, Date expirationDate, std::string manufacturerName, Metric metric, int quantity, std::string comment)
    : Item(name, expirationDate, Date::getCurrentDate(), manufacturerName, metric, quantity, comment) {};

Item::Item(std::string name, Date expirationDate, std::string manufacturerName, Metric metric, int quantity)
    : Item(name, expirationDate, Date::getCurrentDate(), manufacturerName, metric, quantity, "No comment") {};


std::string Item::getName() const {
    return this->name;
}
Date Item::getExpirationDate() const {
    return this->expirationDate;
}
Date Item::getAdmissionDate() const {
    return this->admissionDate;
}
std::string Item::getManufacturerName() const {
    return this->manufacturerName;
}
Metric Item::getMetric() const {
    return this->metric;
}
int Item::getQuantity() const {
    return this->quantity;
}
std::string Item::getComment() const {
    return this->comment;
}

void Item::setName(std::string name) {
    this->name = name;
}
void Item::setExpirationDate(Date expirationDate) {
    this->expirationDate = expirationDate;
}
void Item::setAdmissionDate(Date admissionDate) {
    this->admissionDate = admissionDate;
}
void Item::setManufacturerName(std::string manufacturerName) {
    this->manufacturerName = manufacturerName;
}
void Item::setMetric(Metric metric) {
    this->metric = metric;
}
void Item::setQuantity(int quantity) {
    this->quantity = quantity;
}
void Item::setComment(std::string comment) {
    this->comment = comment;
}

std::ostream& operator<<(std::ostream& os, const Item& item) {
    os << item.name << " " << item.expirationDate << " " << item.admissionDate << " " << item.manufacturerName << " " 
        << item.metric << " " << item.quantity << " " << item.comment;

    return os;
}

std::istream& operator>>(std::istream& input, Item& item) { 
    input >> item.name >> item.expirationDate >> item.admissionDate >> item.manufacturerName 
        >> item.metric >> item.quantity >> item.comment;

    return input;            
}

std::istream& operator>>(std::istream& is, Metric& m) {
    std::string tmp;

    if (is >> tmp) {
        if (tmp == "kg") {
            m = kg;
        } else if (tmp == "l") {
            m = l;
        } else {
            throw "Invalid metric";
        }   
    }

    return is;
}

std::ostream& operator<<(std::ostream& os, const Metric& m) {
    std::string tmp;

    switch (m) {
        case kg:  tmp = "kg"; break; 
        case l: tmp = "l"; break; 
        default: tmp = "?";
    }

    return os << tmp;
} 

void Item::print() {
    std::cout << this->manufacturerName << " " << this->name << " " << this->quantity << this->metric;
    std::cout << " Admissioned at: " << this->admissionDate << " Expires at: " << this->expirationDate << ". Comment: " << this->comment; 
}

int Item::updateQuantity(int amount) {
    this->quantity += amount;

    return this->quantity;
}
<file_sep>#include <tuple>
#include <iostream>
#include <cassert>
#include <ctime>

#include "Date.hpp"

Date::Date() : year(1), month(1), day(1) {};
Date::Date(int year, int month, int day) : year(year), month(month), day(day) {
    if (year < 0) {
        throw "Invalid date";
    }
    if (month < 0 || month > 12) {
        throw "Invalid month";
    }

    int monthDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    if (this->isLeap()) {
        monthDays[1]++;
    }

    if (day < 0 || day > monthDays[month - 1]) {
        throw "Invalid day";
    }
}

bool Date::isLeap() {
    return ((this->year % 4 == 0) && (this->year % 100 != 0)) || (this->year % 400 == 0);
}

Date Date::getCurrentDate() {
    std::time_t t = std::time(0);
    std::tm* now = std::localtime(&t);

    return Date(now->tm_year + 1900, now->tm_mon + 1, now->tm_mday);
}

std::ostream& operator<<(std::ostream& os, const Date& date) {
    os << date.year << '-' << date.month << '-' << date.day;
    
    return os;
}

std::istream& operator>>(std::istream& input, Date& date) {
    std::string token;

    std::getline(input, token, '-');
    date.year = std::stoi(token);
    std::getline(input, token, '-');
    date.month = std::stoi(token);
    // std::getline(input, token, ' ');
    // date.day = std::stoi(token);
    input >> date.day;

    return input;            
}

bool operator== (Date const& lhs, Date const& rhs) {
    return lhs.year == rhs.year
        && lhs.month == rhs.month
        && lhs.day == rhs.day;
}

bool operator!= (Date const& lhs, Date const& rhs) {
    return ! (lhs == rhs);
}

bool operator> (Date const& lhs, Date const& rhs) {
    return std::tie(lhs.year, lhs.month, lhs.day)
        > std::tie(rhs.year, rhs.month, rhs.day);
}

bool operator< (Date const& lhs, Date const& rhs) {
    return ( ! (lhs > rhs) ) && lhs != rhs;
}

bool operator>= (Date const& lhs, Date const& rhs) {
    return lhs > rhs || lhs == rhs;
}

bool operator<= (Date const& lhs, Date const& rhs) {
    return lhs < rhs || rhs == rhs;
}
<file_sep>#include <string>
#include <sstream>
#include <iostream>

#include "User.hpp"
#include "Travel.hpp"

User::User() :loggedIn(false) {}
User::User(std::string username, std::string passwordHash, std::string email)
    : username(username), passwordHash(<PASSWORD>Hash), email(email) {}

std::string User::getUsername() const {
    return this->username;
}

std::string User::getPasswordHash() const {
    return this->passwordHash;
}

std::string User::getEmail() const {
    return this->email;
}

bool User::getLoggedIn() const {
    return this->loggedIn;
}

std::vector<std::string> User::getFriends() const {
    return this->friends;
}

void User::setLoggedIn(bool loggedIn) {
    this->loggedIn = loggedIn;
}

bool User::checkCredentials(std::string username, std::string passwordHash) {
    return this->username == username && this->passwordHash == passwordHash;
}

void User::addFriend(std::string fr) {
    this->friends.push_back(fr);
}

std::ostream& operator<<(std::ostream& os, const User& user) {
    os << user.username << " " << user.passwordHash << " " << user.email;

    for (auto& fr : user.friends) {
        os << " " << fr;
    }

    return os;
}

std::istream& operator>>(std::istream& is, User& user) {
    is >> user.username >> user.passwordHash >> user.email;

    std::string friends, fr;

    getline(is, friends);
    std::istringstream stream;
    stream.str(friends);

    while (stream >> fr) {

        user.addFriend(fr);
    }

    return is;
}<file_sep>#include <iostream>
#include <fstream>
#include <string>

#include "Commands.hpp"
#include "Warehouse.hpp"

std::string FileCommand::filename = "";

BaseCommand::BaseCommand(std::string name) : name(name) {};
FileCommand::FileCommand(std::string name, Warehouse& warehouse) : BaseCommand(name), warehouse(warehouse) {};

HelpCommand::HelpCommand() : BaseCommand("help") {};
ExitCommand::ExitCommand() : BaseCommand("exit") {};

OpenCommand::OpenCommand(Warehouse& warehouse) : FileCommand("open", warehouse) {};
CloseCommand::CloseCommand(Warehouse& warehouse) : FileCommand("close", warehouse) {};
SaveCommand::SaveCommand(Warehouse& warehouse) : FileCommand("save", warehouse) {};
SaveAsCommand::SaveAsCommand(Warehouse& warehouse) : FileCommand("saveas", warehouse) {};

PrintCommand::PrintCommand(Warehouse& warehouse) : FileCommand("print", warehouse) {};
AddCommand::AddCommand(Warehouse& warehouse) : FileCommand("add", warehouse) {};
RemoveCommand::RemoveCommand(Warehouse& warehouse) : FileCommand("remove", warehouse) {};
LogCommand::LogCommand(Warehouse& warehouse) : FileCommand("log", warehouse) {};
CleanCommand::CleanCommand(Warehouse& warehouse) : FileCommand("clean", warehouse) {};

std::string BaseCommand::getName() const {
    return this->name;
}

Warehouse& FileCommand::getWarehouse() const {
    return this->warehouse;
}

void HelpCommand::execute() {
    std::cout << "The following   commands are supported:" << std::endl;
    std::cout << "open <file>     opens <file>" << std::endl;
    std::cout << "close           closes currently opened file" << std::endl;
    std::cout << "save            saves the currently open file" << std::endl;
    std::cout << "saveas <file>   saves the currently open file in <file>" << std::endl;
    std::cout << "help            prints this information" << std::endl;
    std::cout << "exit            exists the program" << std::endl;
  
    std::cout << "print           prints warehouse information" << std::endl;
    std::cout << "add <Item>      adds item to warehouse. Checks for valid data" << std::endl;
    std::cout << "remove          removes item from warehouse by given anem and quantity" << std::endl;
    std::cout << "log <from> <to> prints all operation between dates <from> and <to>" << std::endl;
    std::cout << "clean           removes all items whose expiration dates have passed"  << std::endl;
}

void ExitCommand::execute() {
    exit(0);
}

void OpenCommand::execute() {
    if (FileCommand::filename == "") {
        std::cin >> FileCommand::filename;

        std::ifstream file(FileCommand::filename, std::ios::in);

        this->getWarehouse().fromFile(file);

        file.close();
    } else {
        throw "Currently there is an open file. Please close first";
    }
}

void CloseCommand::execute() {
    if (FileCommand::filename != "") {
        std::ofstream file(FileCommand::filename, std::ios::out);

        this->filename = "";

        file.close();
    } else {
        throw "No file to close. Open first";
    }
}

void SaveCommand::execute() {
    if (FileCommand::filename != "") {
        std::ofstream file(FileCommand::filename, std::ios::out);

        this->getWarehouse().toFile(file);

        file.close();
    } else {
        throw "No file opened. Open first";
    }
}

void SaveAsCommand::execute() {
    if (FileCommand::filename != "") {
        std::string filename;
        std::cout << "Enter filename: ";
        std::cin >> filename;

        std::ofstream file(filename, std::ios::out);

        this->getWarehouse().toFile(file);

        file.close();
    } else {
        throw "No file opened. Open first";
    }
}

void PrintCommand::execute() {
    this->getWarehouse().print();
}

void AddCommand::execute() {
    if (FileCommand::filename != "") {
        Item item;
        std::cin >> item;

        this->getWarehouse().addItem(item);
    } else {
        throw "No file opened. Open first";
    }
}

void RemoveCommand::execute() {
    if (FileCommand::filename != "") {
        std::string name;
        int quantity;

        std::cout << "Enter name: ";
        std::cin >> name;
        std::cout << "Enter quantity: ";
        std::cin >> quantity;

        this->getWarehouse().removeItem(name, quantity);;
    } else {
        throw "No file opened. Open first";
    }
}

void LogCommand::execute() {
    if (FileCommand::filename != "") {
        Date start, end;
        std::cout << "Enter start date (YYYY-MM-DD): ";
        std::cin >> start;
        std::cout << "Enter end date (YYYY-MM-DD): ";
        std::cin >> end;

        this->getWarehouse().log(start, end);
    } else {
        throw "No file opened. Open first";
    }
}

void CleanCommand::execute() {
    this->getWarehouse().clean();
}
<file_sep>#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>

#include "Date.hpp"
#include "Commands.hpp"
#include "User.hpp"

int main() {
    User user;
    DatabaseHelper dbh;
    std::string command;
    std::vector<BaseCommand*> commands;

    commands.push_back(new HelpCommand());
    commands.push_back(new ExitCommand());
    commands.push_back(new RegisterCommand(user, dbh));
    commands.push_back(new LoginCommand(user, dbh));
    commands.push_back(new TravelCommand(user, dbh));
    commands.push_back(new FriendCommand(user, dbh));
    commands.push_back(new BrowseCommand(user, dbh));
    commands.push_back(new ListCommand(user, dbh));
    commands.push_back(new ListGradesCommand(user, dbh));

    while (true) {
        try {
            std::cout << "Enter command: ";
            std::cin >> command;

            std::vector<BaseCommand*>::iterator pos = std::find_if(commands.begin(), commands.end(),
                    [&command](const BaseCommand* x) { return x->getName() == command; });
        
            if (pos != commands.end()) {
                (*pos)->execute();
            } else {
                std::cout << "Invalid command" << std::endl;
            }
        } catch (const char* err) {
            std::cout << err << std::endl;
        }
    }

    return 0;
}

//g++ main.cpp Commands.cpp Travel.cpp DatabaseHelper.cpp Date.cpp User.cpp
/*
Burgas, Bulgaria|2019-07-15|2019-07-29|5|A beautiful city on the Black Sea coast. I spent two unforgettable weeks there, meeting new people|burgas.jpeg locumfest.png sunrise_on_the_coast.jpeg|
*/
<file_sep>#include <fstream>
#include <sstream>
#include <iostream>

#include "DatabaseHelper.hpp"
#include "User.hpp"

std::string DatabaseHelper::dbName = "users.db";

DatabaseHelper::DatabaseHelper() {};

bool DatabaseHelper::userExists(std::string username) {
    try {
        this->getUser(username);

        return true;
    } catch (const char* err) {
        return false;
    }
}

User DatabaseHelper::getUser(std::string username) {
    std::ifstream users(DatabaseHelper::dbName, std::ios::in);
    
    User user;
    std::string line;

    while (getline(users, line)) {
        std::istringstream stream;
        stream.str(line);
        stream >> user;

        if (user.getUsername() == username) {
            users.close();

            return user;
        }
    }

    users.close();
    throw "User not found";
}

void DatabaseHelper::recordUser(const User& user) {
    std::ofstream users(DatabaseHelper::dbName, std::ios::out|std::ios::app);
    users << user << std::endl;
    users.close();
}

void DatabaseHelper::recordTravel(const User& user, const Travel& travel) {
    std::ofstream travels(user.getUsername() + ".db", std::ios::out|std::ios::app);
    travels << travel << std::endl;
    travels.close();
}

std::vector<Travel> DatabaseHelper::getTravels(std::string username) {
    std::ifstream travelsFile(username + ".db", std::ios::in);
    std::vector<Travel> travels;
    std::string line;
    Travel t;

    while(std::getline(travelsFile, line)) {
        std::istringstream stream;
        stream.str(line);
        stream >> t;

        travels.push_back(t);
    }

    travelsFile.close();

    return travels;
}

std::vector<User> DatabaseHelper::getUsers() {
    std::ifstream usersFile(DatabaseHelper::dbName, std::ios::in);
    std::vector<User> users;
    std::string line;
    User user;

    while(std::getline(usersFile, line)) {
        std::istringstream stream;
        stream.str(line);
        stream >> user;

        users.push_back(user);
    }

    usersFile.close();

    return users;
}

void DatabaseHelper::addFriend(const User& user, std::string fr) {
    std::ifstream users(DatabaseHelper::dbName, std::ios::in);
    std::vector<std::string> lines;
    std::string line;

    while(std::getline(users, line)) {
        lines.push_back(line);
    }
    users.close();

    std::ofstream updated(DatabaseHelper::dbName, std::ios::out);

    for (auto& l : lines) {
        std::string usr = l.substr(0, l.find(' '));

        if (usr == user.getUsername()) {
            l += (" " + fr);
        }
        
        updated << l << std::endl;
    }
}