<?php
/**
 * This file is part of PHPWord - A pure PHP library for reading and writing
 * word processing documents.
 *
 * PHPWord is free software distributed under the terms of the GNU Lesser
 * General Public License version 3 as published by the Free Software Foundation.
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code. For the full list of
 * contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
 *
 * @see         https://github.com/PHPOffice/PHPWord
 * @copyright   2010-2018 PHPWord contributors
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
 */

// ADD HOSTCMS
namespace PhpOffice\PhpWord\Writer\HTML\Element;

use PhpOffice\PhpWord\Writer\HTML\Style\Table as TableStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Cell as CellStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph as ParagraphStyleWriter;


/**
 * Table element HTML writer
 *
 * @since 0.10.0
 */
class Table extends AbstractElement
{
    /**
     * Write table
     *
     * @return string
     */
	public function write()
	{
		if (!$this->element instanceof \PhpOffice\PhpWord\Element\Table) {
			return '';
		}

		$content = '';
		$rows = $this->element->getRows();
		$rowCount = count($rows);
		if ($rowCount > 0) {
			$tableStyle = self::getTableStyle($this->element->getStyle());

			$content .= '<table cellpadding="0" cellspacing="0"';

			if (is_object($tableStyle)) {
				$styleWriter = new TableStyleWriter($tableStyle);
				$content .= ' style="' . $styleWriter->write() . '"';
			} else {
				$content .= ' class="' . htmlspecialchars($tableStyle) . '"';
			}

			$content .= '>' . PHP_EOL;

			// Массив для отслеживания объединенных ячеек
			$mergedCells = [];

			for ($i = 0; $i < $rowCount; $i++) {
				/** @var $row \PhpOffice\PhpWord\Element\Row Type hint */
				$rowStyle = $rows[$i]->getStyle();
				$height = $rows[$i]->getHeight();
				$tblHeader = $rowStyle->isTblHeader();

				$sTrStyle = '';
				$height && $sTrStyle .= 'height: ' . intval($height * 0.2 / 3) . 'px';

				$content .= '<tr style="' . $sTrStyle . '">' . PHP_EOL;

				$rowCells = $rows[$i]->getCells();
				$rowCellCount = count($rowCells);

				// Учитываем смещение из-за объединенных ячеек из предыдущих строк
				$colOffset = 0;

				for ($j = 0; $j < $rowCellCount; $j++) {
					$cellStyle = $rowCells[$j]->getStyle();
					$width = $rowCells[$j]->getWidth();
					$cellColSpan = $cellStyle->getGridSpan();
					$cellRowSpan = 1;
					$cellVMerge = $cellStyle->getVMerge();

					// Проверяем, не является ли текущая ячейка продолжением объединения из предыдущих строк
					$isMergedFromAbove = false;
					foreach ($mergedCells as $mergedCell) {
						if ($mergedCell['row'] < $i &&
							$mergedCell['col'] == ($j + $colOffset) &&
							$mergedCell['rowSpan'] > ($i - $mergedCell['row'])) {
							$isMergedFromAbove = true;
							break;
						}
					}

					if ($isMergedFromAbove) {
						// Пропускаем ячейку, так как она уже объединена с ячейкой выше
						continue;
					}

					if ($cellVMerge === 'restart') {
						$cellRowSpan = $this->calculateCellRowSpan($rows, $i, $j);

						// Запоминаем объединенную ячейку
						$mergedCells[] = [
							'row' => $i,
							'col' => $j + $colOffset,
							'rowSpan' => $cellRowSpan
						];
					}

					if ($cellVMerge !== 'continue' && $cellVMerge !== '') {
						$cellTag = $tblHeader ? 'th' : 'td';
						$cellColSpanAttr = (is_numeric($cellColSpan) && ($cellColSpan > 1) ? " colspan=\"{$cellColSpan}\"" : '');
						$cellRowSpanAttr = ($cellRowSpan > 1 ? " rowspan=\"{$cellRowSpan}\"" : '');

						$sTdStyle = '';
						$width && $sTdStyle .= 'width: ' . intval($width * 0.2 / 3) . 'px; ';

						// Собираем стили из всех объединенных ячеек
						$mergedStyles = [];
						if ($cellRowSpan > 1) {
							// Собираем стили границ из всех объединенных ячеек
							for ($k = $i; $k < $i + $cellRowSpan; $k++) {
								if (isset($rows[$k])) {
									$kRowCells = $rows[$k]->getCells();
									// Находим соответствующую ячейку в строке с учетом colspan
									$currentColIndex = 0;
									foreach ($kRowCells as $kCell) {
										if ($currentColIndex == $j + $colOffset) {
											$mergedCellStyle = $kCell->getStyle();
											$mergedStyles[] = $mergedCellStyle;
											break;
										}
										$currentColIndex += max(1, $kCell->getStyle()->getGridSpan());
									}
								}
							}
						} else {
							$mergedStyles[] = $cellStyle;
						}

						// Объединяем стили границ
						$finalCellStyle = clone $cellStyle;
						$hasBottomBorder = false;

						foreach ($mergedStyles as $style) {
							if ($style->getBorderBottomSize() > 0 ||
								$style->getBorderBottomColor() !== null ||
								$style->getBorderBottomStyle() !== null) {
								$hasBottomBorder = true;
								// Копируем нижнюю границу из любой ячейки, где она есть
								$finalCellStyle->setBorderBottomSize($style->getBorderBottomSize());
								$finalCellStyle->setBorderBottomColor($style->getBorderBottomColor());
								$finalCellStyle->setBorderBottomStyle($style->getBorderBottomStyle());
							}

							// Также проверяем другие границы
							if ($style->getBorderTopSize() > 0) {
								$finalCellStyle->setBorderTopSize($style->getBorderTopSize());
								$finalCellStyle->setBorderTopColor($style->getBorderTopColor());
								$finalCellStyle->setBorderTopStyle($style->getBorderTopStyle());
							}
							if ($style->getBorderLeftSize() > 0) {
								$finalCellStyle->setBorderLeftSize($style->getBorderLeftSize());
								$finalCellStyle->setBorderLeftColor($style->getBorderLeftColor());
								$finalCellStyle->setBorderLeftStyle($style->getBorderLeftStyle());
							}
							if ($style->getBorderRightSize() > 0) {
								$finalCellStyle->setBorderRightSize($style->getBorderRightSize());
								$finalCellStyle->setBorderRightColor($style->getBorderRightColor());
								$finalCellStyle->setBorderRightStyle($style->getBorderRightStyle());
							}
						}

						// Если в объединенных ячейках не было нижней границы, но нам нужно ее добавить
						if ($cellRowSpan > 1 && !$hasBottomBorder) {
							// Проверяем последнюю объединенную ячейку
							$lastRowIndex = $i + $cellRowSpan - 1;
							if (isset($rows[$lastRowIndex])) {
								$lastRowCells = $rows[$lastRowIndex]->getCells();
								$currentColIndex = 0;
								foreach ($lastRowCells as $lastCell) {
									if ($currentColIndex == $j + $colOffset) {
										$lastCellStyle = $lastCell->getStyle();
										if ($lastCellStyle->getBorderBottomSize() > 0) {
											$finalCellStyle->setBorderBottomSize($lastCellStyle->getBorderBottomSize());
											$finalCellStyle->setBorderBottomColor($lastCellStyle->getBorderBottomColor());
											$finalCellStyle->setBorderBottomStyle($lastCellStyle->getBorderBottomStyle());
										}
										break;
									}
									$currentColIndex += max(1, $lastCell->getStyle()->getGridSpan());
								}
							}
						}

						$styleWriter = new CellStyleWriter($finalCellStyle);
						$sTdStyle .= $styleWriter->write();

						// Добавляем стили параграфа
						$x = $rowCells[$j]->getElement(0);
						if ($x) {
							$paragraphStyle = $x->getParagraphStyle();
							$ParagraphStyleWriter = new ParagraphStyleWriter($paragraphStyle);
							$sTdStyle .= $ParagraphStyleWriter->write();
						}

						$content .= "<{$cellTag}{$cellColSpanAttr}{$cellRowSpanAttr} style=\"" . $sTdStyle . "\">" . PHP_EOL;

						$writer = new Container($this->parentWriter, $rowCells[$j]);
						$insideContent = $writer->write();

						if ($cellRowSpan > 1) {
							// Добавляем контент из объединенных ячеек
							for ($k = $i + 1; $k < $i + $cellRowSpan; $k++) {
								if (isset($rows[$k])) {
									$kRowCells = $rows[$k]->getCells();
									$currentColIndex = 0;
									foreach ($kRowCells as $kCell) {
										if ($currentColIndex == $j + $colOffset) {
											$writer = new Container($this->parentWriter, $kCell);
											$insideContent .= $writer->write();
											break;
										}
										$currentColIndex += max(1, $kCell->getStyle()->getGridSpan());
									}
								}
							}
						}

						// HostCMS
						//if (trim($insideContent) !== '<p>&nbsp;</p>')
						//{
						$insideContent = preg_replace('/(?:<p>&nbsp;<\/p>\s*)+$/i', '', $insideContent);
						//}

						// Default content for empty cell without defined height
						$content .= $insideContent != '' || $height
							? $insideContent
							: '<br />';

						$content .= "</{$cellTag}>" . PHP_EOL;
					}

					// Увеличиваем смещение для учета colspan
					$colOffset += max(1, $cellColSpan) - 1;
				}
				$content .= '</tr>' . PHP_EOL;
			}
			$content .= '</table>' . PHP_EOL;
		}

		return $content;
	}

    /**
     * Translates Table style in CSS equivalent
     *
     * @param string|\PhpOffice\PhpWord\Style\Table|null $tableStyle
     * @return string
     */
    private function getTableStyle($tableStyle = null)
    {
        if ($tableStyle == null) {
            return '';
        }
        if (is_string($tableStyle)) {
            $style = ' class="' . $tableStyle;
        } else {
            $style = ' style="';
            if ($tableStyle->getLayout() == \PhpOffice\PhpWord\Style\Table::LAYOUT_FIXED) {
                $style .= 'table-layout: fixed;';
            } elseif ($tableStyle->getLayout() == \PhpOffice\PhpWord\Style\Table::LAYOUT_AUTO) {
                $style .= 'table-layout: auto;';
            }
        }

        return $style . '"';
    }

    /**
     * Calculates cell rowspan.
     *
     * @param \PhpOffice\PhpWord\Element\Row[] $rows
     */
	private function calculateCellRowSpan(array $rows, int $rowIndex, int $colIndex): int
	{
		$currentRow = $rows[$rowIndex];
		$currentRowCells = $currentRow->getCells();
		$shiftedColIndex = 0;

		// Вычисляем смещенный индекс с учетом colspan в текущей строке
		for ($i = 0; $i < $colIndex; $i++) {
			$cell = $currentRowCells[$i];
			$colSpan = 1;
			if ($cell->getStyle()->getGridSpan() !== null) {
				$colSpan = $cell->getStyle()->getGridSpan();
			}
			$shiftedColIndex += $colSpan;
		}

		$rowCount = count($rows);
		$rowSpan = 1;

		// Проверяем следующие строки на наличие vMerge = 'continue'
		for ($i = $rowIndex + 1; $i < $rowCount; ++$i) {
			$rowCells = $rows[$i]->getCells();
			$currentColIndex = 0;
			$found = false;

			// Ищем ячейку с нужным смещенным индексом
			foreach ($rowCells as $cell) {
				// Если нашли ячейку с нужным смещенным индексом
				if ($currentColIndex === $shiftedColIndex) {
					$vMerge = $cell->getStyle()->getVMerge();
					// Проверяем, является ли ячейка продолжением объединения
					if ($vMerge === 'continue' || $vMerge === '') {
						++$rowSpan;
						$found = true;
					}
					break;
				}

				// Учитываем colspan текущей ячейки
				$colSpan = 1;
				if ($cell->getStyle()->getGridSpan() !== null) {
					$colSpan = $cell->getStyle()->getGridSpan();
				}
				$currentColIndex += $colSpan;
			}

			// Если не нашли ячейку или она не является продолжением - выходим
			if (!$found) {
				break;
			}
		}

		return $rowSpan;
	}
}
