<?php

ini_set('display_errors', 'on');
error_reporting(E_ALL);
mb_internal_encoding('UTF-8');



class Employee
{
    const RANK_FIRST = 1.0;
    const RANK_SECOND = 1.25;
    const RANK_THIRD = 1.5;
    const BOSS_PREMIUM = 1.5;
    const BOSS_COFFEE_PREMIUM = 2.0;
    const BOSS_PAGES_MULTIPLIER = 0;
    const PROF_ENGINEER = 'engineer';
    const PROF_MANAGER = 'manager';
    const PROF_ANALYST = 'analyst';
    const PROF_MARKETER = 'marketer';

    public $isBoss;
    public $rank;
    public $profession;
    public $baseCoffee;
    public $basePages;
    public $wageRate;

    public function getSalary()
    {
        if ($this->isBoss)
            return self::BOSS_PREMIUM * $this->wageRate * $this->rank;
        else return $this->wageRate * $this->rank;
    }

    public function setWageRate($wageRate)
    {
        $this->wageRate = $wageRate;
    }

    public function getPages()
    {
        if ($this->isBoss) return self::BOSS_PAGES_MULTIPLIER * $this->basePages;
        return $this->basePages;
    }

    public function setBasePages($pages)
    {
        $this->basePages = $pages;
    }

    public function getCoffee()
    {
        if ($this->isBoss) return self::BOSS_COFFEE_PREMIUM * $this->baseCoffee;
        return $this->baseCoffee;
    }

    public function setBaseCoffee($coffee)
    {
        $this->baseCoffee = $coffee;
    }

    public function __construct($profession, $rank = 1, $isBoss = false)
    {
        $this->profession = $profession;
        $this->rank = $rank;
        $this->isBoss = $isBoss;

        switch ($profession) {
            case Employee::PROF_MANAGER :
                $this->baseCoffee = 20;
                $this->basePages = 200;
                $this->wageRate = 500;
                break;
            case Employee::PROF_MARKETER :
                $this->baseCoffee = 15;
                $this->basePages = 150;
                $this->wageRate = 400;
                break;
            case Employee::PROF_ANALYST :
                $this->baseCoffee = 50;
                $this->basePages = 5;
                $this->wageRate = 800;
                break;
            case Employee::PROF_ENGINEER :
                $this->baseCoffee = 5;
                $this->basePages = 50;
                $this->wageRate = 200;
                break;
            default:
                throw new Exception('Неизвестная профессия');
        }
    }
}

class Report
{
    public $departmentName;
    public $salary;
    public $coffee;
    public $pages;
    public $employeeCount;
}

class Department
{
    public $name;
    public $employees = array();

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

    public function getReport()
    {
        $report = new Report;
        foreach ($this->employees as $employee) {
            $report->salary += $employee->getSalary();
            $report->coffee += $employee->getCoffee();
            $report->pages += $employee->getPages();
        }
        $report->departmentName = $this->name;
        $report->employeeCount = count($this->employees);
        return $report;
    }

    public function hireEmployee(Employee $employee)
    {
        $this->employees[] = $employee;
    }

    public function fireEmployee(Employee $e)
    {
        foreach ($this->employees as $employee) {
            if ($employee != $e) continue;
            $index = array_search($employee, $this->employees);
            unset($this->employees[$index]);
            break;
        }
    }
}

function hireCrowd(Department $dep, $number, $profession, $rank = 1, $isBoss = false) {
    for ($i = 0; $i < $number; $i++) {
        $employee = new Employee($profession, $rank, $isBoss);
        $dep->hireEmployee($employee);
    }
}

class Company
{
    public $departments = array();
    public function getDepartments()
    {
        return $this->departments;
    }

    public $name;

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

    public function getReport()
    {
        $report = array();
        foreach ($this->departments as $department) {
            $report[] = $department->getReport();
        }
        return $report;
    }

    public function addDepartment(Department $department)
    {
        $this->departments[] = $department;
    }

    public function dismissDepartment(Department $dep)
    {
        foreach ($this->departments as $department) {
            if ($department != $dep) continue;
            unset($department);
            break;
        }
    }
}

class ViewHelper
{
    public static function printReport(Company $company)
    {
        echo self::padRight('Департамент');
        echo self::padLeft('сотр.');
        echo self::padLeft('тугр.');
        echo self::padLeft('кофе');
        echo self::padLeft('стр.');
        echo self::padLeft('тугр./стр.'), "\n";
        for ($i=0; $i < 95; $i++) echo '-';
        echo "\n";
        $reports = $company->getReport();
        foreach ($reports as $report) {
            echo self::padRight($report->departmentName);
            echo self::padLeft($report->employeeCount);
            echo self::padLeft($report->salary);
            echo self::padLeft($report->coffee);
            echo self::padLeft($report->pages);
            echo self::padLeft(round($report->salary / $report->pages, 1));
            echo "\n";
        }
        echo self::padRight('Среднее');
        echo self::padLeft(self::average($reports, 'employeeCount'));
        echo self::padLeft(self::average($reports, 'salary'));
        echo self::padLeft(self::average($reports, 'coffee'));
        echo self::padLeft(self::average($reports, 'pages'));
        echo "\n";
        echo self::padRight('Всего');
        echo self::padLeft(self::summary($reports, 'employeeCount'));
        echo self::padLeft(self::summary($reports, 'salary'));
        echo self::padLeft(self::summary($reports, 'coffee'));
        echo self::padLeft(self::summary($reports, 'pages'));
        echo "\n";
    }
    
    private static function summary($reports, $propertyName)
    {
        if (!is_array($reports) || count($reports) == 0) return 0;
        for ($i=0, $sum=0; $i < count($reports); $i++) {
            $sum += $reports[$i]->$propertyName;
        }
        return $sum;
    }
    
    private static function average($reports, $propertyName)
    {
        $sum = self::summary($reports, $propertyName);
        return round($sum / count($reports), 1);
    }
    
    private static function padRight($string, $columnSize=20) {
        $paddingSize = $columnSize - mb_strlen($string);
        $string .= str_repeat(' ', $paddingSize);
        return $string;
    }
    
    private static function padLeft($string, $columnSize=15) {
        $paddingSize = $columnSize - mb_strlen($string);
        $string = str_repeat(' ', $paddingSize) . $string;
        return $string;
    }
}


$procurement = new Department('Отдел закупок'); 
hireCrowd($procurement, 9, Employee::PROF_MANAGER);
hireCrowd($procurement, 3, Employee::PROF_MANAGER, Employee::RANK_SECOND);
hireCrowd($procurement, 2, Employee::PROF_MANAGER, Employee::RANK_THIRD);
hireCrowd($procurement, 2, Employee::PROF_MARKETER);
hireCrowd($procurement, 1, Employee::PROF_MANAGER, Employee::RANK_SECOND, true);

$selling = new Department('Отдел продаж');
hireCrowd($selling, 12, Employee::PROF_MANAGER);
hireCrowd($selling, 6, Employee::PROF_MARKETER);
hireCrowd($selling, 3, Employee::PROF_ANALYST);
hireCrowd($selling, 2, Employee::PROF_ANALYST, Employee::RANK_SECOND);
hireCrowd($selling, 1, Employee::PROF_MARKETER, Employee::RANK_SECOND, true);

$advertisement = new Department('Отдел рекламы');
hireCrowd($advertisement, 15, Employee::PROF_MARKETER);
hireCrowd($advertisement, 10, Employee::PROF_MARKETER, Employee::RANK_SECOND);
hireCrowd($advertisement, 8, Employee::PROF_MANAGER);
hireCrowd($advertisement, 2, Employee::PROF_ENGINEER);
hireCrowd($advertisement, 1, Employee::PROF_MARKETER, Employee::RANK_THIRD, true);

$logistics = new Department('Отдел логистики');
hireCrowd($logistics, 13, Employee::PROF_MANAGER);
hireCrowd($logistics, 5, Employee::PROF_MANAGER, Employee::RANK_SECOND);
hireCrowd($logistics, 5, Employee::PROF_ENGINEER);
hireCrowd($logistics, 1, Employee::PROF_MANAGER, Employee::RANK_FIRST, true);

$vector = new Company('Вектор');
$vector->addDepartment($procurement);
$vector->addDepartment($selling);
$vector->addDepartment($advertisement);
$vector->addDepartment($logistics);

// Начальные условия
ViewHelper::printReport($vector);

// 1. Антикризисные меры: увольняю 40% инженеров во всех отделах
/*function fireMassive(Department $d, $profession, $amount)
{
    $tolalCount = 0;
    foreach ($d->employees as $employee) {
        if ($employee->profession == $profession) $tolalCount += 1;
    }
    $toFire = intval(ceil($tolalCount * $amount));
    $fired = 0;
    foreach ($d->employees as $employee) {
        if ($employee->profession != $profession or $employee->isBoss) continue;
        if ($fired == $toFire) break;
        $d->fireEmployee($employee);
        $fired++;
    }
}
foreach ($vector->getDepartments() as $department) {
    fireMassive($department, Employee::PROF_ENGINEER, 0.4);
}
ViewHelper::printReport($vector);*/

// 2. Антикризисные меры: меняю расходы на аналитиков, делаю аналитиков начальниками
// в тех департаментах, где они есть, иначе не меняю босса
/*foreach ($vector->getDepartments() as $department) {
    foreach ($department->employees as $employee) {
        if ($employee->profession == Employee::PROF_ANALYST) {
            $employee->setWageRate(1100);
            $employee->setBaseCoffee(75);
            $employee->setBasePages(5);
        }
        if ($employee->isBoss) {
            $oldBoss = $employee;
            break;
        }
    }
    if ($oldBoss->profession == Employee::PROF_ANALYST) continue;
    $pretendent = null;
    foreach ($department->employees as $employee) {
        if ($employee->profession != Employee::PROF_ANALYST) continue;
        if (!$pretendent or $pretendent->rank < $employee->rank) {
            $pretendent = $employee;
        }
    }
    if ($pretendent) {
        $oldBossIndex = array_search($oldBoss, $department->employees);
        $department->employees[$oldBossIndex]->isBoss = false;
        $pretendentIndex = array_search($pretendent, $department->employees);
        $department->employees[$pretendentIndex]->isBoss = true;
    }
}
ViewHelper::printReport($vector);*/

// 3. Антикризисные меры: повышаю на один ранг половину менеджеров 1 и 2 уровня.
for ($j=0; $j < count($vector->departments); $j++) {
    $toUpgrade = 0;
    foreach ($vector->departments[$j]->employees as $e) {
        if ($e->rank == Employee::RANK_FIRST || Employee::RANK_SECOND) {
            $toUpgrade++;
        }
    }
    for ($i=0; $i < $toUpgrade; $i++) {
        if ($vector->departments[$j]->employees[$i]->rank == Employee::RANK_FIRST) {
            $vector->departments[$j]->employees[$i]->rank = Employee::RANK_SECOND;
        } elseif ($vector->departments[$j]->employees[$i]->rank == Employee::RANK_SECOND) {
            $vector->departments[$j]->employees[$i]->rank = Employee::RANK_THIRD;
        }
    }
}
ViewHelper::printReport($vector);