<?php

error_reporting(-1);
class Report{
    private $col1 = 30;
    private $col2 = 15;

    private function padLeft($string, $length)
    {   
        $countSpace = $length - strlen($string); //количество добавляемых в ячейку пробелов  
        return str_repeat(' ',$countSpace) . $string;
    }

    private function padRight($string, $length)
    {
        $countSpace = $length - strlen($string); //количество добавляемых в ячейку пробелов  
        return $string . str_repeat(' ',$countSpace);
    }

    public function writeTable(Company $company){// Заголовок таблицы
        echo $this->padRight("Departmen", $this->col1) .
             $this->padLeft("employe", $this->col2) . 
             $this->padLeft("money", $this->col2) . 
             $this->padLeft("coffee", $this->col2) .
             $this->padLeft("lists", $this->col2) .
             $this->padLeft("money/lists", $this->col2) . "\n\n";

        $dataCompany = $company->getData();
        // Сама таблица
        foreach ($dataCompany as $dataDepartment) {
            echo $this->padRight($dataDepartment[0], $this->col1) .
                 $this->padLeft($dataDepartment[1], $this->col2) . 
                 $this->padLeft($dataDepartment[2], $this->col2) . 
                 $this->padLeft($dataDepartment[3], $this->col2) .
                 $this->padLeft($dataDepartment[4], $this->col2) .
                 $this->padLeft($dataDepartment[5], $this->col2) . "\n";
    }
}

}

class Company
{
    protected $departments = array();//массив депортаментов
    protected $professions = array();//массив профессий


    public function pushDepartmen(Department $department){


        $this->departments[] = $department;
    }

    public function pushProfession(Profession $profession)
    {
        $this->professions[] = $profession;
    }

    public function getProfessionList(){
        $professionList = array();
        foreach ($this->professions as $value) {
            $professionList[] = $value;     
        }

        return $professionList;
    }

    public function getData(){
        $companyData = array();
        foreach ($this->departments as  $department) {
            $departamentData = array();
            $departamentData[] = $department->getName();
            $departamentData[] = $department->getEmployeCount();
            $departamentData[] = $department->getSalary();
            $departamentData[] = $department->getCoffeeCount();
            $departamentData[] = $department->getListCount();
            $departamentData[] = $department->getMoneyPerList();
            $companyData[] = $departamentData;
        }
        return $companyData;
    }
}

class Department
{
    private $name;
    private $company;
    private $employees = array();//массив из работников этого департамента
    private $boss; //указывает на босса депортамента он один из работников (class Employe)

    //согласно массиву-шаблону создаём работников департаменты и про
    public function __construct(string $name, array $d, Company $company)
    {
        $this->name = $name;
        $employees = array();
        $this->company = $company;

        foreach ($d as $listEmploye){

            //если в массиве написано "b" значит назначит боссом
            
            if ($listEmploye[0] == 'b'){
                $this->boss = $this->createEmloye($listEmploye[1], $listEmploye[2], $this);

            //если написано число значит создаст группу работников
            }else {
                for ($i=0; $i < $listEmploye[0]; $i++) { 
                    $this->employees[] = $this->createEmloye($listEmploye[1], $listEmploye[2], $this);

                }
            }
                
        }
    }

    //приходит заявка на создаваемую профессию. Строчка и цифра например - ('en', 2), то функция делает engineera ранга 2
    public function createEmloye(string $professionName, int $rangEmploye, Department $department){

        $professionList = $this->company->getProfessionList();
        
        foreach ($professionList as $profession) {
            //создает работника необходимой профессии. 
            if ($profession->getName() == $professionName) {
                $employe = new Employe($rangEmploye, $profession, $department);
            }
        }
        return $employe;
    }

    public function getBoss(){
        return $this->boss;
    }

    public function getEmployes(){
        return $this->employees;
    }

    public function getName(){
        return $this->name;
    }
    
    public function getEmployeCount(){
        return count($this->employees);
    }
    
    public function getSalary(){
        $salary = 0;
        foreach ($this->employees as  $employe) {
            $salary = $salary + $employe->getSalary();
        }


        return $salary;
    }
    
    public function getCoffeeCount(){
        $coffee = 0;
        foreach ($this->employees as  $employe) {
            $coffee = $coffee + $employe->getCoffee();
        }
        return $coffee;
    }
    
    public function getListCount(){
        $listCount = 0;
        foreach ($this->employees as  $employe) {
            $listCount = $listCount + $employe->getListCount();
        }
        return $listCount;
    }
    
    public function getMoneyPerList(){
        return round( ($this->getSalary() / $this->getListCount()), 1 );
    }

}

class Employe{
    private $profession;
    private $employeLevel;
    private $department; //указывает на принадлежность департаменту (class Department)

    public function __construct(int $employeLevel, Profession $profession, Department $department)
    {
        $this->employeLevel = $employeLevel;
        $this->profession = $profession;
        $this->department = $department;
    }

    //определяет является ли сотрудник боссом
    public function isBoss(){ 
        $boss = $this->department->getBoss();
        if ($this === $boss) {
            return true;
        } else {
            return false;
        }
    }

    public function setDepartament(Department $department){
        $this->departament = $department;
    }

    //расчёт зарплаты сотрудника
    public function getSalary()
    {
        $rateMoneyProfession = $this->profession->getMoneyRate(); //узнаём коэфициент зарплаты для его професси
        $rateLevel = $this->employeLevel*0.25 + 0.75; //узнаём коэфициент зарплаты для его ранга
        if ($this->isBoss()){                  //является ли сотрудник боссом департамента
            $bossRate = 1.5;                 //и устанавливаем соответствующую ставку
        } else {
            $bossRate = 1;
        }
        return $rateMoneyProfession*$rateLevel*$bossRate;
    }

    //расчёт потребления кофе сотрудником
    public function getCoffee()
    {
        $cofeeRate = $this->profession->getCoffeeRate(); //узнаём коэфициент потребления кофе для его професси
        
        if ($this->isBoss()){                 //является ли сотрудник боссом департамента
            $bossRate = 2;                  //и устанавливаем соответствующее поглощение кофе для босса
        } else {
            $bossRate = 1;
        }
        return $cofeeRate * $bossRate;
    }

    public function getListCount(){
        $listRate = $this->profession->getListRate();
        if ($this->isBoss()){                     //является ли сотрудник боссом департамента
            $bossRate = 0;                      //босс не занимается бумагами
        } else {
            $bossRate = 1;                      //если простой работник то придётся заниматся чертежами отчётами и т.п.
        }
        return $bossRate * $listRate;
    }

}

abstract class Profession{
    private $name;
    private $moneyRate;
    private $coffeeRate;

    abstract protected function getListRate();
    
    abstract function getName();

    abstract function getCoffeeRate();

    abstract function getMoneyRate();
}

Class Manager Extends Profession{
    private $name = 'MN';
    private $reportListCount = 200; //Манагер делает 200 листов отчёта
    private $moneyRate = 500;
    private $coffeeRate = 20;

    public function getListRate()
    {
        return $this->reportListCount;
    }

    public function getName()
    {
        return $this->name;
    }
    public function getCoffeeRate()
    {
        return $this->coffeeRate;
    }
    public function getMoneyRate()
    {
        return $this->moneyRate;
    }

}

Class Marketer Extends Profession{
    private $name = 'MR';
    private $reportListCount = 150; //Маркетёр делает 150 листов отчёта
    private $moneyRate = 400;
    private $coffeeRate = 15;

    public function getListRate()
    {
        return $this->reportListCount;
    }

    public function getName()
    {
        return $this->name;
    }
    public function getCoffeeRate()
    {
        return $this->coffeeRate;
    }
    public function getMoneyRate()
    {
        return $this->moneyRate;
    }
}

Class Engineer Extends Profession{
    private $name = 'EN';
    private $projectListCount = 50; //Инжинер делает 50 листов чертежей и проектов
    private $moneyRate = 200;
    private $coffeeRate = 5;

    public function getListRate()
    {
        return $this->projectListCount;
    }

    public function getName()
    {
        return $this->name;
    }
    public function getCoffeeRate()
    {
        return $this->coffeeRate;
    }
    public function getMoneyRate()
    {
        return $this->moneyRate;
    }
}

Class Analyst Extends Profession{
    private $name = 'AN';
    private $researchListCount = 5; //Аналист делает 5 листов исследований
    private $moneyRate = 800;
    private $coffeeRate = 50;

    public function getListRate()
    {
        return $this->researchListCount;
    }

    public function getName()
    {
        return $this->name;
    }
    public function getCoffeeRate()
    {
        return $this->coffeeRate;
    }
    public function getMoneyRate()
    {
        return $this->moneyRate;
    }
}



// создаётсяя компания
$company = new Company;

//создаются список профессий востребованных в этой компании b
$manager = new Manager();   //  'MN'
$marketer = new Marketer(); //  'MR'
$engineer = new Engineer(); //  'EN'
$analyst = new Analyst();   //  'AN'

//все профессии заталкиваются в эту саму компанию
$company->pushProfession($manager);
$company->pushProfession($marketer);
$company->pushProfession($engineer);
$company->pushProfession($analyst);

/*массивы для формирования сотрудников отдело и т.п.
    первая цифра в ячейке массива - колличество сотрудников. буква b означает босса
    (mn-Manager mr-Marketer en-Engineer an-Analyst) - ранг сотрудника
    цифра в третьей ячеке - ранг сортудников
*/
$name='Department of Procurement';   
$d=[[ 9, 'MN', 1],   [3, 'MN', 2],   [2, 'MN', 3], [2, 'MR', 1], ['b', 'MN', 2]];
$department = new Department($name, $d, $company); 

$company->pushDepartmen($department);

$name='Department of Sales';    
$d=[[12, 'MR', 1],   [6, 'MR', 2],   [3, 'AN', 2], [2, 'AN', 2], ['b', 'MR', 2]]; 
$department = new Department($name, $d, $company); 
$company->pushDepartmen($department);

$name='Department of Advertising';   
$d=[[15, 'MR', 1],   [10, 'MR', 2],  [8, 'MN', 1], [2, 'EN', 1], ['b', 'MR', 3]];
$department = new Department($name, $d, $company);  
$company->pushDepartmen($department); 

$name='Department of Logistics'; 
$d=[[13, 'MN', 1],   [5, 'MN', 2],   [5, 'EN', 1], ['b', 'MN', 1]];
$department = new Department($name, $d, $company);
$company->pushDepartmen($department);

$report= new Report;
$report->writeTable($company);


