<?php


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


class PrettyPrinter
{
	private $head = [
					"col0" => "№",
					"col1" => "департамент",
					"col2" => "Σрабов",
					"col3" => "Σтугриков",
					"col4" => "Σкофе",
					"col5" => "Σстраниц",
					"col6" => "тугриков/страниц"
	             ];
    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"] = $department->getSalaryPagesRatio();
			
			array_push($inputArr, $input);
		}
        //var_dump($inputArr);		
        $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();
		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)));

		//var_dump(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;
	}
}


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;
	}
}


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;
	private $employeeSalarys =  [
	                               "Analyst" => 800,
								   "Engineer" => 200,
								   "Marketer" => 400,
								   "Manager" => 500
								];
	private $employeeCoffees =  [
	                               "Analyst" => 50,
								   "Engineer" => 5,
								   "Marketer" => 15,
								   "Manager" => 20
	                            ]; 	
	private $employeePages = [
	                            "Analyst" => 5,
								"Engineer" => 50,
								"Marketer" => 150,
								"Manager" => 200 
	                        ];
    
	public function __construct($depName)
	{
	    $this->depName = $depName;
	}
	
    public function addEmployee($cls, $rank, $amt, $boss=false)	
	{
	    for($i = 0; $i < $amt; $i++) {
			$emp = new $cls($this->employeeSalarys[$cls],
							$rank,
							$this->employeeCoffees[$cls],
							$this->employeePages[$cls], 
							$boss);
			array_push($this->employees, $emp);				
		}	
	}
	
	public function fireEmployee($emp)
	{
	    if (($key = array_search($emp, $this->employees)) !== false) {
            unset($this->employees[$key]);
        }
    }
	
	public function IncreaseRank($emp)
	{
	    if (in_array($emp, $this->employees)) {
            $emp->setRank($emp->getRank() + 1);
        }
    }
	
	public function DecreaseRank($emp)
	{
	    if (in_array($emp, $this->employees)) {
            $emp->setRank($emp->getRank() - 1);
        }
    }
	
	public function changeBoss($cls, $rank)
	{
	    for($i = 0; $i < count($this->employees); $i++) {
		    if($this->employees[$i]->isBoss()) {
			    unset($this->employees[$i]);
			}
		}
		$this->addEmployee($cls, $rank, 1, $boss=true);
	}
	
	public function changeBaseSalary($cls, $salary)
	{
	    $this->employeeSalarys[$cls] = $salary;
	}
	
	public function changeBaseCoffee($cls, $amt)
	{
	    $this->employeeCoffees[$cls] = $amt;
	}
	
	public function changeBasePages($cls, $num)
	{
	    $this->employeePages[$cls] = $num;
	}
	
	public function getTotalSalary() 
	{
	    foreach($this->employees as $employee) {
		    array_push($this->totalSalary, $employee->getSalary());
		}
		return array_sum($this->totalSalary);
	}
	
	public function getTotalPages()
	{
	    foreach($this->employees as $employee) {
		    array_push($this->totalWorkDone, $employee->getPages());
		}
	    return array_sum($this->totalWorkDone);
	}
		
    public function getTotalCoffee() 	
	{
	    foreach($this->employees as $employee) {
		    array_push($this->totalCoffeeAmount, $employee->getCoffee());
		}
	    return array_sum($this->totalCoffeeAmount);
	}
	
	public function getEmployeesNum()
	{
	    return count($this->employees);
	}
	
	public function getSalaryPagesRatio()
	{
	    return number_format(array_sum($this->totalSalary) / 
		                     array_sum($this->totalWorkDone), 3);
	}
	
	public function getDepName()
	{
	    return $this->depName;
	}
	
	public function __clone()
	{
	    $this->totalSalary = [];
	    $this->totalCoffeeAmount = [];
	    $this->totalWorkDone = [];
    }
}


function testing()
{
    /*Syntax of employee creation $dep->addEmployee("Class", rank, amount)*/
	
    $purchaseDep = new Department("закупок");
	$purchaseDep->addEmployee("Manager", 1, 9);
	$purchaseDep->addEmployee("Manager", 2, 3);
	$purchaseDep->addEmployee("Manager", 3, 2);
	$purchaseDep->addEmployee("Marketer", 1, 2);
	$purchaseDep->addEmployee("Manager", 2, 1, $boss=true);
	
	$salesDep = new Department("продаж");
	$salesDep->addEmployee("Manager", 1, 12);
	$salesDep->addEmployee("Marketer", 1, 6);
	$salesDep->addEmployee("Analyst", 1, 3);
	$salesDep->addEmployee("Analyst", 2, 2);
	$salesDep->addEmployee("Marketer", 2, 1, $boss=true);
	
	$adsDep = new Department("рекламы");
	$adsDep->addEmployee("Marketer", 1, 15);
	$adsDep->addEmployee("Marketer", 2, 10);
	$adsDep->addEmployee("Manager", 1, 8);
	$adsDep->addEmployee("Engineer", 1, 2);
	$adsDep->addEmployee("Marketer", 3, 1, $boss=true);
	
	$logisticsDep = new Department("логистики");
	$logisticsDep->addEmployee("Manager", 1, 13);
	$logisticsDep->addEmployee("Manager", 2, 5);
	$logisticsDep->addEmployee("Engineer", 1, 5);
	$logisticsDep->addEmployee("Manager", 1, 1, $boss=true);
	
	$grid = new PrettyPrinter([$purchaseDep, $salesDep, $adsDep, $logisticsDep]);
	echo $grid;
}

testing();
