<?php
namespace nur\v\bs3\vc;

use Exception;
use nur\b\params\Tparametrable;
use nur\func;
use nur\iter;
use nur\v\v;
use nur\v\vo;

/**
 * Class CVerticalTable: comme {@link CTable} mais les données sont affichées
 * de façon verticale
 */
class CVerticalTable extends CTable {
  use Tparametrable;

  const TABLE_CLASS = "table-bordered table-auto";

  const PARAMETRABLE_PARAMS_SCHEMA = [
    "before_row" => ["?content", null, "Contenu à afficher pour chaque ligne avant le tableau s'il y a des lignes à afficher"],
    "after_row" => ["?content", null, "Contenu à afficher pour chaque ligne après le tableau s'il y a des lignes à afficher"],
  ];

  /** @var array|string */
  protected $ppBeforeRow;

  /** @var array|string */
  protected $ppAfterRow;

  function print(): void {
    try {
      $this->_rewindRows();
      $this->rowIndex = 0;
      $haveRows = false;
      while ($this->_validRow()) {
        $this->rawRow = $this->_currentRow($this->rowKey);
        try {
          $this->origRow = $this->nextRow();
          if ($this->origRow === null) continue;

          $skipRow = false;
          if (!$haveRows) $skipRow = $this->resolveColsHeaders($this->origRow);
          if (!$skipRow) {
            $this->row = $this->cookRow($this->origRow);
            if (!$haveRows) {
              $haveRows = true;
              vo::print($this->beforeTable());
              vo::stable(["class" => "table", $this->table()]);
              vo::stbody($this->tbody());
            }
            $this->printBeforeRow();
            $this->printRow($this->row);
            $this->printAfterRow();
            $this->rowIndex++;
          }
        } finally {
          $this->_nextRow();
        }
      }
      if ($haveRows) {
        vo::etbody();
        vo::etable();
        vo::print($this->afterTable());
      } else {
        vo::print($this->noData());
      }
    } catch (Exception $e) {
      $onException = $this->ppOnException;
      if ($onException !== null) func::call($onException, $e);
      else throw $e;
    }
  }

  function tr(bool $rowContent): ?iterable {
    return null;
  }

  /** contenu à afficher avant le tableau s'il y a des lignes à afficher */
  function beforeRow(): ?iterable {
    return $this->ppBeforeRow;
  }

  function printBeforeRow(): void {
    $beforeRowContents = $this->beforeRow();
    if ($beforeRowContents !== null) {
      vo::tr([
        $this->tr(false),
        v::td([
          "colspan" => 2,
          $beforeRowContents,
        ]),
      ]);
    }
  }

  /** contenu à afficher après le tableau s'il y a des lignes à afficher */
  function afterRow(): ?iterable {
    return $this->ppAfterRow;
  }

  function printAfterRow(): void {
    $afterRowContents = $this->afterRow();
    if ($afterRowContents !== null) {
      vo::tr([
        $this->tr(false),
        v::td([
          "colspan" => 2,
          $afterRowContents,
        ]),
      ]);
    }
  }

  function printRow(array $row): void {
    $this->index = 0;
    $headers = $this->headers;
    $headerCol = 0;
    $headerIndex = 0;
    foreach ($this->cols as $this->col) {
      vo::str($this->tr(true));
      if ($headerCol == $this->index) {
        if ($headerCol == 0) iter::rewind($headers);
        else iter::next($headers);
        if (iter::valid($headers)) {
          $header = iter::current($headers, $key);
          $label = $header["label"];
          if ($label === null) $label = $header["name"];
          $rowspan = $header["span"];

          if ($key === $headerIndex) {
            $headerIndex++;
            $headerCol++;
          } else {
            $headerCol += $rowspan;
          }
          vo::print($this->colTh($label, $rowspan));
        }
      }
      $value = $this->col !== null? $row[$this->col]: null;
      vo::print($this->colTd($value));
      vo::etr();
      $this->index++;
    }
  }

  function colTh($header, ?int $rowspan): array {
    return v::th([
      "rowspan" => $rowspan,
      $header,
    ]);
  }
}