<?php
include_once("misc.php");

class class_column
{
	public $title;
	
	public $field;
	
	public $format_;
	
	public function __construct($title, $field, $format_ = null)
	{
		if ($format_ == null) $format_ = function ($x) {return $x;};
		$this->title = $title;
		$this->field = $field;
		$this->format_ = $format_;
	}
	
	public function extract($row)
	{
		// return call_user_func($this->extract_, $row);
		return $row[$this->field];
		// return $this->extract_($row);
	}
	
	public function format($value)
	{
		return call_user_func($this->format_, $value);
		// return $this->format_($value);
	}
}

class class_table
{
	private $columns;
	
	private $rows;
	
	public function __construct($columns, $rows = [])
	{
		$this->columns = $columns;
		$this->rows = [];
		$this->fill($rows);
	}
	
	public function columns_get()
	{
		return $this->columns;
	}
	
	private function add($row)
	{
		array_push($this->rows, $row);
	}
	
	private function fill($rows)
	{
		array_map
		(
			function ($row) {$this->add($row);},
			$rows
		);
	}
	
	/*
	+------+------+------+
	|  xA  |  xB  |  xC  |
	+------+------+------+
	|  a2  |  b1  |  c3  |
	+------+------+------+
	|  a1  |  b1  |  c0  |
	+------+------+------+
	|  a1  |  b3  |  c2  |
	+------+------+------+
	|  a2  |  b2  |  c4  |
	+------+------+------+
	|  a1  |  b2  |  c1  |
	+------+------+------+
	|  a2  |  b4  |  c5  |
	+------+------+------+
	
	+------+------+------+
	|  xA  |  xB  |  xC  |
	+------+------+------+
	|  a1  |  b1  |  c0  |
	+------+------+------+
	|  a1  |  b2  |  c1  |
	+------+------+------+
	|  a1  |  b3  |  c2  |
	+------+------+------+
	|  a2  |  b1  |  c3  |
	+------+------+------+
	|  a2  |  b2  |  c4  |
	+------+------+------+
	|  a2  |  b4  |  c5  |
	+------+------+------+
	
	+------+------+------+
	|  xA  |  xB  |  xC  |
	+------+------+------+
	|  a1  |  b1  |  c0  |
	|      +------+------+
	|      |  b2  |  c1  |
	|      +------+------+
	|      |  b3  |  c2  |
	+------+------+------+
	|  a2  |  b1  |  c3  |
	|      +------+------+
	|      |  b2  |  c4  |
	|      +------+------+
	|      |  b4  |  c5  |
	+------+------+------+
	
	+------+------+------+------+------+
	|  xA  | xB:b1| xB:b2| xB:b3| xB:b4|
	+------+------+------+------+------+
	|  a1  |  c0  |  c1  |  c2  |  --  |
	+------+------+------+------+------+
	|  a2  |  c3  |  c4  |  --  |  c5  |
	+------+------+------+------+------+
	 */
	public function snap($configuration)
	{
		$columns_vertical = fetch($configuration, "columns_vertical", null, 2);
		$columns_horizontal = fetch($configuration, "columns_horizontal", null, 2);
		$columns_data = fetch($configuration, "columns_data", null, 2);
		$data_aggregator = fetch($configuration, "data_aggregator", function ($values) {return /*json_encode(*/$values/*)*/;}, 1);
		
		$columns_source =
		[
			new class_column("Vertical", "_vertical"),
			new class_column("Horizontal", "_horizontal"),
			new class_column("Data", "_data", function ($x) {return json_encode($x);}),
		];
		// gather data for the three columns
		$rows_source = null;
		{
			$rows_source = array_map
			(
				function ($row) use (&$columns_vertical, &$columns_horizontal, &$columns_data, $data_aggregator)
				{
					$raw_vertical = []; foreach ($columns_vertical as $column) array_push($raw_vertical, $column->extract($row));
					$raw_horizontal = []; foreach ($columns_horizontal as $column) array_push($raw_horizontal, $column->extract($row));
					$raw_data = []; foreach ($columns_data as $column) $raw_data[$column->field] = $column->extract($row);
					
					$row_ = [];
					$row_["_vertical"] = implode("/", $raw_vertical);
					$row_["_horizontal"] = implode("/", $raw_horizontal);
					$row_["_data"] = $data_aggregator($raw_data);
					return $row_;
				},
				$this->rows
			);
		}
		// return (new class_table($columns_source, $rows_source));
		$columns_result = [];
		{
			array_push
			(
				$columns_result,
				new class_column
				(
					fetch
					(
						$configuration,
						"label_vertical",
						function ($columns) {return implode("/", array_map(function ($column) {return $column->title;}, $columns));},
						1
					)($columns_vertical),
					"vertical"
				)
			);
		}
		// find groups
		$values = [];
		foreach ($rows_source as $row)
		{
			$value = $columns_source[1]->extract($row);
			if (array_search($value, $values) === false)
			{
				array_push
				(
					$columns_result,
					new class_column
					(
						fetch
						(
							$configuration,
							"label_horizontal",
							function ($columns, $value) {return implode("/", array_map(function ($column) {return $column->title;}, $columns)) . ":" . $value;},
							1
						)($columns_horizontal, $value),
						sprintf("horizontal_%u", count($columns_result)-1),
						fetch($configuration, "data_formatter", function ($x) {return json_encode($x);}, 1)
					)
				);
				array_push($values, $value);
			}
		}
		$groups = sql_groups($rows_source, "_vertical");
// echo("<!-- " . json_encode($groups, JSON_PRETTY_PRINT) . " -->" . "\n");
		$rows_result = array_map
		(
			function ($group) use (&$columns_vertical,&$columns_horizontal,&$columns_data,&$columns_source,&$columns_result,&$values)
			{
				$row = [];
				{
					$row["vertical"] = $group["value"];
				}
				for ($index = 0; $index < count($columns_result); ++$index)
				{
					$row[sprintf("horizontal_%u", $index)] = [];
				}
				foreach ($group["members"] as $member)
				{
					$value = $columns_source[1]->extract($member);
					$data = $columns_source[2]->extract($member);
					$index = array_search($value, $values);
					if ($index === false)
					{
						throw ("fatal error");
					}
					else
					{
						$field = sprintf("horizontal_%u", $index);
						array_push($row[$field], $data);
					}
				}
				return $row;
			},
			$groups
		);
		return (new class_table($columns_result, $rows_result));
	}
	
	public function generate()
	{
 ?>
<table class="datatable">
	<thead>
		<tr>
<?php
	array_map
	(
		function ($column)
		{
 ?>
 			<th>
<?php
			echo($column->title);
 ?>
			</th>
<?php
		},
		$this->columns
	);
 ?>
		</tr>
	</thead>
	<tbody>
<?php
	array_map
	(
		function ($row)
		{
 ?>
		<tr>
<?php
			array_map
			(
				function ($column) use (&$row)
				{
 ?>
		 			<td>
<?php
					echo($column->format($column->extract($row)));
 ?>
		 			</td>
<?php
				},
				$this->columns
			);
 ?>
		</tr>
<?php
		},
		$this->rows
	);
 ?>
	</tbody>
</table>
<?php
	}
}
 ?>