<?php
/**
 * Created by PhpStorm.
 * User: danielknudsen
 * Date: 10.01.19
 * Time: 09:14
 */

namespace App\Utils;


use App\ControllerHelper\Tender\TenderHelper;
use App\Entity\EntDinSecondLevel;
use App\Entity\EntDinVersion;
use App\Entity\EntMandator;
use Doctrine\Common\Persistence\ObjectManager;
use Onurb\Bundle\ExcelBundle\Factory\ExcelFactory;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooter;
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Psr\Container\ContainerInterface;
use Symfony\Component\Config\Definition\Exception\Exception;

class Excel
{
    const EXCEL_CREATOR = "PLP Parkhauswerbung";

    const EXCEL_BOOLEAN_WORD_TRUE = 'ja';
    const EXCEL_BOOLEAN_WORD_FALSE = 'nein';
    const EXCEL_BOOLEAN_TRUE = 'x';
    const EXCEL_BOOLEAN_FALSE = '--';

    public static $validCheckboxValues = [
        self::EXCEL_BOOLEAN_WORD_TRUE => 1,
        self::EXCEL_BOOLEAN_WORD_FALSE => 1,
        self::EXCEL_BOOLEAN_TRUE => 1,
        self::EXCEL_BOOLEAN_FALSE => 1,
        '0' => 1,
        '1' => 1,
        '' => 1
    ];

    /**
     * Returns spreadsheet with meta data
     * @param ContainerInterface $container
     * @param null $filename
     * @return Spreadsheet
     */
    public static function createSpreadSheet(ContainerInterface $container, $filename = null)
    {
        /** @var ExcelFactory $phpSpreadsheet */
        $phpSpreadSheet = $container->get('phpspreadsheet');
        /** @var Spreadsheet $spreadSheet */
        $spreadSheet = $phpSpreadSheet->createSpreadsheet($filename);
        $spreadSheet->getProperties()->setCreator(self::EXCEL_CREATOR);
        $spreadSheet->getProperties()->setLastModifiedBy(self::EXCEL_CREATOR);
        //$phpSpreadSheet->createSheet()
        return $spreadSheet;
    }

    /**
     * Returns an excel writer
     * @param ContainerInterface $container
     * @param Spreadsheet $spreadsheet
     * @return \PhpOffice\PhpSpreadsheet\Writer\IWriter
     */
    public static function createWriter(ContainerInterface $container, Spreadsheet $spreadsheet)
    {
        /** @var ExcelFactory $phpSpreadsheet */
        $phpSpreadSheet = $container->get('phpspreadsheet');
        $writer = $phpSpreadSheet->createWriter($spreadsheet, 'Xlsx');
        return $writer;
    }

    /**
     * Returns active sheet
     * @param Spreadsheet $spreadSheet
     * @param null $sheetName
     * @param null $assetDir
     * @param int $activeSheetIndex
     * @return Worksheet
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function getActiveSheet(Spreadsheet $spreadSheet, $sheetName = null, $assetDir = null, $activeSheetIndex = 0)
    {
        $spreadSheet->setActiveSheetIndex($activeSheetIndex);
        $activeSheet = $spreadSheet->getActiveSheet();
        $activeSheet->setTitle($sheetName);
        return $activeSheet;
    }

    /**
     * Check if given excel value is a valid checkbox value
     * @param $value
     * @return bool
     */
    private static function isExcelCheckboxValue($value)
    {
        return array_key_exists($value, self::$validCheckboxValues);
    }

    /**
     * Returns Excel checkbox value
     * @param Worksheet $sheet
     * @param $column
     * @param $row
     * @return bool
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function getExcelCheckboxValue(Worksheet $sheet, $column, $row)
    {
        $value = self::getExcelTextValue($sheet, $column, $row);
        if (!self::isExcelCheckboxValue($value)) {
            throw new Exception('invalid check box value');
        }
        return $value == "1" || $value == "ja" || $value == "x";
    }

    /**
     * Return Excel date value
     * @param Worksheet $sheet
     * @param $column
     * @param $row
     * @return bool|null|string
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function getExcelDateValue(Worksheet $sheet, $column, $row)
    {
        $value = self::getExcelTextValue($sheet, $column, $row);
        if (!is_null($value)) {
            $date = null;
            // Try all valid date formats of too1s
            $date = \DateTime::createFromFormat("d.m.y", $value);
            if ($date !== false) {
                return $date->format('Y-m-d');
            }
            $date = \DateTime::createFromFormat("d.m.Y", $value);
            if ($date !== false) {
                return $date->format('Y-m-d');
            }
            $date = \DateTime::createFromFormat("j.n.y", $value);
            if ($date !== false) {
                return $date->format('Y-m-d');
            }
            $date = \DateTime::createFromFormat("j.n.Y", $value);
            if ($date !== false) {
                return $date->format('Y-m-d');
            }
            throw new Exception('invalid date value'); // No valid date
        }
        return null;
    }

    /**
     * Gets date for excel export (german date format)
     * @param $value
     * @return bool|null|string
     */
    public static function getExcelExportDate($value)
    {
        if (!is_null($value)) {
            $date = \DateTime::createFromFormat("Y-m-d", $value);
            if ($date !== false) {
                return $date->format('d.m.Y');
            }
            return false; // No valid date
        }
        return null;
    }

    /**
     * Returns text value of Excel sheet
     * @param Worksheet $sheet
     * @param $column
     * @param $row
     * @param bool $stringResult
     * @return null|string
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function getExcelTextValue(Worksheet $sheet, $column, $row, $stringResult = false)
    {
        $column = self::convertColumnToIndex($column);
        $value = $sheet->getCell($column.$row);
        return $value == "" ? ($stringResult ? "" : null) : trim($value);
    }

    /**
     * Returns integer value of Excel sheet
     * @param Worksheet $sheet
     * @param $column
     * @param $row
     * @return float|null
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function getExcelIntValue(Worksheet $sheet, $column, $row)
    {
        $value = self::getExcelTextValue($sheet, $column, $row);
        if (!is_null($value)) {
            $value = intval($value);
            if (!Utils::isIntValue($value)) {
                throw new Exception('invalid integer value');
            }
            return $value;
        }
        return null;
    }

    /**
     * Returns float value of Excel sheet
     * @param Worksheet $sheet
     * @param $column
     * @param $row
     * @return float|null
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function getExcelFloatValue(Worksheet $sheet, $column, $row)
    {
        $value = self::getExcelTextValue($sheet, $column, $row);
        if (!is_null($value)) {
            if (!Utils::isFloatValue($value)) {
                throw new Exception('invalid float value');
            }
            return Utils::getFloatValue($value);
        }
        return null;
    }

    /**
     * @param $value
     * @return string
     */
    public static function getBooleanExcelValue($value)
    {
        return $value === true ? self::EXCEL_BOOLEAN_TRUE : self::EXCEL_BOOLEAN_FALSE;
    }

    /**
     * @param $value
     * @return string
     */
    public static function getBooleanExcelValueWord($value)
    {
        return $value === true ? 'x' : '';
    }

    /**
     * Converts numeric column index to string column
     * @param $column
     * @return string
     */
    public static function convertColumnToIndex($column)
    {
        return is_numeric($column) ? Coordinate::stringFromColumnIndex($column) : $column;
    }

    /**
     * Converts string column index to numeric column index
     * @param $columnIndex
     * @return int|string
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function convertIndexToColumn($columnIndex)
    {
        return !is_numeric($columnIndex) ? Coordinate::columnIndexFromString($columnIndex) : $columnIndex;
    }

    /**
     * Returns calculated value of Excel sheet
     * @param Worksheet $sheet
     * @param $column
     * @param $row
     * @param bool $stringResult
     * @return null|string
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function getExcelCalculatedValue(Worksheet $sheet, $column, $row, $stringResult = false)
    {
        $column = self::convertColumnToIndex($column);
        $value = $sheet->getCell($column.$row)->getCalculatedValue();
        return $value;
    }

    /**
     * Sets a column of given worksheet with optional header content and border etc.
     * @param Worksheet $worksheet
     * @param $column
     * @param int $width
     * @param null $headerContent
     * @param null $bordersArray
     * @param int $colRow
     * @param null $colAlign
     * @param null $colCellAlign
     * @param bool $wrapText
     * @param null $colCellColor
     * @param null $colCellFontColor
     * @param bool $bold
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setColumn(Worksheet $worksheet, $column, $width = 10, $headerContent = null,
                                     $bordersArray = null, $colRow = 1, $colAlign = null, $colCellAlign = null,
                                     $wrapText = true, $colCellColor = null, $colCellFontColor = null, $bold = false)
    {
        $column = self::convertColumnToIndex($column);
        $worksheet->getColumnDimension($column)->setWidth($width);
        $cell = $column.$colRow;
        !is_null($headerContent) ? $worksheet->getCell($cell)->setValue($headerContent) : null;
        !is_null($bordersArray) ?  $worksheet->getStyle($cell)->applyFromArray($bordersArray) : null;
        !is_null($colAlign) ? $worksheet->getStyle($column.":".$column)->getAlignment()->setHorizontal($colAlign) : null;
        !is_null($colCellAlign) ? $worksheet->getStyle($cell)->getAlignment()->setHorizontal($colCellAlign) : null;
        $worksheet->getStyle($cell)->getAlignment()->setWrapText($wrapText);

        !is_null($colCellColor) ? $worksheet->getStyle($cell)->getFill()->setFillType(Fill::FILL_SOLID): null;
        !is_null($colCellColor) ? $worksheet->getStyle($cell)->getFill()->getStartColor()->setARGB($colCellColor): null;
        !is_null($colCellFontColor) ? $worksheet->getStyle($cell)->getFont()->getColor()->setARGB($colCellFontColor): null;
        $worksheet->getStyle($cell)->getFont()->setBold($bold);
    }

    /**
     * Sets a cell value of given worksheet
     * @param Worksheet $worksheet
     * @param $column
     * @param $row
     * @param $value
     * @param null $cellColor
     * @param null $cellFontColor
     * @param bool $bold
     * @param bool $wrapText
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setCellValue(Worksheet $worksheet, $column, $row, $value,
                                        $cellColor = null, $cellFontColor = null, $bold = false, $wrapText = true)
    {
        $column = self::convertColumnToIndex($column);
        $cell = $column.$row;
        self::setCellValueSimple($worksheet, $column, $row, $value);
        !is_null($cellColor) ? $worksheet->getStyle($cell)->getFill()->setFillType(Fill::FILL_SOLID): null;
        !is_null($cellColor) ? $worksheet->getStyle($cell)->getFill()->getStartColor()->setARGB($cellColor): null;
        !is_null($cellFontColor) ? $worksheet->getStyle($cell)->getFont()->getColor()->setARGB($cellFontColor): null;
        $worksheet->getStyle($cell)->getFont()->setBold($bold);
        $worksheet->getStyle($cell)->getAlignment()->setWrapText($wrapText);
    }

    /**
     * Just set value
     * @param Worksheet $worksheet
     * @param $column
     * @param $row
     * @param $value
     * @param bool $bAsPlainText
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setCellValueSimple(Worksheet $worksheet, $column, $row, $value, $bAsPlainText = false)
    {
        $column = self::convertColumnToIndex($column);
        if ($bAsPlainText) {
            // @TODO: this is a hack to force excel to render value not as number
            $worksheet->getCell($column.$row)->setValue(strval($value)." ");
            self::setFormatCode($worksheet, $column, $row, $column, $row, NumberFormat::FORMAT_TEXT);
        } else {
            $worksheet->getCell($column.$row)->setValue($value);
        }
    }

    /**
     * Sets cells unlocked (need to implement: $activeSheet->getProtection()->setSheet(true); )
     * @param Worksheet $worksheet
     * @param $startCol
     * @param $startRow
     * @param $endCol
     * @param $endRow
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setUnLocked(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $startCol = is_numeric($startCol) ? Coordinate::stringFromColumnIndex($startCol) : $startCol;
        $endCol = is_numeric($endCol) ? Coordinate::stringFromColumnIndex($endCol) : $endCol;
        $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
    }

    /**
     * Sets Conditional
     * @param Worksheet $worksheet
     * @param $condition_type
     * @param $operator_type
     * @param $condition_value
     * @param $startCol
     * @param $startRow
     * @param null $endCol
     * @param null $endRow
     * @param string $background_color
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setConditional(Worksheet $worksheet, $condition_type, $operator_type, $condition_value,
                                          $startCol, $startRow, $endCol = null, $endRow = null, $background_color = Color::COLOR_WHITE)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $conditional1 = new Conditional();
        $conditional1->setConditionType($condition_type);
        $conditional1->setOperatorType($operator_type);
        $conditional1->addCondition($condition_value);
        $conditional1->getStyle()->getFill()->setFillType(Fill::FILL_SOLID);
        $conditional1->getStyle()->getFill()->getEndColor()->setARGB($background_color);

        $conditionalStyles = $worksheet->getStyle($startCol.$startRow)->getConditionalStyles();
        $conditionalStyles[] = $conditional1;

        if (!is_null($endCol) && !is_null($endRow)) {
            $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->setConditionalStyles($conditionalStyles);
        } else {
            $worksheet->getStyle($startCol.$startRow)->setConditionalStyles($conditionalStyles);
        }
    }

    /**
     * Merges cells of given range
     * @param Worksheet $worksheet
     * @param $startCol
     * @param $endCol
     * @param $startRow
     * @param $endRow
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function mergeCells(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $worksheet->mergeCells($startCol.$startRow.':'.$endCol.$endRow);
    }

    /**
     * Unmerges cells of given range
     * @param Worksheet $worksheet
     * @param $startCol
     * @param $startRow
     * @param $endCol
     * @param $endRow
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function unMergeCells(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $worksheet->unmergeCells($startCol.$startRow.':'.$endCol.$endRow);
    }

    /**
     * Sets color for cell range
     * @param Worksheet $worksheet
     * @param $startCol
     * @param $startRow
     * @param $endCol
     * @param $endRow
     * @param string $color
     * @param string $fontColor
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setRangeColor(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow,
                                         $color = Color::COLOR_WHITE, $fontColor = Color::COLOR_BLACK)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getFill()->setFillType(Fill::FILL_SOLID);
        $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getFill()->getStartColor()->setARGB($color);
        $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getFont()->getColor()->setARGB($fontColor);
    }

    /**
     * Sets range border
     * @param Worksheet $worksheet
     * @param $startCol
     * @param $startRow
     * @param $endCol
     * @param $endRow
     * @param $bordersArray
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setRangeBorder(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow, $bordersArray)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->applyFromArray($bordersArray);
    }

    /**
     * Sets format code
     * @param Worksheet $worksheet
     * @param $startCol
     * @param $startRow
     * @param $endCol
     * @param $endRow
     * @param $formatCode
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setFormatCode(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow, $formatCode)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getNumberFormat()->setFormatCode($formatCode);
    }

    /**
     * Sets styles from style array
     * @param Worksheet $worksheet
     * @param $startCol
     * @param $startRow
     * @param $endCol
     * @param $endRow
     * @param $styleArray
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     */
    public static function setStyleArray(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow, $styleArray)
    {
        $startCol = self::convertColumnToIndex($startCol);
        $endCol = self::convertColumnToIndex($endCol);
        $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->applyFromArray($styleArray);
    }
}