<?php


mb_internal_encoding("utf-8");
error_reporting(-1);


class PrettyPrinter
{
    private $head = [
                    "col0" => "№",
                    "col1" => "департамент",
                    "col2" => "Σрабов",
                    "col3" => "Σтугриков",
                    "col4" => "Σкофе",
                    "col5" => "Σстраниц",
                    "col6" => "тугриков/страниц"
                 ];
    private $foot = [
                    "col0" => " ",
                    "col1" => "Итого",
                    "col2" => NULL,
                    "col3" => NULL,
                    "col4" => NULL,
                    "col5" => NULL,
                    "col6" => NULL
                 ];          
    private $trail = [];
    private $padder;
    
    private $prettyData = [];
    private $printableResult;
    
    public function __construct($departments) 
    {
        $this->setPadder();
        $this->setHeadTrials();
        $inputArr= [];
        foreach($departments as $department) {
            $input["col1"] = $department->getDepName(); 
            $input["col2"] = $department->getEmployeesNum();
            $input["col3"] = $department->getTotalSalary(); 
            $input["col4"] = $department->getTotalCoffee();
            $input["col5"] = $department->getTotalPages();
            $input["col6"] = number_format($department->getSalaryPagesRatio(), 3);
            
            array_push($inputArr, $input);
        }
        $enum = range(1, count($inputArr)); 
        for($i = 0; $i < count($inputArr); $i++) {
            $inputArr[$i] = array_merge(["№" => $enum[$i]], $inputArr[$i]);
            $this->fillPrettyData($inputArr[$i]);
        }
        
        $this->setPrintableHeader();
        $this->initFooter($inputArr);
        $this->setPrintableFoot();
        foreach($this->prettyData as $row) {
            $this->setPrintableResult($row);
        }
    }
    
    private function setPrintableHeader()
    {
        foreach(array_values($this->head) as $header) {
            $this->printableResult .= (string)($header);
            $this->printableResult .= " ";
        }
        $this->printableResult .= ("\n" . str_repeat("-", $this->padder) . "\n");
    }
    
    private function setHeadTrials()
    {
        foreach($this->head as $col=>$str) {
            $this->trail[$col] = mb_strlen($str);
        }                            
    }
    
    private function setPadder()
    {
        $func = function($value) {
            return mb_strlen((string)$value);
        };
        $this->padder = array_sum(array_map($func, array_values($this->head))) +
                                  count(array_values($this->head)) - 1;
    }
    
    private function fillPrettyData($arr)
    {
        $func = function($value) {
            return mb_strlen((string)$value);
        };
        $dataLens = [];
        $temp = array_combine(array_values($this->trail),
                             array_map($func, array_values($arr)));
        
        foreach($temp as $len1=>$len2) {
            $len = abs($len1 - $len2);
            $data = str_repeat(" ", $len);
            array_push($dataLens, $data);
        }   
        $prettyChunk = array_combine(array_values($arr),
                                    $dataLens); 
        
        array_push($this->prettyData, $prettyChunk);
    }
    
    private function setPrintableResult($arr)
    {
        foreach($arr as $data=>$padding) {
            $res = (string)$data . (string)$padding;
            $this->printableResult .= $res;
            $this->printableResult .= " ";
        }
        $this->printableResult .= ("\n" . str_repeat("-", $this->padder) . "\n");
    }
    
    public function __toString()
    {
        return $this->printableResult;
    }
    
    private function initFooter($arr)
    {
        $allWorkers = 0;
        $allCoffee = 0;
        $allSalary = 0;
        $allPages = 0;
        $allAvg = [];
        
        foreach($arr as $subarr) {
            $cur_vals = array_values($subarr);
            $allWorkers += $cur_vals[2];
            $allSalary += $cur_vals[3];
            $allCoffee += $cur_vals[4];
            $allPages += $cur_vals[5];
            array_push($allAvg, $cur_vals[6]);
        }
        
        $this->foot["col2"] = $allWorkers;
        $this->foot["col3"] = $allSalary;
        $this->foot["col4"] = $allCoffee;
        $this->foot["col5"] = $allPages;
        $this->foot["col6"] = array_sum($allAvg) / count($allAvg);
    }
    
    private function setPrintableFoot()
    {
        $this->fillPrettyData($this->foot);
    }
}


class Employee
{
    private $salary;
    private $rank;
    private $coffeeAmt;
    private $pages;
    private $isBoss;
    
    public function __construct($salary, $rank, $coffeeAmt, $pages, $boss=false)
    {
        $this->salary = $salary;
        $this->rank = $rank;
        $this->coffeeAmt = $coffeeAmt;
        $this->pages = $pages;
        $this->isBoss = $boss;
    }   
    
    public function getSalary()
    {
        if ($this->rank === 1) {
            $salary = $this->salary;
        } elseif($this->rank === 2) {
            $salary = $this->salary * 1.25;
        } elseif($this->rank === 3) {
            $salary = $this->salary * 1.5;
        } 
        
        if($this->isBoss) {
            $salary += $salary * 0.5;
        }
        
        return $salary;
    }
    
    public function getCoffee()
    {
        if($this->isBoss) {
            return $this->coffeeAmt * 2;
        }
        return $this->coffeeAmt;
    }
    
    public function getPages()
    {
        if($this->isBoss) {
            return 0;
        }
        return $this->pages;
    }
    
    public function isBoss()
    {
        return $this->isBoss;
    }
    
    public function getRank()
    {
        return $this->rank;
    }
    
    public function setRank($rank)
    {
        $this->rank = $rank;
    }
    
    public function setSalary($salary)
    {
        $this->salary = $salary;
    }
    
    public function setCoffee($amt)
    {
        $this->coffeeAmt = $amt;
    }
    
    public function setPages($amt)
    {
        $this->pages = $amt;
    }
}


class Manager extends Employee
{
}


class Analyst extends Employee
{
}


class Engineer extends Employee
{
}


class Marketer extends Employee
{
}


class Department
{
    private $totalSalary;
    private $totalCoffeeAmount;
    private $totalWorkDone;
    private $employees = [];
    private $depName;
    
    public function __construct($depName)
    {
        $this->depName = $depName;
    }
    
    public function addEmployee($emp)   
    {
        array_push($this->employees, $emp);
    }
    
    public function fireEmployee($emp)
    {
        if (($key = array_search($emp, $this->employees)) !== false) {
            unset($this->employees[$key]);
        }
    }
    
    public function getTotalSalary() 
    {
        foreach($this->employees as $employee) {
          // var_dump($employee->getSalary());
           $this->totalSalary += $employee->getSalary();
        }
        return $this->totalSalary;
    }
    
    public function getTotalPages()
    {
        foreach($this->employees as $employee) {
            $this->totalWorkDone += $employee->getPages();
        }
        return $this->totalWorkDone;
    }
        
    public function getTotalCoffee()    
    {
        foreach($this->employees as $employee) {
            $this->totalCoffeeAmount += $employee->getCoffee();
        }
        return $this->totalCoffeeAmount;
    }
    
    public function getEmployeesNum()
    {
        return count($this->employees);
    }
    
    public function getSalaryPagesRatio()
    {
        return $this->totalSalary / $this->totalWorkDone;
    }
    
    public function getDepName()
    {
        return $this->depName;
    }
    
    public function getEmployees()
    {
        return $this->employees;
    }
    
    public function __clone()
    {
        $clonedEmps = [];
        foreach($this->employees as $emp) {
            $clonedEmps[] = clone $emp;
        }
        $this->employees = $clonedEmps;
        $this->totalSalary = 0;
        $this->totalCoffeeAmount = 0;
        $this->totalWorkDone = 0;
    }
}


function createEmployee($dep, $cls, $rank, $amt, $boss=false)
{
    $empSalarys = ["Manager" => 500, "Marketer" => 400, "Analyst" => 800, "Engineer" => 200];
    $empCoffee = ["Manager" => 20, "Marketer" => 15, "Analyst" => 50, "Engineer" => 5];
    $empPages = ["Manager" => 200, "Marketer" => 150, "Analyst" => 5, "Engineer" => 50];

    for($i = 0; $i < $amt; $i++)     {
       $emp = new $cls($empSalarys[$cls], $rank, $empCoffee[$cls], $empPages[$cls], $boss);
       $dep->addEmployee($emp);
    }
}


function cmpRanks($emp1, $emp2) 
{
    if($emp1->getRank() === $emp2->getRank()) {
        return 0;
    }
    return ($emp1->getRank() < $emp2->getRank()) ? -1 : 1;
}

function tryModel1($dep)
{
    $engineers = [];
    foreach($dep->getEmployees() as $emp) {
        if(get_class($emp) === "Engineer") {
            array_push($engineers, $emp);
        } elseif($emp->isBoss() === true) {
            continue;
        } else { 
            $randomEmp = array_rand($dep->getEmployees());
        }    
    }    
    usort($engineers, "cmpRanks");      
    for($i = 0; $i < round(count($engineers) * 0.4); $i++) {
        if($engineers[$i]->isBoss() !== true) {
            $dep->fireEmployee($engineers[$i]);
        } else {
            $dep->fireEmployee($randomEmp);
        }
    }
}

function tryModel2($dep)
{
    $checkBoss = false;
    foreach($dep->getEmployees() as $emp) {
        if(get_class($emp) === "Analyst") {
            $checkBoss = true;
            $emp->setSalary(1100);
            $emp->setCoffee(75);
        }
    }
    if($checkBoss) {
        foreach($dep->getEmployees() as $emp) {
            if($emp->isBoss() && get_class($emp) !== "Analyst") {
                $dep->fireEmployee($emp);
                $newBoss = new Analyst(1100, 3, 75, 5, $boss=true);
                $dep->addEmployee($newBoss);
            }
        }
    }
}   

function tryModel3($dep) 
{
    $depEmployees = $dep->getEmployees();
    $firstRankMan = [];
    $secondRankMan = [];
    foreach($depEmployees as $emp) {
        if(get_class($emp) === "Manager" && $emp->getRank() === 1 && $emp->isBoss() === false) {
            array_push($firstRankMan, $emp);
        } elseif(get_class($emp) === "Manager" && $emp->getRank() === 2 && $emp->isBoss() === false) {
            array_push($secondRankMan, $emp);
        } 
    }
    $firstRankLim = round(count($firstRankMan) * 0.5);
    $secondRankLim = round(count($secondRankMan) * 0.5);
    for($i = 0; $i < $firstRankLim; $i++) {
        $key = array_search($firstRankMan[$i], $depEmployees);
        $depEmployees[$key]->setRank(2);
    }
    for($i = 0; $i < $secondRankLim; $i++) {
        $key = array_search($secondRankMan[$i], $depEmployees);
        $depEmployees[$key]->setRank(3);
    }
}


$purchaseDep = new Department("закупок");
createEmployee($purchaseDep, "Manager", 1, 9);
createEmployee($purchaseDep, "Manager", 2, 3);
createEmployee($purchaseDep, "Manager", 3, 2);
createEmployee($purchaseDep, "Marketer", 1, 2);
createEmployee($purchaseDep, "Manager", 2, 1, $boss=true);

echo "Посчитаем-ка расходы на зряплату {$purchaseDep->getTotalSalary()}\n";
echo "Забыл, сколько там вышло? {$purchaseDep->getTotalSalary()}\n";
echo "Посчитаем в третий раз чтобы точно знать {$purchaseDep->getTotalSalary()}\n";



// $salesDep = new Department("продаж");
// createEmployee($salesDep, "Manager", 1, 12);
// createEmployee($salesDep, "Marketer", 1, 6);
// createEmployee($salesDep, "Analyst", 1, 3);
// createEmployee($salesDep, "Analyst", 2, 2);
// createEmployee($salesDep, "Marketer", 2, 1, $boss=true);

// $adsDep = new Department("рекламы");
// createEmployee($adsDep, "Marketer", 1, 15);
// createEmployee($adsDep, "Marketer", 2, 10);
// createEmployee($adsDep, "Manager", 1, 8);
// createEmployee($adsDep, "Engineer", 1, 2);
// createEmployee($adsDep, "Marketer", 3, 1, $boss=true);

// $logisticsDep = new Department("логистики");
// createEmployee($logisticsDep, "Manager", 1, 13);
// createEmployee($logisticsDep, "Manager", 2, 5);
// createEmployee($logisticsDep, "Engineer", 1, 5);
// createEmployee($logisticsDep, "Manager", 1, 1, $boss=true);
    
// $allDeps = [$purchaseDep, $salesDep, $adsDep, $logisticsDep];
// $clonedDeps1 = [];
// $clonedDeps2 = [];
// $clonedDeps3 = [];

// foreach($allDeps as $department) {
//     $clonedDeps1[] = clone $department;
//     $clonedDeps2[] = clone $department;
//     $clonedDeps3[] = clone $department;
// }

// function tryCrisisModel($clonedDeps, $choice)
// { 
//     if($choice === 1) {
//         array_walk($clonedDeps, "tryModel1");
//     } elseif($choice === 2) {
//         array_walk($clonedDeps, "tryModel2");
//     } elseif($choice === 3) { 
//         array_walk($clonedDeps, "tryModel3");
//     } else {
//         exit("No such model number.\n");
//     }
//     $result = new PrettyPrinter($clonedDeps);
//     echo "\n\n";
//     echo "Применение кризисной модели #{$choice}\n\n";
//     echo $result;
// }

// $baseData = new PrettyPrinter($allDeps);
// echo "Начальные данные.\n\n";
// echo $baseData;

// tryCrisisModel($clonedDeps1, 1);
// tryCrisisModel($clonedDeps2, 2);
// tryCrisisModel($clonedDeps3, 3);

//?>