<?php

/**
 * classes.php
 */

class Company {
	protected array $depts;

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

	public function getDepts() {
		return $this->depts;
	}

	public function getTotalStaffNumber() {
		$staffNumber = 0;
		foreach ($this->depts as $dept) {
			$staffNumber += $dept->getStaffNumber();
		}
		return $staffNumber;
	}

	public function getTotalLaborCost() {
		$laborCost = 0;
		foreach ($this->depts as $dept) {
			$laborCost += $dept->getLaborCost();
		}
		return $laborCost;
	}

	public function getTotalCoffeeConsumption() {
		$coffee = 0;
		foreach ($this->depts as $dept) {
			$coffee += $dept->getCoffeeConsumption();
		}
		return $coffee;
	}

	public function getTotalPaperworkProduced() {
		$paperwork = 0;
		foreach ($this->depts as $dept) {
			$paperwork += $dept->getPaperworkProduced();
		}
		return $paperwork;
	}

	public function getTotalCostPerPage() {
		$cost = 0;
		foreach ($this->depts as $dept) {
			$cost += $dept->getCostPerPage();
		}
		return $cost;
	}

	public function getAverageStaffNumber() {
		return round($this->getTotalStaffNumber() / count($this->depts), 2);
	}

	public function getAverageLaborCost() {
		return round($this->getTotalLaborCost() / count($this->depts), 2);
	}

	public function getAverageCoffeeConsumption() {
		return round($this->getTotalCoffeeConsumption() / count($this->depts), 2);
	}

	public function getAveragePaperworkProduced() {
		return round($this->getTotalPaperworkProduced() / count($this->depts), 2);
	}

	public function getAverageCostPerPage() {
		return round($this->getTotalCostPerPage() / count($this->depts), 2);
	}

	/**
	 * should I use echo or is it better to put the entire report string in a var?
	 */
	public function printReport() {
		$regcol = 15;
		$widecol = 20;

		echo padString('DEPARTMENT', $widecol) . padString('STAFF', $regcol, 'left') . padString('LABOR COST', $regcol, 'left') . padString('COFFEE DRUNK', $regcol, 'left') . padString('PAGES', $regcol, 'left') . padString('COST PER PAGE', $regcol, 'left') . "\n";
		echo padString('=', $widecol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . "\n";
		foreach ($this->depts as $dept) {
			echo padString($dept->getName(), $widecol) . padString($dept->getStaffNumber(), $regcol, 'left') . padString($dept->getLaborCost(), $regcol, 'left') . padString($dept->getCoffeeConsumption(), $regcol, 'left') . padString($dept->getPaperworkProduced(), $regcol, 'left') . padString($dept->getCostPerPage(), $regcol, 'left') . "\n";
		}
		echo padString('=', $widecol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . padString('=', $regcol, 'right', '=') . "\n";
		echo padString('TOTAL', $widecol) . padString($this->getTotalStaffNumber(), $regcol, 'left') . padString($this->getTotalLaborCost(), $regcol, 'left') . padString($this->getTotalCoffeeConsumption(), $regcol, 'left') . padString($this->getTotalPaperworkProduced(), $regcol, 'left') . padString($this->getTotalCostPerPage(), $regcol, 'left') . "\n";
		echo padString('AVERAGE', $widecol) . padString($this->getAverageStaffNumber(), $regcol, 'left') . padString($this->getAverageLaborCost(), $regcol, 'left') . padString($this->getAverageCoffeeConsumption(), $regcol, 'left') . padString($this->getAveragePaperworkProduced(), $regcol, 'left') . padString($this->getAverageCostPerPage(), $regcol, 'left') . "\n";
	}
}

class Department {
	protected string $name;
	protected array $staff;

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

	public function getName() {
		return $this->name;
	}

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

	public function getStaffNumber() {
		return count($this->staff);
	}

	public function getLaborCost() {
		$laborCost = 0;
		foreach ($this->staff as $employee) {
			$laborCost += $employee->getActualPay();
		}
		return $laborCost;
	}

	public function getCoffeeConsumption() {
		$coffee = 0;
		foreach ($this->staff as $employee) {
			$coffee += $employee->getActualCoffeeConsumption();
		}
		return $coffee;
	}

	public function getPaperworkProduced() {
		$paperwork = 0;
		foreach ($this->staff as $employee) {
			$paperwork += $employee->getActualPaperworkProduced();
		}
		return $paperwork;
	}

	public function getCostPerPage() {
		return round($this->getLaborCost() / $this->getPaperworkProduced(), 2);
	}
	
}

abstract class Employee {
	const MANAGER = "Manager";
	const MARKETER = "Marketer";
	const ENGINEER = "Engineer";
	const ANALYST = "Analyst";

	protected int $grade;
	protected bool $chief;

	public function __construct(int $grade, bool $chief = false) {
		$this->grade = $grade;
		$this->chief = $chief;
	}
	
	abstract public function getBaseRate();
	abstract public function getBaseCoffeeConsumption();
	abstract public function getBasePaperworkProduced();

	public function getActualPay() {
		$rate = $this->getBaseRate();
		if ($this->grade == 2) {
			$rate *= 1.25;
		} elseif ($this->grade == 3) {
			$rate = $rate * 1.5;
		}

		return $this->chief ? $rate * 2 : $rate;
	}

	public function getActualCoffeeConsumption() {
		return $this->chief ? $this->getBaseCoffeeConsumption() * 2 : $this->getBaseCoffeeConsumption();
	}

	public function getActualPaperworkProduced(): int {
		return $this->chief ? 0 : $this->getBasePaperworkProduced();	
	}
}

class Manager extends Employee {
	protected $baseRate = 500;
	protected $baseCoffeeConsumption = 20;
	protected int $basePaperworkProduced = 200;

	public function getBaseRate() {
		return $this->baseRate;
	}

	public function getBaseCoffeeConsumption() {
		return $this->baseCoffeeConsumption;
	}

	public function getBasePaperworkProduced() {
		return $this->basePaperworkProduced;
	}
}

class Marketer extends Employee {
	protected $baseRate = 400;
	protected $baseCoffeeConsumption = 15;
	protected int $basePaperworkProduced = 150;

	public function getBaseRate() {
		return $this->baseRate;
	}

	public function getBaseCoffeeConsumption() {
		return $this->baseCoffeeConsumption;
	}

	public function getBasePaperworkProduced() {
		return $this->basePaperworkProduced;
	}
}

class Engineer extends Employee {
	protected $baseRate = 200;
	protected $baseCoffeeConsumption = 5;
	protected int $basePaperworkProduced = 50;
	
	public function getBaseRate() {
		return $this->baseRate;
	}

	public function getBaseCoffeeConsumption() {
		return $this->baseCoffeeConsumption;
	}

	public function getBasePaperworkProduced() {
		return $this->basePaperworkProduced;
	}
}

class Analyst extends Employee {
	protected $baseRate = 800;
	protected $baseCoffeeConsumption = 50;
	protected int $basePaperworkProduced = 5;

	public function getBaseRate() {
		return $this->baseRate;
	}

	public function getBaseCoffeeConsumption() {
		return $this->baseCoffeeConsumption;
	}

	public function getBasePaperworkProduced() {
		return $this->basePaperworkProduced;
	}
}

/**
 * input.php
 */

$input = [
	'Purchasing' => [
		[9, Employee::MANAGER, 1],
		[3, Employee::MANAGER, 2],
		[2, Employee::MANAGER, 3],
		[2, Employee::MARKETER, 1],
		[1, Employee::MANAGER, 2, true]
	],

	'Sales' => [
		[12, Employee::MANAGER, 1],
		[6, Employee::MARKETER, 1],
		[3, Employee::ANALYST, 1],
		[2, Employee::ANALYST, 2],
		[1, Employee::MARKETER, 2, true]
	],


	'Advertising' => [
		[15, Employee::MARKETER, 1],
		[10, Employee::MARKETER, 2],
		[8, Employee::MANAGER, 1],
		[2, Employee::ENGINEER, 1],
		[1, Employee::MARKETER, 3, true]
	],


	'Logistics' => [
		[13, Employee::MANAGER, 1],
		[5, Employee::MANAGER, 2],
		[5, Employee::ENGINEER, 1],
		[1, Employee::MANAGER, 1, true]
	]

];

/**
 * padstring.php
 */

function padString($string, $length, $side = "right", $pad = " ") {
	if (strlen($string) == $length) {
		return $string;
	} else {
		$charsNeeded = $length - strlen($string); // 5
		$padding = str_repeat($pad, $charsNeeded);
		($side == "right") ? ($string = $string . $padding) : ($string = $padding . $string);
		return $string;
	}
}

/**
 * main.php
 */
 
 function makeDepts(array $input): array {
	$depts = [];
	foreach ($input as $dept => $staff) {
		$currentDept = new Department($dept);
		foreach ($staff as $employeeGroup) {
			$quantity = $employeeGroup[0];
			$type = $employeeGroup[1];
			$grade = $employeeGroup[2];
			$chief = isset($employeeGroup[3]) ? true : false;
			for ($c = 0; $c < $quantity; $c++) {
				$employeeObject = new $type($grade, $chief);
				$currentDept->addToStaff($employeeObject);
			}
		}
		$depts[] = $currentDept;
	}
	return $depts;
}

$depts = makeDepts($input);
$company = new Company($depts);

$company->printReport();