<?php
 
class Company
{
    private $departments = [];
 
    public function addDepartment(Department $department) : self
    {
        $this->departments[] = $department;
 
        return $this;
    }
 
    public function getCountEmployee() : int
    {
        $data = 0;
        foreach ($this->departments as $department) {
            $data += $department->getCountEmployee();
        }
        return $data;
    }
 
    public function getExpenses
() : array     {
        $rate = 0;
        $coffee = 0;
 
        foreach ($this->departments as $department) {
            $rate   += $department->getExpenses()['Зарплата'];
            $coffee += $department->getExpenses()['Кофе'];
        }
 
        return [$rate, $coffee];
    }
 
    public function getAverageExpenses
() : array     {
        $rate = 0;
        $coffee = 0;
 
        foreach ($this->departments as $department) {
            $rate   += $department->getExpenses()['Зарплата'];
            $coffee += $department->getExpenses()['Кофе'];
        }
 
        return [
            $rate / count($this->getDepartments()),             $coffee / count($this->getDepartments())         ];
    }
 
    public function getReports() : float
    {
        $reports = 0;
        foreach ($this->departments as $department) {
            $reports += $department->getReports();
        }
        return $reports;
    }
 
    public function getAverageReports() : float
    {
        $reports = 0;
        foreach ($this->departments as $department) {
            $reports += $department->getReports();
        }
        return $reports / count($this->getDepartments());     }
 
    public function getAverageConsumptionMoneyPerPage() : float
    {
        $moneyConsumption = 0;
        foreach ($this->departments as $department) {
            $moneyConsumption += $department->getAverageConsumptionMoneyPerPage();
        }
        return $moneyConsumption / count($this->getDepartments());     }
 
    public function getConsumptionMoneyPerPage() : float
    {
        $moneyConsumption = 0;
        foreach ($this->departments as $department) {
            $moneyConsumption += $department->getAverageConsumptionMoneyPerPage();
        }
        return $moneyConsumption;
    }
 
    public function getDepartments
() : array     {
        return $this->departments;
    }
 
    public function getAverageCountEmployers() : float
    {
        $countEmployers  = 0;
        foreach ($this->departments as $department) {
            $countEmployers += $department->getCountEmployee();
        }
        return $countEmployers / count($this->getDepartments());     }
 
    public function setAntiCrisisMeasuresFirst() : self
    {
        foreach ($this->departments as $department) {
            $department->setAntiCrisisMeasuresFirst();
        }
 
        return $this;
    }
 
    public function setAntiCrisisMeasuresSecond() : self
    {
        foreach ($this->departments as $department) {
            $department->setAntiCrisisMeasuresSecond();
        }
 
        return $this;
    }
 
    public function setAntiCrisisMeasuresThird() : self
    {
        foreach ($this->departments as $department) {
            $department->setAntiCrisisMeasuresThird();
        }
 
        return $this;
    }
}
 
class Department
{
    protected $name;
    protected $employees = [];
 
    public function __construct(string $name)
    {
        $this->name = $name;
    }
 
    public function addEmployee(Employee $employee, int $count = 1) : self
    {
        for ($i = 0; $i < $count; $i++) {
        }
 
        return $this;
    }
 
    public function getCountEmployee() : int
    {
        return count($this->employees);     }
 
    public function getExpenses
() : array     {
        $money  = 0;
        $coffee = 0;
 
        foreach ($this->employees as $employee) {
            $money  += $employee->getJob()->getRate();
            $coffee += $employee->getJob()->getCoffee();
        }
 
        return [
            'Зарплата' => $money,
            'Кофе'     => $coffee
        ];
    }
 
    public function getReports() : float
    {
        $reports = 0;
 
        foreach ($this->employees as $employee) {
            $reports += $employee->getJob()->getReport();
        }
 
        return $reports;
    }
 
    public function getName() : string
    {
        return $this->name;
    }
 
    public function getAverageConsumptionMoneyPerPage() : float
    {
        return round($this->getExpenses()['Зарплата'] / $this->getReports(), 2);     }
 
    public function dismissEmployee(int $id) : self
    {
        unset($this->employees[$id]);  
        return $this;
    }
 
    public function getEmployeesByJob
(Job 
$job) : array     {
        $data = [];
        foreach ($this->employees as $employee) {
            if ($employee->getJob() instanceof $job) {
                $data[] = $employee;
            }
        }
        return $data;
    }
 
    public function getLeaders
() : array     {
        $data = [];
        foreach ($this->employees as $employee) {
            if ($employee->isLeader()) {
                $data[] = $employee;
            }
        }
        return $data;
    }
 
    public function getEmployeersByJobAndRank
(Job 
$job, int 
$rank) : array     {
        $data = [];
        foreach ($this->employees as $id => $employee) {
            if ($employee->getJob() instanceof $job) {
                if ($employee->getRank() === $rank) {
                    $data[] = $employee;
                }
            }
        }
        return $data;
    }
 
    public function setAntiCrisisMeasuresFirst()
    {
        $reduction = ceil(count($this->getEmployeesByJob(new Engineer
())) * 0.4);         $minRank = 1;
        $iteration = 1;
 
        $skippableEmployees = [];
 
        while($reduction !== 0) {
            foreach ($this->employees as $id => $employee) {
                if ($employee->getJob() instanceof Engineer) {
                    if ($employee->getRank() === $minRank) {
                        if ($employee->isLeader()) {
                            if (! in_array($id, $skippableEmployees)) {                                 $skippableEmployees[] = $id;
                            }
                        }
 
                        if (! in_array($id, $skippableEmployees)) {                             if ($reduction > 0) {
                                $this->dismissEmployee($id);
                                $reduction -= 1;
                            }
                        }
                    }
                }
            }
            if ($iteration % 2 === 0) {
                $minRank++;
                if ($minRank > 3) {
                    break;
                }
            }
            $iteration++;
        }
    }
 
    public function setAntiCrisisMeasuresSecond()
    {
        $maxRank = 0;
 
        foreach ($this->employees as $id => $employee) {
            if ($employee->isLeader()) {
                if (! is_a($employee->getJob(), Analyst
::class)) {  
                    foreach ($this->employees as $search) {
                        if ($search->getJob() instanceof Analyst && $search->getRank() > $maxRank) {
                            $maxRank = $search->getRank();
                        }
                    }
 
                    if($maxRank !== 0) {
                        foreach ($this->employees as $search) {
                            if ($search->getJob() instanceof Analyst && $search->getRank() === $maxRank) {
                                $search->setJob(new $job(), $maxRank, true);
                            }
                        }
                        $employee->setJob(new $job(), $maxRank);
                    }
 
                }
            }
            if ($employee->getJob() instanceof Analyst) {
                $employee->getJob()->setRate(1100);
                $employee->getJob()->setCoffee(75);
 
                $employee->updateJob($employee->getRank(), $employee->isLeader());
            }
        }
    }
 
    public function setAntiCrisisMeasuresThird()
    {
        $employeeFirstRank  = $this->getEmployeersByJobAndRank(new Manager(), 1);
        $employeeSecondRank = $this->getEmployeersByJobAndRank(new Manager(), 2);
 
        $countFirstRank  = count($employeeFirstRank) * 0.5;         $countSecondRank = count($employeeSecondRank) * 0.5;  
        $upgradedFirstRank  = 0;
        $upgradedSecondRank = 0;
 
        foreach ($this->employees as $id => $employee) {
            if ($employee->getJob() instanceof Manager) {
                if ($employee->getRank() === 1) {
                    if($upgradedFirstRank < $countFirstRank) {
                        $employee->setJob(new Manager(), 2);
                        $upgradedFirstRank++;
                    }
                } elseif($employee->getRank() === 2) {
                    if($upgradedSecondRank < $countSecondRank) {
                        $employee->setJob(new Manager(), 3);
                        $upgradedSecondRank++;
                    }
                }
            }
        }
    }
}
 
abstract class Job
{
    public function setRate(int $rate) : self
    {
        $this->rate = $rate;
 
        return $this;
    }
 
    public function getRate() : int
    {
        return $this->rate;
    }
 
    public function setCoffee(int $coffee) : self
    {
        $this->coffee = $coffee;
 
        return $this;
    }
 
    public function getCoffee() : int
    {
        return $this->coffee;
    }
 
    public function setReport(int $report) : self
    {
        $this->report = $report;
 
        return $this;
    }
 
    public function getReport() : int
    {
        return $this->report;
    }
}
 
class Employee
{
    protected $rank;
    protected $isLeader = false;
    protected $job;
 
    public function __construct(Job $job, int $rank, bool $isLeader = false)
    {
        $this->rank     = $rank;
        $this->isLeader = $isLeader;
        $this->job      = $job;
 
        $this->updateJob($rank, $isLeader);
    }
 
    public function updateJob(int $rank, bool $isLeader = false)
    {
        switch ($rank) {
            case 1:
                break;
            case 2:
                $this->job->setRate($this->job->getRate() * 1.25);
                break;
            case 3:
                $this->job->setRate($this->job->getRate() * 1.5);
                break;
        }
 
        if ($isLeader === true) {
            $this->job->setRate($this->job->getRate() * 1.5);
            $this->job->setCoffee($this->job->getCoffee() * 2);
            $this->job->setReport(0);
        }
    }
 
    public function getRank() : int
    {
        return $this->rank;
    }
 
    public function isLeader() : bool
    {
        return $this->isLeader;
    }
 
    public function getJob() : Job
    {
        return $this->job;
    }
 
    public function setJob(Job $job, int $rank, bool $isLeader = false) : self
    {
        $this->job      = $job;
        $this->rank     = $rank;
        $this->isLeader = $isLeader;
 
        $this->updateJob($rank, $isLeader);
 
        return $this;
    }
}
 
class Manager extends Job
{
    protected $rate   = 500;
    protected $coffee = 20;
    protected $report = 200;
}
 
class Marketer extends Job
{
    protected $rate   = 400;
    protected $coffee = 15;
    protected $report = 150;
}
 
class Engineer extends Job
{
    protected $rate   = 200;
    protected $coffee = 5;
    protected $report = 50;
}
 
class Analyst extends Job
{
    protected $rate   = 800;
    protected $coffee = 50;
    protected $report = 5;
}
 
function padRight(string $string, int $length): string
{
}
 
function padLeft(string $string, int $length): string
{
}
 
function write($vector) {
    foreach ($vector->getDepartments() as $department) {
        echo padRight($department->getName(), 11)
            .padLeft($department->getCountEmployee(), 11)
            .padLeft($department->getExpenses()['Зарплата'], 11)
            .padLeft($department->getExpenses()['Кофе'], 11)
            .padLeft($department->getReports(), 11)
            .padLeft($department->getAverageConsumptionMoneyPerPage(), 11) . PHP_EOL;
    }
    echo padRight('Среднее', 11)
        .padLeft($vector->getAverageCountEmployers(), 11)
        .padLeft($vector->getAverageExpenses()[0], 11)
        .padLeft($vector->getAverageExpenses()[1], 11)
        .padLeft($vector->getAverageReports(), 11)
        .padLeft($vector->getAverageConsumptionMoneyPerPage(), 11) . PHP_EOL;
    echo padRight('Всего', 11)
        .padLeft($vector->getCountEmployee(), 11)
        .padLeft($vector->getExpenses()[0], 11)
        .padLeft($vector->getExpenses()[1], 11)
        .padLeft($vector->getReports(), 11)
        .padLeft($vector->getConsumptionMoneyPerPage(), 11) . PHP_EOL;
}
 
$vector = new Company();
 
$purchasing = (new Department('закупок'))
    ->addEmployee(new Employee(new Manager(), 3), 9)
    ->addEmployee(new Employee(new Manager(), 2), 3)
    ->addEmployee(new Employee(new Manager(), 3), 2)
    ->addEmployee(new Employee(new Marketer(), 1), 2)
    ->addEmployee(new Employee(new Manager(), 2, true));
 
$sells = (new Department('продаж'))
    ->addEmployee(new Employee(new Manager(), 1), 12)
    ->addEmployee(new Employee(new Marketer(), 1), 6)
    ->addEmployee(new Employee(new Analyst(), 1), 3)
    ->addEmployee(new Employee(new Analyst(), 2), 2)
    ->addEmployee(new Employee(new Manager(), 2, true));
 
$ad = (new Department('рекламы'))
    ->addEmployee(new Employee(new Marketer(), 1), 15)
    ->addEmployee(new Employee(new Marketer(), 2), 10)
    ->addEmployee(new Employee(new Manager(), 1), 8)
    ->addEmployee(new Employee(new Engineer(), 1), 2)
    ->addEmployee(new Employee(new Marketer(), 3, true));
 
$logistics = (new Department('логистики'))
    ->addEmployee(new Employee(new Manager(), 1), 13)
    ->addEmployee(new Employee(new Manager(), 2), 5)
    ->addEmployee(new Employee(new Engineer(), 1), 5)
    ->addEmployee(new Employee(new Manager(), 1, true));
 
$vector->addDepartment($purchasing);
$vector->addDepartment($sells);
$vector->addDepartment($ad);
$vector->addDepartment($logistics);
 
echo padLeft("Департамент", 11)
    .padLeft("сотр.", 11)
    .padLeft("тугр.", 11)
    .padLeft("кофе", 11)
    .padLeft("стр.", 11)
    .padLeft("тугр./стр.", 15) . PHP_EOL;
 
echo '---------------------------------------------------------------------' . PHP_EOL;
echo "=== DEFAULT ===" . PHP_EOL;
write($vector);
 
 
 
$antiCrisisVectorFirst->setAntiCrisisMeasuresFirst();
$antiCrisisVectorSecond->setAntiCrisisMeasuresSecond();
$antiCrisisVectorThird->setAntiCrisisMeasuresThird();
 
echo "=== ANTI-CRISIS FIRST ===" . PHP_EOL;
write($antiCrisisVectorFirst);
 
echo "=== ANTI-CRISIS SECOND ===" . PHP_EOL;
write($antiCrisisVectorSecond);
 
echo "=== ANTI-CRISIS THIRD ===" . PHP_EOL;
write($antiCrisisVectorThird);