<?php
error_reporting(-1);
mb_internal_encoding('utf-8');

abstract class Worker
{
    protected $salary;
    protected $coffee;
    protected $pages;
    protected $rank;
    protected $isBoss = FALSE;

    public function setSalary($x)   {  $this->salary = $x;      }
    public function setCoffee($x)   {  $this->coffee = $x;      }
    public function setIsBoss($x)   {  $this->isBoss = $x;      }
    public function setRank($x)     {  $this->rank = $x;        }

    public function getRank()       {  return $this->rank;      }
    public function getIsBoss()     {  return $this->isBoss;    }

    public function __construct($rank)
    {
        $this->rank = $rank;
    }

    //использую статичесую функцию со new static для создания экземпляров класса.
    //иными словами, буду вызывать эту функцию через подклассы и создавать объекты
    public static function create($rank, $bodyCount)
    {
        $arrayOfMen = [];
        for($i = 1; $i <= $bodyCount; $i++){
            $newMan = new static($rank);
            $arrayOfMen[] = $newMan;
        }
        return $arrayOfMen;
    }

    //функция, которая будет давать статистику по персонажу
    public function getStatistic()
    {
        $rankCoefficient = 0;           // коэффициент для умножения результатов по рангам
        switch ($this->rank):
            case 1:
                $rankCoefficient = 1;
                break;
            case 2:
                $rankCoefficient = 1.25;
                break;
            case 3:
                $rankCoefficient = 1.5;
                break;
        endswitch;

        $bossCoefficient = 0;           //коэффициент для босса
        switch ($this->isBoss):
            case FALSE:
                $bossCoefficient = 1;
                break;
            case TRUE:
                $bossCoefficient = 2;
                break;
        endswitch;

        $statistic = [
            "salary" => $this->salary * $rankCoefficient * $bossCoefficient,
            "coffee" => $this->coffee * $bossCoefficient,
            "pages" => $this->pages,
        ];

        //проверяю, босс ли, чтобы стереть его страницы
        if ($this->isBoss) {
            $statistic["pages"] = 0;
        }
        return $statistic;
    }

    public static function makeHimBoss(array $arrayOfMen)
    {
        $futureBoss = $arrayOfMen[0];
        $futureBoss->isBoss = TRUE;
    }
}

class Marketing extends Worker
{
    protected $salary = 400;
    protected $coffee = 15;
    protected $pages = 150;
}
class Management extends Worker
{
    public $salary = 500;
    public $coffee = 20;
    public $pages = 200;
}

class Engineer extends Worker
{
    protected $salary = 200;
    protected $coffee = 5;
    protected $pages = 50;
}

class Analysis extends Worker
{
    protected $salary = 800;
    protected $coffee = 50;
    protected $pages = 5;
}

class Department
{
    //в этом массиве хранятся все сотрудники
    public $staff = [];

    //тут добавляю сотрудников в этот массив
    public function addNewStaffMember(array $men)
    {
        $this->staff = array_merge($this->staff, $men);
    }

    //увольняю(для антикризисных мер)
    public function fireStaffMember(Worker $fired)
    {
        foreach($this->staff as $id => $man){
            //тут долго думал, в итоге использую "===" для проверки того, идентичные ли объекты. Правильно всё?
            if($man === $fired)  { unset($this->staff[$id]); }
        }
    }

    //через эту функцию вывожу статистику по департаменту
    public function getStatisticOfDepartment()
    {
        $totalSalary = $totalCoffee = $totalPages = $totalBodies = 0;
        foreach($this->staff as $man){
            $totalSalary += $man->getStatistic()["salary"];
            $totalCoffee += $man->getStatistic()["coffee"];
            $totalPages  += $man->getStatistic()["pages"];
            $totalBodies++;
        }
        $resultStats =
            [
                "bodies" => $totalBodies,
                "salary" => $totalSalary,
                "coffee" => $totalCoffee,
                "pages" =>  $totalPages,
            ];
        //возвращаю массив со статистикой
        return $resultStats;
    }
}
///// для таблицы ----------------------------------------------------------------------
//функция аналог str_pad, только для кириллицы (взял с инета, т.к. стандартная str_pad не работает с кириллицей)
function mb_str_pad($str, $pad_len, $pad_str = ' ', $dir = STR_PAD_RIGHT)
{
    $padBefore = $dir === STR_PAD_BOTH || $dir === STR_PAD_LEFT;
    $padAfter = $dir === STR_PAD_BOTH || $dir === STR_PAD_RIGHT;
    $pad_len -= mb_strlen($str);
    $targetLen = $padBefore && $padAfter ? $pad_len / 2 : $pad_len;
    $strToRepeatLen = mb_strlen($pad_str);
    $repeatTimes = ceil($targetLen / $strToRepeatLen);
    $repeatedString = str_repeat($pad_str, max(0, $repeatTimes));
    $before = $padBefore ? mb_substr($repeatedString, 0, floor($targetLen)) : '';
    $after = $padAfter ? mb_substr($repeatedString, 0, ceil($targetLen)) : '';
    return $before . $str . $after;
}

//пишу функцию для вставки пробелов справа
function padRight($string, $length)
{
    $spaces = $length - mb_strlen($string);
    if($spaces>0) { $string=mb_str_pad($string, mb_strlen($string) + $spaces, " ", STR_PAD_RIGHT); }
    return $string;
}

//пишу функцию для вставки пробелов слева
function padLeft($string, $length)
{
    $spaces = $length - mb_strlen($string);
    if($spaces>0) { $string=mb_str_pad($string, mb_strlen($string) + $spaces, " ", STR_PAD_LEFT); }
    return $string;
}
///// для таблицы ---------------------------------------------------------------------- КОНЕЦ

//Заполняем департменты рабочими
$BuyingDep = new Department();
$men = Management::create(1, 9);
$BuyingDep->addNewStaffMember($men);
$men = Management::create(2, 3);
$BuyingDep->addNewStaffMember($men);
$men = Management::create(3, 2);
$BuyingDep->addNewStaffMember($men);
$men = Marketing::create(1, 2);
$BuyingDep->addNewStaffMember($men);
$men = Management::create(2, 1);
Worker::makeHimBoss($men);
$BuyingDep->addNewStaffMember($men);

$SellingDep = new Department();
$men = Management::create(1, 12);
$SellingDep->addNewStaffMember($men);
$men = Marketing::create(1, 6);
$SellingDep->addNewStaffMember($men);
$men = Analysis::create(1, 3);
$SellingDep->addNewStaffMember($men);
$men = Analysis::create(2, 2);
$SellingDep->addNewStaffMember($men);
$men = Marketing::create(2, 1);
Worker::makeHimBoss($men);
$SellingDep->addNewStaffMember($men);

$AdsDep = new Department();
$men = Marketing::create(1, 15);
$AdsDep->addNewStaffMember($men);
$men = Marketing::create(2, 10);
$AdsDep->addNewStaffMember($men);
$men = Management::create(1, 8);
$AdsDep->addNewStaffMember($men);
$men = Engineer::create(1, 2);
$AdsDep->addNewStaffMember($men);
$men = Marketing::create(3, 1);
Worker::makeHimBoss($men);
$AdsDep->addNewStaffMember($men);

$LogisticsDep = new Department();
$men = Management::create(1, 13);
$LogisticsDep->addNewStaffMember($men);
$men = Management::create(2, 5);
$LogisticsDep->addNewStaffMember($men);
$men = Engineer::create(1, 5);
$LogisticsDep->addNewStaffMember($men);
$men = Management::create(1, 1);
Worker::makeHimBoss($men);
$LogisticsDep->addNewStaffMember($men);

//функция вывода Таблицы
function showTable($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep)
{
//два типа колонок
    $col1 = 25;
    $col2 = 15;

//верхняя строка таблицы
    echo padRight("Департамент", $col1) .
        padLeft("сотр.", $col2) .
        padLeft("тугр.", $col2) .
        padLeft("кофе", $col2) .
        padLeft("стр.", $col2) .
        padLeft("тугр./стр.", $col2) .
        "\n";

    echo str_repeat("-", 100) . "\n";

    $totalBodies = $totalSalary = $totalCoffee = $totalPages = $totalBalance = 0;
    //делаю массив массивов статистики по всем департаментам
    $statistics = [
        "Закупок" => $BuyingDep->getStatisticOfDepartment(),
        "Продаж" => $SellingDep->getStatisticOfDepartment(),
        "Рекламы" => $AdsDep->getStatisticOfDepartment(),
        "Логистики" => $LogisticsDep->getStatisticOfDepartment(),
    ];

    //прохожусь по массиву массивов
    foreach ($statistics as $department => $datum) {
        $balance = round($datum["salary"] / $datum["pages"], 2);
        echo padRight("$department", $col1) .
            padLeft($datum["bodies"], $col2) .
            padLeft($datum["salary"], $col2) .
            padLeft($datum["coffee"], $col2) .
            padLeft($datum["pages"], $col2) .
            padLeft($balance, $col2) .
            "\n";
        $totalBodies += $datum["bodies"];
        $totalSalary += $datum["salary"];
        $totalCoffee += $datum["coffee"];
        $totalPages += $datum["pages"];
        $totalBalance += $balance;
    }

    echo "\n";

    //колонка СРЕДНЕЕ
    echo padRight("Среднее", $col1) .
        padLeft($totalBodies / 4, $col2) .
        padLeft($totalSalary / 4, $col2) .
        padLeft($totalCoffee / 4, $col2) .
        padLeft($totalPages / 4, $col2) .
        padLeft($totalBalance / 4, $col2) .
        "\n";

    //колонка ВСЕГО
    echo padRight("Всего", $col1) .
        padLeft($totalBodies, $col2) .
        padLeft($totalSalary, $col2) .
        padLeft($totalCoffee, $col2) .
        padLeft($totalPages, $col2) .
        padLeft($totalBalance, $col2) .
        "\n";
}

showTable($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep);
echo "\n\n\n\n\n";
//антикризисные меры, надо раскоментировать необходимую строчку
//executeAction1($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep);
//executeAction2($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep);
executeAction3($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep);


//
//          АНТИКРИЗИСНЫЕ МЕРЫ ---------------------
//--------------------------------------------------
//
//1. Сократить в каждом департаменте 40% (округляя в большую сторону) инженеров,
// преимущественно самого низкого ранга.
// Если инженер является боссом, вместо него надо уволить другого инженера, не босса.
function executeAction1($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep)
{

    $allDepartments = [$BuyingDep, $SellingDep, $AdsDep, $LogisticsDep];
    foreach ($allDepartments as $department) {
        $allEngineers = 0;
        $listOfRank1 = [];
        $listOfRank2 = [];
        $listOfRank3 = [];
        $wasThereAnEngineerBoss = 0;
        //прохожусь по департаментам и распределяю инженеров
        foreach ($department->staff as $man) {
            if ($man instanceof Engineer AND $man->getIsBoss() == FALSE) {
                $allEngineers++;
                if ($man->getRank() == 1) {
                    $listOfRank1[] = $man;
                } elseif ($man->getRank() == 2) {
                    $listOfRank2[] = $man;
                } elseif ($man->getRank() == 3) {
                    $listOfRank3[] = $man;
                }
            } elseif ($man instanceof Engineer AND $man->getIsBoss() == TRUE) {
                $wasThereAnEngineerBoss = 1;
            }
            $allEngineers += $wasThereAnEngineerBoss;

            $engineersToFire = ceil($allEngineers * 0.4);
            $firedCount = 0;
        }

            //соединяю в один массив, от меньших рангов к большим.
            $listByRank = array_merge($listOfRank1, $listOfRank2, $listOfRank3);

            if (!empty($listByRank)) {
                foreach ($department->staff as $id => $man) {
                    foreach ($listByRank as $fired) {
                        if ($man === $fired) {
                            unset($department->staff[$id]);
                            $firedCount++;
                        }
                    }
                    if ($firedCount == $engineersToFire) break;
                }
            }
        }
    echo "Solution #1. This is what would happen if we dismissed 40% of our Engineers\n\n";
    showTable($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep);
    echo "\n\n\n\n\n";
}


//2. 2. Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков,
// а количество выпиваемого им кофе с 50 до 75 литров. В тех департаментах, где руководитель
// не является аналитиком, заменить его на аналитика самого
// высшего ранга из этого департамента (а бывшего руководителя вернуть к обычной работе)
function executeAction2($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep)
{
    $allDepartments = [$BuyingDep, $SellingDep, $AdsDep, $LogisticsDep];
    //прохожусь по департаментам
    foreach($allDepartments as $department) {
        $wasThereNotAnalysisBoss = FALSE; //использую эту переменную для того, чтоб унзать, был ли босс Аналитиком
        //прохожусь по сотрудникам департаментов
        foreach ($department->staff as $man) {
            $highestRank = 1;
            $highestRankedAnalysisMan = NULL; //эта переменная служит для выявления наиболее ранкнутого аналитика
            if ($man instanceof Analysis) {
                $man->setSalary(1100);
                $man->setCoffee(75);
                if ($man->getRank() >= $highestRank) {
                    $highestRank = $man->getRank();
                    $highestRankedAnalysisMan = $man;
                }
            //проверяю, есть ли босс не аналитик, если да, разжалую его
            } elseif($man->getIsBoss() == TRUE){
                $wasThereNotAnalysisBoss = TRUE;
                $exBoss = $man;
            }
        }
        //наиболее ранкнутого инженера повышаю, если $wasThereNotAnalysisBoss показало, что был босс-не-аналитик
        if($wasThereNotAnalysisBoss == TRUE AND $highestRankedAnalysisMan != NULL) {
            $highestRankedAnalysisMan->setIsBoss(TRUE);
            $exBoss->setIsBoss(FALSE);
        }
    }
    echo "Solution #2. This is what would happen if we raised the salary rate of the Analytics.\n\n";
    showTable($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep);
    echo "\n\n\n\n\n";
}



//3. В каждом департаменте повысить 50% (округляя в большую сторону) менеджеров
// 1-го и 2-го ранга на один ранг с целью расширить их полномочия.
function executeAction3($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep)
{
    $allDepartments = [$BuyingDep, $SellingDep, $AdsDep, $LogisticsDep];
    $allManagersOfRank1 = [];
    $allManagersOfRank2 = [];
    foreach($allDepartments as $department){
        foreach($department->staff as $man){
            if($man instanceof Management){
                if($man->getRank() == 1) {
                    $allManagersOfRank1[] = $man;
                } elseif($man->getRank() == 2){
                    $allManagersOfRank2[] = $man;
                }
            }
        }
        $toGetPromotedOfRank1 = ceil((count($allManagersOfRank1)+1)*0.5);
        $toGetPromotedOfRank2 = ceil((count($allManagersOfRank2)+1)*0.5);
        $alreadyPromotedOfRank1 = 0;
        $alreadyPromotedOfRank2 = 0;
        foreach($allManagersOfRank1 as $man){
            $man->setRank(2);
            $alreadyPromotedOfRank1++;
            if($toGetPromotedOfRank1 == $alreadyPromotedOfRank1) break;
        }
        foreach($allManagersOfRank2 as $man){
            $man->setRank(3);
            $alreadyPromotedOfRank2++;
            if($toGetPromotedOfRank2 == $alreadyPromotedOfRank2) break;
        }
    }

    echo "Solution #3. This is what would happen if we promoted half of our Managers.\n\n";
    showTable($BuyingDep, $SellingDep, $AdsDep, $LogisticsDep);
    echo "\n\n\n\n\n";
}
