#include<iostream>
#include<vector>
#include<unordered_map>
#include<string>

using namespace std;

/*
U have an organizational structure, which shows hierarchy of the organization. This hierarchy contains employees
E or managers M who has some Employees or Managers reporting to M.
Employee has ( id, name, JobDesc, salary etc).
Design the data structure you would be using to store this hierarchy

problem 1: Given an ID of an employee , print all the employee ID's who are directly reporting or indirectly
reporting to the manager.

problem 2: prefix search of employees by String. If employees have nishant and nikhil. If searched by "ni" we need to print all details of both nishant and nikhil.

problem 3(bonus): search should print all emloyee's and their details if a given string is subString of the name of
 an employee.(Like a phonebook contacts search)
*/


class Employee
{
private:
    int id;
    string name;
    string jobDesc;
    double sal;
    int managerId;
    vector<Employee*> reportedEmployees;
public:
    int getId()
    {
        return id;
    }

    void setId(int id)
    {
        this->id = id;
    }

    string getName()
    {
        return name;
    }

    void setName(string name)
    {
        this->name = name;
    }

    string getJobDesc()
    {
        return jobDesc;
    }

    void setJobDesc(string jobDesc)
    {
        this->jobDesc = jobDesc;
    }

    double getSal()
    {
        return sal;
    }

    void setSal(double sal)
    {
        this->sal = sal;
    }

    int getManagerId()
    {
        return managerId;
    }

    void setManagerId(int managerId)
    {
        this->managerId = managerId;
    }

    vector<Employee*>& getReportedEmployees()
    {
       // cout<< "In getReportedEmployees() empty - "<<reportedEmployees.empty()<<endl;
        return reportedEmployees;
    }

    void setReportedEmployees(vector<Employee*> *reportedEmployees)
    {
        this->reportedEmployees = *reportedEmployees;
    }
};

class OrganizationStructure
{
    public:

    unordered_map<int, Employee*> data ;

    void searchPrefixNames(string str)
    {
        for (unordered_map<int, Employee*>::iterator it=data.begin(); it!=data.end(); ++it)
        {
            if (it->second->getName().substr(0,str.size())== str)
            {
                cout<<it->second->getId();
                cout<<" "<< it->second->getName();
                cout<<" "<< it->second->getJobDesc();
                cout<<" "<< it->second->getSal()<<endl;
            }
        }
    }

    void searchSubNames(string str)
    {
        for (unordered_map<int, Employee*>::iterator it=data.begin(); it!=data.end(); ++it)
        {
            if (it->second->getName().find(str)!=string::npos)
            {
                cout<<it->second->getId();
                cout<<" "<< it->second->getName();
                cout<<" "<< it->second->getJobDesc();
                cout<<" "<< it->second->getSal()<<endl;
            }
        }
    }

    unordered_map<int, Employee*> storeData(Employee *emp, int parseInt)
    {
        data.insert ( std::pair<int,Employee*>(parseInt,emp) );

        Employee *empl;

        if(data.find(emp->getManagerId()) != data.end())
            empl = data.find(emp->getManagerId())->second; /*empl is manager of emp */

        if(empl->getId()!=emp->getId())
            empl->getReportedEmployees().push_back(emp); /* Add emp to reported list of empl  */

            //cout<<"After inserting reportee empty " << empl->getReportedEmployees().empty()<<endl;

        return data;
    }

    vector<int> getReportedEmployees(int id, unordered_map<int, Employee*> data)
    {
        vector<int> empListIds;
        Employee *emp;

        if(data.find(id)!=data.end())
        {
            emp = data.find(id)->second;
        }

        recursiveMethod(data, empListIds, emp->getReportedEmployees());

        return empListIds;
    }

    void recursiveMethod(unordered_map<int, Employee*> data, vector<int>& empListIds, vector<Employee*> listEmp)
    {
        std::vector<Employee*>::iterator itr = listEmp.begin();

        cout<<"listEmp size is - "<<listEmp.size()<<endl;
        for (itr=listEmp.begin();itr!=listEmp.end();itr++)
        {
            //  cout<<"in rec method"<<endl;
            empListIds.push_back((*itr)->getId());

            if(data.find((*itr)->getId())!=data.end())
            {
                //cout<<"data find"<<endl;
                Employee *emp;
                emp = data.find((*itr)->getId())->second;

                if(emp->getReportedEmployees().empty()==false)
                {
                 //   cout<<"recursive method called"<<endl;
                     recursiveMethod(data, empListIds, emp->getReportedEmployees());
                }
            }
        }
    }
};

int main()
{
    OrganizationStructure os;

    Employee *emp = new Employee;
    Employee *emp1 = new Employee;
    Employee *emp2 = new Employee;
    Employee *emp3 = new Employee;
    Employee *emp4 = new Employee;
    Employee *emp5 = new Employee;
    Employee *emp6 = new Employee;
    Employee *emp7 = new Employee;
    Employee *emp8 = new Employee;
    Employee *emp9 = new Employee;
    Employee *emp10 = new Employee;

    vector<Employee*> *vec = new vector<Employee*>;
    vector<Employee*> *vec1 = new vector<Employee*>;
    vector<Employee*> *vec2 = new vector<Employee*>;
    vector<Employee*> *vec3 = new vector<Employee*>;
    vector<Employee*> *vec4 = new vector<Employee*>;
    vector<Employee*> *vec5 = new vector<Employee*>;
    vector<Employee*> *vec6 = new vector<Employee*>;
    vector<Employee*> *vec7 = new vector<Employee*>;
    vector<Employee*> *vec8 = new vector<Employee*>;
    vector<Employee*> *vec9 = new vector<Employee*>;
    vector<Employee*> *vec10 = new vector<Employee*>;
    unordered_map<int, Employee*> data;

    emp10->setId(10);
    emp10->setName("Praveen");
    emp10->setJobDesc("SDE-2");
    emp10->setSal(99000.00);
    emp10->setReportedEmployees(vec10);
    data = os.storeData(emp10, 10);

    emp->setId(5);
    emp->setName("Neeraj");
    emp->setJobDesc("SDE-1");
    emp->setSal(85000.00);
    emp->setReportedEmployees(vec);
    emp->setManagerId(10);
    data = os.storeData(emp, 5);

    emp1->setId(12);
    emp1->setName("sachin");
    emp1->setJobDesc("SME");
    emp1->setSal(103000.00);
    emp1->setManagerId(5);
    emp1->setReportedEmployees(vec1);
    data = os.storeData(emp1, 12);

    emp2->setId(1);
    emp2->setName("Ajay");
    emp2->setJobDesc("SPM");
    emp2->setSal(196000.00);
    emp2->setManagerId(5);
    emp2->setReportedEmployees(vec2);
    data = os.storeData(emp2, 1);

    emp3->setId(2);
    emp3->setName("Anjali");
    emp3->setJobDesc("Manager");
    emp3->setSal(106000.00);
    emp3->setManagerId(5);
    emp3->setReportedEmployees(vec3);
    data = os.storeData(emp3, 2);

    emp4->setId(3);
    emp4->setName("garima");
    emp4->setJobDesc("CA");
    emp4->setSal(56010.00);
    emp4->setManagerId(5);
    emp4->setReportedEmployees(vec4);
    data = os.storeData(emp4, 3);

    emp5->setId(4);
    emp5->setName("Sanjeev");
    emp5->setJobDesc("IAS");
    emp5->setSal(76000.00);
    emp5->setManagerId(5);
    emp5->setReportedEmployees(vec5);
    data = os.storeData(emp5, 4);

    emp6->setId(6);
    emp6->setName("Rajiv");
    emp6->setJobDesc("SET");
    emp6->setSal(66100.00);
    emp6->setManagerId(4);
    emp6->setReportedEmployees(vec6);
    data = os.storeData(emp6, 6);

    emp7->setId(7);
    emp7->setName("Rajat");
    emp7->setJobDesc("SE");
    emp7->setSal(36000.00);
    emp7->setManagerId(4);
    emp7->setReportedEmployees(vec7);
    data = os.storeData(emp7, 7);

    emp8->setId(8);
    emp8->setName("Prajakata");
    emp8->setJobDesc("SDET-2");
    emp8->setSal(67000.00);
    emp8->setManagerId(4);
    emp8->setReportedEmployees(vec8);
    data = os.storeData(emp8, 8);

    emp9->setId(9);
    emp9->setName("Pranay");
    emp9->setJobDesc("SDET");
    emp9->setSal(36910.00);
    emp9->setManagerId(8);
    emp9->setReportedEmployees(vec9);
    data = os.storeData(emp9, 9);


    vector<int> empListIds =   os.getReportedEmployees(10, data);

    std::vector<int>::iterator itr;
    for (itr=empListIds.begin();itr!=empListIds.end();itr++)
        cout<<*itr<<" ";



    cout<<endl;
    os.searchPrefixNames("Pra");
    os.searchSubNames("eer");

    return 0;
}
