<?php
namespace CodeIgniter\Database;
abstract class BaseResult implements ResultInterface
{
	
	public $connID;
	
	public $resultID;
	
	public $resultArray = [];
	
	public $resultObject = [];
	
	public $customResultObject = [];
	
	public $currentRow = 0;
	
	public $numRows;
	
	public $rowData;
	
	public function __construct(&$connID, &$resultID)
	{
		$this->connID   = $connID;
		$this->resultID = $resultID;
	}
	
	public function getResult(string $type = 'object'): array
	{
		if ($type === 'array')
		{
			return $this->getResultArray();
		}
		elseif ($type === 'object')
		{
			return $this->getResultObject();
		}
		return $this->getCustomResultObject($type);
	}
	
	public function getCustomResultObject(string $className)
	{
		if (isset($this->customResultObject[$className]))
		{
			return $this->customResultObject[$className];
		}
		if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
		{
			return [];
		}
			$_data = null;
		if (($c = count($this->resultArray)) > 0)
		{
			$_data = 'result_array';
		}
		elseif (($c = count($this->resultObject)) > 0)
		{
			$_data = 'result_object';
		}
		if ($_data !== null)
		{
			for ($i = 0; $i < $c; $i ++)
			{
				$this->customResultObject[$className][$i] = new $className();
				foreach ($this->{$_data}[$i] as $key => $value)
				{
					$this->customResultObject[$className][$i]->$key = $value;
				}
			}
			return $this->customResultObject[$className];
		}
		is_null($this->rowData) || $this->dataSeek(0);
		$this->customResultObject[$className] = [];
		while ($row = $this->fetchObject($className))
		{
			if (method_exists($row, 'syncOriginal'))
			{
				$row->syncOriginal();
			}
			$this->customResultObject[$className][] = $row;
		}
		return $this->customResultObject[$className];
	}
	
	public function getResultArray(): array
	{
		if (! empty($this->resultArray))
		{
			return $this->resultArray;
		}
					if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
		{
			return [];
		}
		if ($this->resultObject)
		{
			foreach ($this->resultObject as $row)
			{
				$this->resultArray[] = (array) $row;
			}
			return $this->resultArray;
		}
		is_null($this->rowData) || $this->dataSeek(0);
		while ($row = $this->fetchAssoc())
		{
			$this->resultArray[] = $row;
		}
		return $this->resultArray;
	}
	
	public function getResultObject(): array
	{
		if (! empty($this->resultObject))
		{
			return $this->resultObject;
		}
					if (is_bool($this->resultID) || ! $this->resultID || $this->numRows === 0)
		{
			return [];
		}
		if ($this->resultArray)
		{
			foreach ($this->resultArray as $row)
			{
				$this->resultObject[] = (object) $row;
			}
			return $this->resultObject;
		}
		is_null($this->rowData) || $this->dataSeek(0);
		while ($row = $this->fetchObject())
		{
			if (method_exists($row, 'syncOriginal'))
			{
				$row->syncOriginal();
			}
			$this->resultObject[] = $row;
		}
		return $this->resultObject;
	}
	
	public function getRow($n = 0, string $type = 'object')
	{
		if (! is_numeric($n))
		{
					is_array($this->rowData) || $this->rowData = $this->getRowArray(0);
					if (empty($this->rowData) || ! array_key_exists($n, $this->rowData))
			{
				return null;
			}
			return $this->rowData[$n];
		}
		if ($type === 'object')
		{
			return $this->getRowObject($n);
		}
		elseif ($type === 'array')
		{
			return $this->getRowArray($n);
		}
		return $this->getCustomRowObject($n, $type);
	}
	
	public function getCustomRowObject(int $n, string $className)
	{
		isset($this->customResultObject[$className]) || $this->getCustomResultObject($className);
		if (empty($this->customResultObject[$className]))
		{
			return null;
		}
		if ($n !== $this->currentRow && isset($this->customResultObject[$className][$n]))
		{
			$this->currentRow = $n;
		}
		return $this->customResultObject[$className][$this->currentRow];
	}
	
	public function getRowArray(int $n = 0)
	{
		$result = $this->getResultArray();
		if (empty($result))
		{
			return null;
		}
		if ($n !== $this->currentRow && isset($result[$n]))
		{
			$this->currentRow = $n;
		}
		return $result[$this->currentRow];
	}
	
	public function getRowObject(int $n = 0)
	{
		$result = $this->getResultObject();
		if (empty($result))
		{
			return null;
		}
		if ($n !== $this->customResultObject && isset($result[$n]))
		{
			$this->currentRow = $n;
		}
		return $result[$this->currentRow];
	}
	
	public function setRow($key, $value = null)
	{
			if (! is_array($this->rowData))
		{
			$this->rowData = $this->getRowArray(0);
		}
		if (is_array($key))
		{
			foreach ($key as $k => $v)
			{
				$this->rowData[$k] = $v;
			}
			return;
		}
		if ($key !== '' && $value !== null)
		{
			$this->rowData[$key] = $value;
		}
	}
	
	public function getFirstRow(string $type = 'object')
	{
		$result = $this->getResult($type);
		return (empty($result)) ? null : $result[0];
	}
	
	public function getLastRow(string $type = 'object')
	{
		$result = $this->getResult($type);
		return (empty($result)) ? null : $result[count($result) - 1];
	}
	
	public function getNextRow(string $type = 'object')
	{
		$result = $this->getResult($type);
		if (empty($result))
		{
			return null;
		}
		return isset($result[$this->currentRow + 1]) ? $result[++ $this->currentRow] : null;
	}
	
	public function getPreviousRow(string $type = 'object')
	{
		$result = $this->getResult($type);
		if (empty($result))
		{
			return null;
		}
		if (isset($result[$this->currentRow - 1]))
		{
			-- $this->currentRow;
		}
		return $result[$this->currentRow];
	}
	
	public function getUnbufferedRow(string $type = 'object')
	{
		if ($type === 'array')
		{
			return $this->fetchAssoc();
		}
		elseif ($type === 'object')
		{
			return $this->fetchObject();
		}
		return $this->fetchObject($type);
	}
	
	abstract public function getFieldCount(): int;
	
	abstract public function getFieldNames(): array;
	
	abstract public function getFieldData(): array;
	
	abstract public function freeResult();
	
	abstract public function dataSeek(int $n = 0);
	
	abstract protected function fetchAssoc();
	
	abstract protected function fetchObject(string $className = 'stdClass');
}