<?php
    
    error_reporting(-1);
    mb_internal_encoding('utf-8');
  
    define("SMALL_COL", 10);
    define("MEDIUM_COL", 20);
    define("LARGE_COL", 25);
  
    class Company
    {
        private $name; //Имя компанию
        private $listOfDepatments; //Список департаментов
        
        public function __construct($name = 'Без имени')
        {
            $this->name = $name;
            $this->listOfDepatments = array();
        }

        //Получить имя компании
        public function getNameCompany()
        {
            return $this->name;
        }
        
        //Количество департаментов
        public function getCountDeparments()
        {
            return count($this->listOfDepatments);
        }
        
        //Добавить новый департамент
        public function addDepartmentToCompany($newDepartment)
        {
            $this->listOfDepatments[] = $newDepartment;
        }
        
        //Дать приказ на увольнение инженеров в каждом департаменте
        public function reduceEngineer()
        {
            foreach ($this->listOfDepatments as $departament) {
                $departament->reduceEngineerInDep(40);
            }
        }
        
        //Дать приказ на повышение менеджеров
        public function raiseManager()
        {
            foreach($this->listOfDepatments as $departament) {
                $departament->raiseManagerInDep(50);
            }
        }
        
        //повышение аналитиков (вторая мера)
        public function raiseAnalytics()
        {
            foreach($this->listOfDepatments as $departament) {
                $departament->changeDirector("Аналитик");
            }
        }
        
        //Распечатать информацию о компании (стоит ли вынести это в отдельный класс (например, CompanyReport??))
        public function printInfoAboutCompany($message)
        {
            $countDepartment = $this->getCountDeparments();
            $total = array('сотр' => 0, 'тугр' => 0, 'кофе' => 0, 'стр' => 0);

            echo "Отчет компании " . $this->getNameCompany() . $message . "\n";
            echo padRigth("Департамент", LARGE_COL) . padLeft("сотр.", SMALL_COL) . padLeft("тугр.", MEDIUM_COL) .
                 padLeft("кофе", SMALL_COL) . padLeft("стр.", SMALL_COL) . padLeft("тугр./стр.", MEDIUM_COL) . "\n";
            foreach ($this->listOfDepatments as $departament) {
                echo padRigth($departament->getDepartmentName(), LARGE_COL) . padLeft($departament->getCountEmployee(), SMALL_COL) .
                     padLeft($departament->getTotalSalary(), MEDIUM_COL) . padLeft($departament->getTotalCups(), SMALL_COL) .
                     padLeft($departament->getTotalPages(), SMALL_COL) . padLeft($departament->getTotalSalary() / $departament->getTotalPages(), MEDIUM_COL) . "\n";
                $total['сотр'] += $departament->getCountEmployee();
                $total['тугр'] += $departament->getTotalSalary();
                $total['кофе'] += $departament->getTotalCups();
                $total['стр'] += $departament->getTotalPages();
            }
            
            //возможно не правильно расчитываю среднее значение
            echo padRigth('Среднее', LARGE_COL) . padLeft($total['сотр'] / $countDepartment, SMALL_COL) .
                     padLeft($total['тугр'] / $countDepartment, MEDIUM_COL) . padLeft($total['кофе'] / $countDepartment, SMALL_COL) .
                     padLeft($total['стр'] / $countDepartment, SMALL_COL) . padLeft(($total['тугр'] / $total['стр']) / $countDepartment, MEDIUM_COL) . "\n";
            echo padRigth('Всего', LARGE_COL) . padLeft($total['сотр'], SMALL_COL) .
                     padLeft($total['тугр'], MEDIUM_COL) . padLeft($total['кофе'], SMALL_COL) .
                     padLeft($total['стр'], SMALL_COL) . padLeft($total['тугр'] / $total['стр'], MEDIUM_COL) . "\n"; 
        }
    }
    
    class Department
    {
        private $name; //Имя компании
        private $listOfEmployee; //Список сотрудников
        
        public function __construct($name = 'Noname Department')
        {
            $this->name = $name;
            $this->listOfEmployee = array();
        }
        
        //Получить имя департамента
        public function getDepartmentName()
        {
            return $this->name;
        }
        
        //Добавить сотрудника в департамент
        public function addEmployeeToDepartment($newEmployee)
        {
            $this->listOfEmployee[] = $newEmployee;
        }
        
        //Количество сотрудников
        public function getCountEmployee()
        {
            return count($this->listOfEmployee);
        }

        //Общая сумма
        public function getTotalSalary()
        {
            $totalSalary = 0;
            foreach ($this->listOfEmployee as $employee) {
                $totalSalary += $employee->getSalary();
            }
            return $totalSalary;
        }
        
        //Общий объем страниц
        public function getTotalPages()
        {
            $totalPages = 0;
            foreach ($this->listOfEmployee as $employee) {
                $totalPages += $employee->getPages();
            }
            return $totalPages;
        }
        
        //Обцее число кружек кофе
        public function getTotalCups()
        {
            $totalCups = 0;
            foreach ($this->listOfEmployee as $employee) {
                $totalCups += $employee->getCupOfCoffee();
            }
            return $totalCups;
        }
        
        //Сколько всего инженеров
        public function totalEngineer()
        {
            $result = 0;
            foreach ($this->listOfEmployee as $employee) {
                if ($employee->checkProffession("Инженер")) {
                    $result++;
                }
            }
            return $result;
        }
        
        //Сколько всего менеддеров
        public function totalManager()
        {
            $result = 0;
            foreach ($this->listOfEmployee as $employee) {
                if ($employee->checkProffession("Менеджер")) {
                    $result++;
                }
            }
            return $result;
        }
        
        //Сортировка по рангу
        public function sortByRank()
        {
            usort($this->listOfEmployee, "compareRank");
        }
        
        //Увольняем инженеров
        public function reduceEngineerInDep($percent)
        {
            $countEmployee = $this->getCountEmployee();
            $countEngineer = $this->totalEngineer();
            $engineerDelete = ceil(0.4 * $countEngineer);
            
            for ($i = 0; $i < $countEmployee; $i++) {
                if ($engineerDelete == 0) {
                    break;
                }
                if ($this->listOfEmployee[$i]->checkProffession("Инженер") && !($this->listOfEmployee[$i]->isDirector)) {
                    echo "Удалён: " . $this->listOfEmployee[$i]->getEmployeeName() . " [" . $this->listOfEmployee[$i]->getEmployeeRank() . " ранг] из " . $this->getDepartmentName() .  "\n";
                    unset($this->listOfEmployee[$i]);
                    $engineerDelete--;
                }
            }
        }
        
        //Повышение менеджеров
        public function raiseManagerInDep($percent)
        {
            $countEmployee = $this->getCountEmployee();
            $countManager = $this->totalManager();
            $managerRaise = ceil(0.5 * $countManager);
            for ($i = 0; $i < $countEmployee; $i++) {
                if ($managerRaise == 0) {
                    break;
                }
                if ($this->listOfEmployee[$i]->checkProffession("Менеджер") && ($this->listOfEmployee[$i]->getEmployeeRank() != 3)) {
                    echo "Повышен: " . $this->listOfEmployee[$i]->getEmployeeName() . " [" . $this->listOfEmployee[$i]->getEmployeeRank() . " ранг] из " . $this->getDepartmentName() .  "\n";
                    $oldRank = $this->listOfEmployee[$i]->getEmployeeRank();
                    $this->listOfEmployee[$i]->setRank($oldRank + 1);
                    $managerRaise--;
                }
            }
        }
        
        //кто директор
        public function findDirector()
        {
            foreach ($this->listOfEmployee as $employee) {
                if ($employee->isDirector) {
                    $director = $employee;
                    break;
                }
            }
            return $director;
        }
        
        //найти аналитика с высшим рангом
        public function findHighestRankInProffession($proffession)
        {
            $rank = 0;
            foreach ($this->listOfEmployee as $employee) {
                if (($employee->getEmployeeRank() > $rank) && ($employee->getEmployeeProffession() == $proffession)) {
                    $analytics = $employee;
                    $rank = $employee->getEmployeeRank();
                }
            }
            
            return ($rank) ? $analytics : false;
        }
        
        //Сменяем директора на аналитика с высшим рангом
        public function changeDirector($proffession)
        {
            $director = $this->findDirector();
            if ($director->getEmployeeProffession() != $proffession) {
                $highestAnalytics = $this->findHighestRankInProffession($proffession);
                
                if ($highestAnalytics) {
                    $highestAnalytics->isDirector = true;
                    $director->isDirector = false;
                    echo "Новый директор \"{$this->getDepartmentName()}\" - {$highestAnalytics->getEmployeeName()} [ранг - {$highestAnalytics->getEmployeeRank()}]\n";
                }
            } 
        }
    }
    
    class Employee
    {
        protected  $name; //Имя сотрудника
        public  $isDirector; //Является ли он руководителем
        protected  $rank; //ранг
        protected  $proffession = ''; //Профессия
        protected  $salary = 0; //Зарплата
        protected  $pages = 0; //Страниц отчета
        protected  $cupOfCoffee = 0; //Кружек кофе
        
        public function __construct($name = 'Username', $rank = 0, $isDirector = false)
        {
            $this->name = $name;
            $this->rank = $rank;
            $this->isDirector = $isDirector;
        }

        //Получить имя сотрудника
        public function getEmployeeName()
        {
            return $this->name;
        }
        
        //Получить профессию сотрудника
        public function getEmployeeProffession()
        {
            return $this->proffession;
        }
        
        //Получить ранг сотрудника
        public function getEmployeeRank()
        {
            return $this->rank;
        }
        
        //Посчитать зарплату
        public function getSalary()
        {
            $totalSalary = $this->salary;
            if ($this->rank == 2) {
                $totalSalary *= 1.25;
            } elseif ($this->rank == 3) {
                $totalSalary *= 1.5;
            }
            
            if ($this->isDirector) {
                $totalSalary *= 1.5;
            }
            return $totalSalary;
        }
        
        //Посчитать количество страниц
        public function getPages()
        {
            return ($this->isDirector ? 0 : $this->pages);
        }
        
        //Посчитать количество кружек кофе
        public function getCupOfCoffee()
        {
            return ($this->isDirector ? ($this->cupOfCoffee * 2) : $this->cupOfCoffee);
        }
        
        public function getRank()
        {
            return $this->rank;
        }
        
        public function setRank($rank)
        {
            $this->rank = $rank;
        }
        
        public function checkProffession($proffession) {
            return ($this->proffession == $proffession) ? true : false;
        }
    }
    
    class Manager extends Employee
    {
        protected  $salary = 500;
        protected  $pages = 200;
        protected  $cupOfCoffee = 20;
        protected  $proffession = 'Менеджер';
    }
    
    class Marketer extends Employee
    {
        protected  $salary = 400;
        protected  $pages = 150;
        protected  $cupOfCoffee = 15;
        protected  $proffession = 'Маркетолог';
    }
    
    class Engineer extends Employee
    {
        protected  $salary = 200;
        protected  $pages = 50;
        protected  $cupOfCoffee = 5;
        protected  $proffession = 'Инженер';
    }
    
    class Analyst extends Employee
    {
        protected  $salary = 800;
        protected  $pages = 5;
        protected  $cupOfCoffee = 50;
        protected  $proffession = 'Аналитик';
    }
    
    class AnalystImproved extends Analyst
    {
        protected $salary = 1100;
        protected $cupOfCoffee = 75;
    }
    
    //Для форматированного вывода
    function padLeft($text, $length)
    {
        if (mb_strlen($text) < $length) {
            $text = str_repeat(" ", $length - mb_strlen($text)) . $text;
        }
        
        return $text;
    }
    
    function padRigth($text, $length) {
        if (mb_strlen($text) < $length) {
            $text .= str_repeat(" ", $length - mb_strlen($text));
        }        
        
        return $text;
    }
    
    //функция принимает на вход строку вида: 9xме1;3xмe2;2xмe3;2xмa1;1xмe2 (последний идет руководитель) и возвращает массив созданных объектов
    function createEmployee($message)
    {
        $employee = array();
        
        $people = explode(';', $message);
        $totalPeople = count($people);
        
        
        for ($i = 0; $i < $totalPeople; $i++) {
            $rank = (int) mb_substr($people[$i], mb_strlen($people[$i]) - 1, 1);
            $proffession = mb_substr($people[$i], mb_strpos($people[$i], 'x') + 1, 2);
            $countEmployee = (int) $people[$i];
        
            $isDirector = ($i == ($totalPeople - 1)) ? true : false;
        
            for ($j = 0; $j < $countEmployee; $j++ ) {
                switch ($proffession) {
                    case "ме": $employee[] = new Manager("Имя{$j}", $rank, $isDirector);
                               break;
                    case "ма": $employee[] = new Marketer("Имя{$j}", $rank, $isDirector);
                               break;
                    case "ан": $employee[] = new Analyst("Имя{$j}", $rank, $isDirector);
                               break;
                    case "ин": $employee[] = new Engineer("Имя{$j}", $rank, $isDirector);
                               break;
                    case "па": $employee[] = new AnalystImproved("Имя{$j}", $rank, $isDirector);
                               break;
                    default: echo "Проверьте входные данные! {$proffession}";
                             exit();
                }
            }
        }

        return $employee;        
    }
    
    //Добавляем каждого сотрудника в определенный департамент
    function addAllEmployeeToDepartment($employee, $departament)
    {
        $totalEmployee = count($employee);
        
        for ($i = 0; $i < $totalEmployee; $i++) {
            $departament->addEmployeeToDepartment($employee[$i]);
        }
    }
    
    //Функция для сортировки
    function compareRank($a, $b)
    {
        return ($a->getRank() < $b->getRank()) ? -1 : 1;
    }
    
    function createCompany($procurementPeople, $salesPeople, $advertisingPeople, $logisticsPeople)
    {
        //Создаем нашу компанию
        $vector = new Company('Вектор');

        //Создаем департаменты и добавляем их в команию
        $procurementDepartment = new Department('Департамент закупок');
        $vector->addDepartmentToCompany($procurementDepartment);
        
        $salesDepartament = new Department('Департамент продаж');
        $vector->addDepartmentToCompany($salesDepartament);
        
        $advertisingDepartment = new Department('Департамент рекламы');
        $vector->addDepartmentToCompany($advertisingDepartment);
        
        $logisticsDepartament = new Department('Департамент логистики');
        $vector->addDepartmentToCompany($logisticsDepartament);
        
        //Создаем сотрудников и добавляем их в департаменты
        $procurementEmployee = array();
        $procurementEmployee = createEmployee($procurementPeople);
        addAllEmployeeToDepartment($procurementEmployee, $procurementDepartment);
        
        $salesEmployee = array();
        $salesEmployee = createEmployee($salesPeople);
        addAllEmployeeToDepartment($salesEmployee, $salesDepartament);
        
        $advertisingEmployee = array();
        $advertisingEmployee = createEmployee($advertisingPeople);
        addAllEmployeeToDepartment($advertisingEmployee, $advertisingDepartment);
        
        $logisticsEmployee = array();
        $logisticsEmployee = createEmployee($logisticsPeople);
        addAllEmployeeToDepartment($logisticsEmployee, $logisticsDepartament);
        
        return $vector;
    }

   
    //Выводим отчет без антикризисных мер
    $vector = createCompany(
        '9xме1;3xме2;2xме3;2xма1;1xме2', 
        '12xме1;6xма1;3xан1;2xан2;1xма2',
        '15xма1;10xма2;8xме1;2xин1;1xма3',
        '13xме1;5xме2;5xин1;1xме1');
    $vector->printInfoAboutCompany("");
    echo str_repeat('-', 80) . "\n";
    
    //первая антикризисная мера
    $vectorWithoutEngineer = createCompany(
        '9xме1;3xме2;2xме3;2xма1;1xме2', 
        '12xме1;6xма1;3xан1;2xан2;1xма2',
        '15xма1;10xма2;8xме1;2xин1;1xма3',
        '13xме1;5xме2;5xин1;1xме1');
    $vectorWithoutEngineer->reduceEngineer();
    $vectorWithoutEngineer->printInfoAboutCompany("[Увольнение 40% инженеров из каждого департамента]");
    echo str_repeat('-', 80) . "\n";
    
    //вторая антикризисная мера
    $vectorRaiseAnalytics = createCompany(
        '9xме1;3xме2;2xме3;2xма1;1xме2', 
        '12xме1;6xма1;3xпа1;2xпа2;1xма2',
        '15xма1;10xма2;8xме1;2xин1;1xма3',
        '13xме1;5xме2;5xин1;1xме1');
    $vectorRaiseAnalytics->raiseAnalytics();
    $vectorRaiseAnalytics->printInfoAboutCompany("[Повышение ставки аналитикам]");
    echo str_repeat('-', 80) . "\n";
       
    //третья антикризисная мера
    $vectorRaiseManager = createCompany(
        '9xме1;3xме2;2xме3;2xма1;1xме2', 
        '12xме1;6xма1;3xан1;2xан2;1xма2',
        '15xма1;10xма2;8xме1;2xин1;1xма3',
        '13xме1;5xме2;5xин1;1xме1');
    $vectorRaiseManager->raiseManager();
    $vectorRaiseManager->printInfoAboutCompany("[Повышение ранга менеджеров]");