<?php
include_once("misc.php");
include_once("list.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 = array_map
					(
						function ($row) use (&$columns_vertical, &$columns_horizontal, &$columns_data, $data_aggregator)
							{
								$raw_vertical = array_map(function ($column) use (&$row) {return $column->extract($row);}, $columns_vertical);
								$raw_horizontal = array_map(function ($column) use (&$row) {return $column->extract($row);}, $columns_horizontal);
								$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));
				// find groups
				$values = list_clean
					(
						array_map
							(
								function ($row) use (&$columns_source) {return $columns_source[1]->extract($row);},
								$rows_source
							)
					)
				;
				$count = 0;
				$columns_result = array_merge
					(
						[
							new class_column
								(
									fetch
										(
											$configuration,
											"label_vertical",
											function ($columns) {return implode("/", array_map(function ($column) {return $column->title;}, $columns));},
											1
										)
										($columns_vertical)
									,
									"vertical"
								)
						],
						array_map
							(
								function ($value) use (&$configuration, &$columns_horizontal, &$count)
									{
										$count += 1;
										return (
											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-1),
													fetch($configuration, "data_formatter", function ($x) {return json_encode($x);}, 1)
												)
										);
									}
								,
								$values
							)
					)
				;
				$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;
							}
						,
						sql_groups($rows_source, "_vertical")
					)
				;
				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
			}
	}
	
 ?>