#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include <time.h>
#include <cstdlib>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <iomanip>
#include <limits.h>

using namespace std;


const int EMP_ID_WIDTH = 8;
const int EMP_NAME_WIDTH = 20;
const int EMP_GENDER_WIDTH = 8;
const int EMP_NUMERIC_WIDTH = 10;
const int EMP_JOB_TYPE_WIDTH = 10;
const int EMP_EXPERIENCE_WIDTH = 10;
const int TOTAL_WIDTH = 120;

class Employee;
class Requirements;

void printMenu();
void printSearchMenu();
void openInputFile(ifstream&, string);
void openOutputFile(ofstream&, string);
void doTask(int, vector<Employee>&);
int generateId(vector<Employee>);         // Function that generatres random id for each employee
bool contain(string, string);
void searchById(int, vector<Employee>);
void searchByName(vector<Employee>, string);
void searchByExperience(vector<Employee>, string);
void searchByRoughSalary(vector<Employee>, double);
void ReadFile(ifstream&, vector<Employee>);

void displayEmployees(vector<Employee>);
void addEmployee(vector<Employee>&);      //Push_back new employee
void searchEmployee(vector<Employee>);   //We can search by id, name, salary range and skills
void deleteEmployee(vector<Employee>&);   //We do it mannualy or computer does
void saveData(vector<Employee>);
void printEmployee(Employee emp);
void printEmployees(vector <Employee> v_emp);

class Experience {
public :
    vector<string> experiences;  //{"clerk", "scientist"}

    string operator[](int i) const {
        if(i >= experiences.size()) {
            return "";
        }
        return experiences[i];
    }

    unsigned size() const {
        return experiences.size();
    }
    bool contains(const string& exp) const {
        for(unsigned i = 0; i < experiences.size(); i++) {
            if(experiences[i] == exp) {
                return true;
            }
        }
        return false;
    }
    Experience push_back(string experience) {
        experiences.push_back(experience);
        return *this;
    }
    bool operator == (const Experience& exp) {
        return exp.experiences == experiences;
    }
};

//Employee* em = new Employee(1002, 26, "Dalibor", 10000)

class Employee {

private :
    int id;
    int age;
    char gender;   // gender = 'M' || gender = 'F'
    string name;
    double basic;
    Experience exp;
    //vector<string> skills;          // Maybe I am able to add

public :
    Employee() {
        id = generateId(vector<Employee>{*this});
    }
    Employee(int id, int age, char gender, string name, double basic) {
        this -> id = id;
        this -> age = age;
        this -> gender = gender;
        this -> name = name;
        this -> basic = basic;
        this -> exp = exp;
    }
    Experience getExp() const {
        return exp;
    }
    int getId() const {
        return id;
    }
    string getName() const{
        return name;
    }
    int getAge() const {
        return age;
    }
    double getBasic() const {
        return basic;
    }
    char getGender() {
        return gender;
    }
    bool qualifiedForAnyJob() const {
        return exp.size() > 0;
    }
    double calculateSalary() const {
        double salary = basic * (1 + exp.size() * 0.2);
        return salary;
    }
    Employee addExperience(string experience) {
        exp.push_back(experience);
        return *this;
    }
    Experience getExp() {
        return exp;
    }
    bool operator == (const Employee& emp) const {
        return emp.name == name && emp.age == age && emp.basic == basic;
    }
    string getJobType() const;
    friend ostream& operator << (ostream& sout, const Employee& emp);
    friend istream& operator >> (istream& sin, Employee& emp);
};

ostream& operator << (ostream& sout, const Employee& emp) {
    sout << emp.id << string(8, ' ') << emp.age << string(8, ' ') << emp.name << string(8, ' ') << endl;
    return sout;
}

bool contain(string subStr, string str) {

   int position = 0;
   int index;
   while((index = str.find(subStr, position)) != string::npos) {
      return true;
   }
   return false;
}

istream& operator >> (istream& sin, Employee& emp) {

    string exper;
    cout << string(15, ' ') <<  "Unesite ime novog zaposlenog: ";
    sin >> emp.name;
    cout << string(15, ' ') << "Unesite f ako je zensko ili m ako je musko: " ;
    sin >> emp.gender;
    cout << string(15, ' ') << "Unesite broj godina novog zaposlenog: ";
    sin >> emp.age;
    cout << string(15, ' ') << "Unesite iskustva koja ima zaposleni(ako nema iskustva samo"
    << endl << " pritisnite enter ili ako ima iskustva kao programer i naucnik unesite p i n): "
    << string(15, ' ');

    sin.clear();
    sin.ignore(INT_MAX, '\n');

    getline(cin, exper);

    string programmer = "p", scientist = "n", clerk = "s";
    //emp.addExperience();
    if(contain(programmer, exper)) {
        emp.addExperience(programmer);
    }
    if(contain(scientist, exper)) {
        emp.addExperience(scientist);
    }
    if(contain(clerk, exper)) {
        emp.addExperience(clerk);
    }
    emp.getJobType();

    return sin;
}


string Employee :: getJobType() const {


    if(exp.size() == 1) {
        return exp[0];
    }
    if(exp.size() == 3) {
        return "menadzer";
    }
    if(exp.contains("p") && exp.contains("n")) {
        return "naucni programer";
    }
    if(exp.contains("p") && exp.contains("s")) {
        return "kompjuterski menadzer";
    }
    if(exp.contains("s") && exp.contains("n")) {
        char deside;
        cout << "Unesite s ako zelite da zaposleni bude sluzbenik ili n ako zelite da bude naucnik: ";
        cin >> deside;
        if(deside == 's' || deside == 'S') {
            return "sluzbenik";
        }
        return "naucnik";
    }
}


// There are requirements to be satisfied in order to be in a company
// If employee does not have any of these requriements he can't
// qualify for any job

// Company has scientits, pure programers and clerks
// Only one of those are needed to qualify for a job

void printSearchMenu() {
    cout << string(15, ' ') << "1. Trazite zaposlenog po id" << endl;
    cout << string(15, ' ') << "2. Trazite zaposlenog po imenu" << endl;
    cout << string(15, ' ') << "3. Trazite zaposlenog po njegovim iskustvima" << endl;
    cout << string(15, ' ') << "4. Pretrazite po plati(priblizna cifra)" << endl;
    cout << string(15, ' ') << "5. Izadjite iz menija za pretragu" << endl;
}

void printMenu() {

    cout << string(15, ' ') << "1. Dodajte zaposlenog " << endl;
    cout << string(15, ' ') << "2. Prikazete podatke o zaposlenima " << endl;
    cout << string(15, ' ') << "3. Potrazite zaposlenog " << endl;
    cout << string(15, ' ') << "4. Obrisite zaposlenog " << endl;
    cout << string(15, ' ') << "5. Spasite " << endl;
    cout << string(15, ' ') << "6. Izadjete iz menija " << endl;
}

void ReadFile(ifstream& fin, vector<Employee> employees) {

    openInputFile(fin, "employees.txt");

    while(!fin.eof()) {

        string line;
        getline(fin, line);

        int id;
        string name;
        int age;
        double basic;
        char gender;
        //Experiences
        string programmer;
        string scientist;
        string clerk;

        istringstream iss(line);

        string strId;
        string strName;
        string strBasic;
        string strGender;

        getline(iss, strId, ',');
        getline(iss, strName, ',');
        getline(iss, strBasic, ',');
        getline(iss, strGender, ',');

        getline(iss, programmer, ',');
        getline(iss, scientist, ',');
        getline(iss, clerk);

        id = atoi(strId.c_str());
        name = strName;
        basic = atof(strBasic.c_str());
        gender = strGender[0];

        Experience exp;
        if(programmer.size() > 0) {
            exp.push_back(programmer);
        }
        if(scientist.size() > 0) {
            exp.push_back(programmer);
        }
        if(clerk.size() > 0) {
            exp.push_back(clerk);
        }
        string experience = "";
        for(int i = 0; i < exp.size(); i++) {
            experience += exp[i];
        }
        Employee emp(id, age, gender, name, basic);
        emp.addExperience(experience);


        employees.push_back(emp);
    }

    fin.close();
}

void printEmployees(vector<Employee> emp) {
    cout << setw(EMP_ID_WIDTH) << left << "EmpId"
         << setw(EMP_NAME_WIDTH) << left << "Name"
         << setw(EMP_GENDER_WIDTH);
}

void doTask(int option, vector<Employee>& employees) {

    ifstream fin;

    ReadFile(fin, employees);

    switch(option) {
        case 1 :
            addEmployee(employees);
            break;
        /*
        case 2 :
            displayEmployees(employees);
            break;
        */
        case 3 :
            searchEmployee(employees);
            break;
        case 4 :
            deleteEmployee(employees);
            break;
        case 5 :
            saveData(employees);
            break;
        case 6 :
            exit(1);
            break;
        default :
            cout << "Invalid Argument!" << endl;
            break;
    }
}

void saveData(vector<Employee> emp) {

    ofstream fout;
    openOutputFile(fout, "employees.txt");

    for(vector<Employee> :: iterator it = emp.begin(); it != emp.end(); it++) {
        fout << it -> getId() << ", " << it -> getName() << ", " << it -> getAge()
             << ", " << it -> getBasic() << ", " << it -> getJobType() << ", " << endl;
    }
    fout.close();
}

void addEmployee(vector<Employee>& emp) {

    bool isDuplicate = true;

    Employee newEmployee;
    cin >> newEmployee;

    while(isDuplicate) {

        isDuplicate = false;

        for(vector<Employee> :: iterator it = emp.begin(); it != emp.end(); it++) {
            if(*it == newEmployee) {
                isDuplicate = true;
            }
        }
        if(isDuplicate == true) {
            int deside;
            cout << "Unesite 0 ako zelite da izadjete ili 1 ako zelite da unesete opet: ";
            cin >> deside;
            if(deside == 0) {
                return;
            }
            cin >> newEmployee;
        }
    }
    if(newEmployee.qualifiedForAnyJob() == false) {
        if(newEmployee.getGender() == 'f') {
            cout << "Zaposleni nema radnog iskustva i nema mesta za nju u kompaniji." << endl;
        }
        else {
            cout << "Zaposleni nema radnog iskustva i nema mesta za njega u kompaniji." << endl;
        }
        return;
    }
    emp.push_back(newEmployee);
    cout << "Novi zaposleni ima titulu " << newEmployee.getJobType() << "." << endl;
    cout << "Ukupan broj zaposlenih posle dodavanja novog zaposlenog je " << emp.size() << "." << endl;
}

void searchById(int id, vector<Employee> emp) {

    bool found = false;
    for(int i = 0; i < emp.size(); i++) {
        if(emp[i].getId() == id) {
            found = true;
            cout << emp[i];
        }
    }
    if(!found) {
        cout << "Nije nadjen zaposleni sa id " << id << "." << endl;
    }
}

void searchByName(vector<Employee> emp, string name) {

    bool found = false;
    for(int i = 0; i < emp.size(); i++) {
        if(emp[i].getName() == name) {
            cout << emp[i];
            found = true;
        }
    }
    if(!found) {
        cout << "Nije nadjen zaposleni sa imenom " << name << endl;
    }
}

void searchByExperience(vector<Employee> emp, string experiences) {

    bool found;
    for(int i = 0; i < emp.size(); i++) {
        found = false;
        for(int j = 0; j < emp[i].getExp().size(); j++) {
            bool found;
            if(contain(emp[i].getExp()[j], experiences)) {
                found = true;
            }
        }
        if(found == true) {
            cout << emp[i];
        }
    }
    if(!found) {
        cout << "Nismo nasli zaposlenog!" << endl;
    }
}

void searchByRoughSalary(vector<Employee> emp, double salary) {

    bool found = false;
    for(int i = 0; i < emp.size(); i++) {
        if(emp[i].calculateSalary() * 0.9 <= salary <= emp[i].calculateSalary() * 1.1) {
            cout << emp[i];
            found = true;
        }
    }
    if(!found) {
        cout << "Nismo nasli zaposlenog sa platom " << salary << "." << endl;
    }
}

void searchEmployee(vector<Employee> emp) {

    int option;

    while(option != 5) {

        printSearchMenu();
        cin >> option;

        switch(option) {
            case 1 : {
                int id;
                cout << "Unesite id: " << endl;
                cin >> id;
                searchById(id, emp);
                break;
            }
            case 2 : {
                    string name;
                    cout << "Unesite ime: " << endl;
                    cin.clear();
                    cin.ignore(INT_MAX, '\n');
                    getline(cin, name);
                    searchByName(emp, name);
                    break;
            }
            case 3 : {
                    cout << "Unesite iskustva: " << endl;
                    cout << "Primer unosa(p n s) ili (p): " << endl;

                    string experiences;
                    cin.clear();
                    cin.ignore(INT_MAX, '\n');
                    getline(cin, experiences);

                    searchByExperience(emp, experiences);

                    break;
            }
            case 4 :
                {
                    cout << "Unesite pribliznu platu: " << endl;
                    double roughSalary;
                    cin >> roughSalary;
                    searchByRoughSalary(emp, roughSalary);
                    break;
                }
            default :
                break;
        }
    }
}

void openInputFile(ifstream& fin, string fileName) {
    fin.open(fileName, fstream :: app);
    if(fin.fail()) {
        cout << "Fajl se neuspesno otvorio!" << endl;
        return;
    }
}

void openOutputFile(ofstream& fout, string fileName) {

    fout.open(fileName, fstream :: app);
    if(fout.fail()) {
        cout << "Fajl se neuspesno otvorio!" << endl;
        return;
    }
}

void deleteEmployee(vector<Employee>& emp) {

    int id;
    cout << "Unesite od zaposlenog kojeg bi voleli da izbrisete: " << endl;
    cin >> id;
    int i = 0;
    for(i = 0; i < emp.size(); i++) {
        if(emp[i].getId() == id) {
            break;
        }
    }
    emp.erase(emp.begin() + i);
    saveData(emp);
}

int generateId(vector<Employee> emp) {

    int uniqueId;
    bool isUnique = false;
    while(!isUnique) {

        isUnique = true;
        uniqueId = rand() % 10000;
        for(auto it = emp.begin(); it != emp.end(); it++) {
            if(it -> getId() == uniqueId) {
                isUnique = false;
            }
        }
    }

    return uniqueId;
}

int main() {

    vector <Employee> employees;

    int option;
    bool quit = false;

    srand(time(0));

    while(!quit) {

        printMenu();
        cout << string(15, ' ');
        cin >> option;
        if(option == 6) {
            quit = true;
        }
        else {
            doTask(option, employees);
        }
    }

    return 0;
}
