<?php
namespace App\Utils\CSV\Reader;

abstract class AbstractReader
{
	use \App\Utils\BaseParser\OptionTrait;


	/**
	 * Asscoative field mappings. Used to define a spreadsheet header, if any
	 * @var \App\Utils\CSV\Reader\Row
	 */
	protected $assocMappings = null;

	/**
	 * The offset to the current line
	 * @var int
	 */
	protected $currentLineIndex = null;

	/**
	 * The file path
	 * @var string
	 */
	protected $filePath;

	/**
	 * A validator class
	 * @var \App\Utils\CSV\Reader\RowValidator
	 */
	public $validator = null;

	/**
	 * A formatter class
	 * @var \App\Utils\CSV\Reader\RowFormatter
	 */
	public $formatter = null;

	/**
	 * Construct a new AbstractReader instance.
	 *
	 */
	public function __construct()
	{
		$this->validator = new \App\Utils\CSV\Reader\RowValidator($this);
		$this->formatter = new \App\Utils\CSV\Reader\RowFormatter($this);
	}

	public function __destruct()
	{
		$this->closeFile();
	}

	/**
	 * Open the file, save it internally
	 * @param string $filePath
	 * @return $this
	 */
	public function openFile(string $filePath)
	{
		$this->filePath = $filePath;
		$this->_openFile($this->filePath);
		return $this;
	}

	/**
	 * Close the file
	 * @return $this
	 */
	public function closeFile()
	{
		$this->_closeFile($this->filePath);
		$this->filePath = null;
		return $this;
	}

	/**
	 * Set the associative mappings expected
	 * @param \App\Utils\CSV\Reader\Row $row
	 * @return $this
	 */
	public function setAssocMappings(\App\Utils\CSV\Reader\Row $row)
	{
		$this->assocMappings = $row;
		return $this;
	}

	/**
	 * Get the internal assocMappings row
	 * @return \App\Utils\CSV\Reader\Row
	 */
	public function getAssocMappings()
	{
		return $this->assocMappings;
	}

	/**
	 * Get the offset of the current line
	 * @return int
	 */
	public function getCurentLineIndex()
	{
		return $this->currentLineIndex;
	}

	/**
	 * Get the next row value
	 * @return \App\Utils\CSV\Reader\Row
	 */
	public function next(bool $validate=true)
	{
		$row = $this->_next();

		if ($row && $this->assocMappings) {
			$row->setAssocMap($this->assocMappings->getRawFields());
		}

		if ($validate && $row) {
			$this->validator->validateRow($row);
		}

		return $row;
	}

	/**
	 * Get all remaining rows in an associative array
	 * @return array
	 */
	public function nextAll()
	{
		$return = [];
		while ($row = $this->next()) {
			$return[] = $row->getFields();
		}
		return $return;
	}

	/**
	 * Skip some rows
	 * @param number $numberOfRows
	 * @param bool $validate
	 * @return $this
	 */
	public function skipRows(int $numberOfRows=1, bool $validate=true)
	{
		while ($numberOfRows--) {
			$this->next($validate);
		}
		return $this;
	}

	/**
	 * Set the header row, usually read from the file
	 * Its used by \App\Utils\CSV\Reader\Reader, so i moved it here
	 * @param \App\Utils\CSV\Reader\Row $row
	 * @param bool $validate
	 * @return $this
	 */
	public function setHeaderRow(\App\Utils\CSV\Reader\Row $row, bool $validate=true)
	{
		if ($row && $row->hasData() && $row->trimValues()->countValues() == $row->trimValues()->countValidValues()) {
			$this->setAssocMappings($row);
		}

		// if we have a list of expected columns, then we can assume them to be header names, and we can check if they are all here
		if ($this->validator->getExpectedColumns()) {
			$row->setAssocMap($row->getRawFields());
			if ($validate) {
				$this->validator->validateRow($row);
			}
			$row->removeAssocMap();

			$this->setAssocMappings($row);
		}

		return $this;
	}

	/**
	 * Set the list of expected columns
	 * @param array $expectedColumns
	 * @param string $mode
	 * @return $this
	 */
	public function setExpectedColumns(array $expectedColumns, string $mode='strict')
	{
		$this->validator->setExpectedColumns($expectedColumns, $mode);
		return $this;
	}

	/**
	 * Set the used column formats
	 * @param array $columnFormats
	 * @return $this
	 */
	public function setColumnFormats(array $columnFormats)
	{
		$this->validator->setColumnFormats($columnFormats);
		$this->formatter->setColumnFormats($columnFormats);
		return $this;
	}

	/**
	 * This has to be implemented by extending classes
	 * @param string $file
	 */
	abstract protected function _openFile($file);

	/**
	 * This has to be implemented by extending classes
	 * @param string $file
	 */
	abstract protected function _closeFile();

	/**
	 * This has to be implemented by extending classes
	 * @param string $file
	 * @return \App\Utils\CSV\Reader\Row
	 */
	abstract protected function _next();

}


