<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 8/7/18
 * Time: 11:32 AM
 *
 * Check: CODING.md > Download
 */

namespace IMATHUZH\Qfq\Core\Report;

use IMATHUZH\Qfq\Core\Helper\HelperFile;
use IMATHUZH\Qfq\Core\Helper\OnString;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

const COLUMN = 0;
const ROW = 1;

/**
 * Class Excel
 * @package qfq
 */
class Excel {

    /**
     * @param array $files
     * @param array $data
     * @return string
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     * @throws \downloadException
     */
    public function process(array $files, array $data, array $cache = array()) {

        // Check that there are some sources
        if (empty($files) && empty($data)) {
            throw new \downloadException("No source found to offer to download", ERROR_DOWNLOAD_NOTHING_TO_DO);
        }

        // Check if there are only File(s) given: return them.
        if (empty($data)) {
            if (count($files) == 1) {
                return $files[0]; // Return the given file.
            } else {
                throw new \downloadException("Only one source file per Excel download supported", ERROR_DOWNLOAD_UNEXPECTED_NUMBER_OF_SOURCES);
            }
        }

        // Use Template or NEW
        if (empty($files[0])) {
            // Create new
            $spreadsheet = new Spreadsheet();
        } else {
            // Open template
            $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($files[0]);
        }

        // Iterate over all sources.
        foreach ($data as $page) {
            $spreadsheet = $this->fillSpreadsheet($spreadsheet, $page);
        }

        if (($cache[DOWNLOAD_CACHE_STATUS] ?? '') === DOWNLOAD_CACHE_STATUS_CREATE) {
            $filename = $cache[DOWNLOAD_CACHE_PATH];
        } else {
            $filename = HelperFile::tempnam();
        }

        $writer = new Xlsx($spreadsheet);
        $writer->save($filename);

        return $filename;
    }

    /**
     * @param Spreadsheet $spreadsheet
     * @param string $data
     * @return Spreadsheet
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws \downloadException
     */
    private function fillSpreadsheet(Spreadsheet $spreadsheet, $data) {

        $worksheet = $spreadsheet->getActiveSheet();
        $pos = 'A1';
        if (!OnString::splitExcelPos($pos, $posColumn, $posRow)) {
            throw new \downloadException("Invalid cell coordinates: " . $pos, ERROR_EXCEL_INVALID_COORDINATES);
        }
        $posColumnStart = $posColumn;
        $arr = explode(PHP_EOL, $data);
        foreach ($arr as $line) {

            $line = trim($line);

            // Skip empty /comment lines
            if (empty($line) || $line[0] == TOKEN_COMMENT) {
                continue;
            }

            $token = explode('=', $line, 2);
            $key = trim($token[0] ?? '');
            $value = trim($token[1] ?? '');

            switch ($key) {
                case EXCEL_WORKSHEET:
                    throw new \downloadException("Not implemented: $key", ERROR_NOT_IMPLEMENTED);

                case EXCEL_MODE:
                    throw new \downloadException("Not implemented: $key", ERROR_NOT_IMPLEMENTED);

                case EXCEL_POSITION:

                    if (empty($value)) {
                        throw new \downloadException("Position argument is empty", ERROR_EXCEL_POSITION_ARGUMENT_EMPTY);
                    }

                    if (!OnString::splitExcelPos($value, $posColumn, $posRow)) {
                        throw new \downloadException("Invalid cell coordinates: " . $value, ERROR_EXCEL_INVALID_COORDINATES);
                    }
                    $posColumnStart = $posColumn;
                    break;

                case EXCEL_NEWLINE:
                    $posRow++;
                    $posColumn = $posColumnStart;
                    break;

                case EXCEL_STRING:
                case EXCEL_STRING2:
                case EXCEL_FORMULA:
                case EXCEL_NUMERIC:
                case EXCEL_BASE64:
                case EXCEL_NULL:
                case EXCEL_INLINE:
                case EXCEL_ERROR:
                    if ($key == EXCEL_BASE64) {
                        $value = base64_decode($value);
                        $key = EXCEL_STRING;
                    }

                    // if type numeric is not a number replace wit '' or 'not a number'
                    if ($key === EXCEL_NUMERIC && !is_numeric($value)) {
                        $value = (ctype_space($value) || $value === '') ? '' : 'not a number';
                        $key = EXCEL_STRING;
                    }

                    $spreadsheet->getActiveSheet()
                        ->setCellValueExplicit(
                            $posColumn . $posRow,
                            $value,
                            $key
                        );
                    $posColumn = $this->nextColumn($posColumn);
                    break;

                default:
                    $errorMsg[ERROR_MESSAGE_TO_USER] = 'Excel Export: unknown token';
                    $errorMsg[ERROR_MESSAGE_TO_DEVELOPER] = 'Unknown Token: ' . $key;

                    throw new \downloadException(json_encode($errorMsg), ERROR_UNKNOWN_TOKEN);
            }
        }

        return $spreadsheet;
    }

    /**
     * Increment the alpha string. i.e. count using A-Z as base.
     * 'A' > 'B'
     * 'Z' > 'AA'
     * 'AA' > 'AB'
     * 'AGTC' > 'AGTD'
     *
     * @param $column
     * @return string - incremented column.
     */
    private function nextColumn($column) {
        return ++$column;
    }
}
