<?php
namespace App\Utils\BaseParser\Utils;

/**
 * Parse passenger names
 * @author Lucian
 */
class PassengerNameParser
{
	use \App\Utils\BaseParser\OptionTrait;

	/**
	 * The input text
	 * @var string
	 */
	protected $text = null;

	/**
	 * The list of possible suffixes and how should they be interpreted
	 * 	format:
	 * 		key -
	 * @var array
	 */
	protected $suffixes = [
		'MR' => 	['mr', 'm', ],
		'MRS' => 	['mrs', 'f', ],
		'MS' => 	['ms', 'f', ],
		# MSTR
		# MISS
		# CHD
		# INF
	];

	public function __construct()
	{
		// do nothing
		// if we dont define the constructor, we will get an error: 'Fatal error: Cannot call constructor' in all extending classes

		$this->options['allowInlineSuffix'] = true;	// allow suffixes like "ANAMS" instead of "ANA MS". This is the last tried match before giving up on suffixes
		$this->options['extractSuffix'] = true;		// extract the suffix
		$this->options['handleInlineSlashAndSpaceDelimiters'] = true;	// handle slash and space at the same time. Used for names like 'NICULESCU RACHIERU/DIANA MARIA'
	}

	/**
	 * Set the text to parse
	 * @param string $text
	 * @return $this
	 */
	public function setText($text)
	{
		$this->text = $text;
		return $this;
	}

	/**
	 * Reset the suffix list
	 * @return $this
	 */
	public function resetSuffixes()
	{
		$this->suffixes = [];
		return $this;
	}

	/**
	 * Add a suffix to the suffix list
	 * @param string $text
	 * @param string $title - mr/ms/mrs
	 * @param string $type - m/f
	 * @return $this
	 */
	public function addSuffix($text, $title, $type = null)
	{
		$this->suffixes[$text] = [$title, $type];
		return $this;
	}

	protected function doExtract(\App\Utils\BaseParser\Utils\Types\Passenger $ret)
	{
		$ret->isOwner = null;	// this defaults to 'true'. reset it

		$extractor = new \App\Utils\BaseParser\Utils\Extractor\Single();

		// format: ANA/ARE/MERE/PERE
		// format: ANA/MERE/PERE
		// format: ANA/ARE
		// format: ANA

		// format: ANA ARE MERE PERE
		// format: ANA ARE MERE
		// format: ANA ARE
		// format: ANA

		$xtrList = [];

		// handle inline slash and space format
		if ($this->options['handleInlineSlashAndSpaceDelimiters'] && $extractor->contains("/[\/]/ui", $this->text) && $extractor->contains("/[\s]/ui", $this->text)) {
			$xtrList[]= $extractor->find("/^"
				."(?P<lastName>[^\/]+)"
				."([\s\/]+(?P<nm1>[^\s]+))?"
				."([\s\/]+(?P<nm2>[^\s]+))?"
				."([\s\/]+(?P<nm3>[^\s]+))?"
				."([\s\/]+(?P<nm4>[^\s]+))?"
				."([\s\/]+(?P<nm5>[^\s]+))?"
				."([\s\/]+(?P<nm6>[^\s]+))?"
				."([\s\/]+(?P<nm7>[^\s]+))?"
				."([\s\/]+(?P<nm8>[^\s]+))?"
				."([\s\/]+(?P<nm9>[^\s]+))?"
				."$/ui", $this->text);
		}

		// default formats
		if (!$xtrList) {
			$xtrList[]= $extractor->find("/^"
				."(?P<lastName>[^\/]+)"
				."(\/(?P<nm1>[^\/]+))?"
				."(\/(?P<nm2>[^\/]+))?"
				."(\/(?P<nm3>[^\/]+))?"
				."(\/(?P<nm4>[^\/]+))?"
				."(\/(?P<nm5>[^\/]+))?"
				."(\/(?P<nm6>[^\/]+))?"
				."(\/(?P<nm7>[^\/]+))?"
				."(\/(?P<nm8>[^\/]+))?"
				."(\/(?P<nm9>[^\/]+))?"
				."$/ui", $this->text);

			$xtrList[]= $extractor->find("/^"
				."[\s]*(?P<lastName>[^\s]+)"
				."(\s+(?P<nm1>[^\s]+))?"
				."(\s+(?P<nm2>[^\s]+))?"
				."(\s+(?P<nm3>[^\s]+))?"
				."(\s+(?P<nm4>[^\s]+))?"
				."(\s+(?P<nm5>[^\s]+))?"
				."(\s+(?P<nm6>[^\s]+))?"
				."(\s+(?P<nm7>[^\s]+))?"
				."(\s+(?P<nm8>[^\s]+))?"
				."(\s+(?P<nm9>[^\s]+))?"
				."[\s]*$/ui", $this->text);

			$xtrList[]= $extractor->find("/^"
				."(?P<lastName>[^\s\/]+)"
				."([\s\/](?P<nm1>[^\s\/]+))?"
				."([\s\/](?P<nm2>[^\s\/]+))?"
				."([\s\/](?P<nm3>[^\s\/]+))?"
				."([\s\/](?P<nm4>[^\s\/]+))?"
				."([\s\/](?P<nm5>[^\s\/]+))?"
				."([\s\/](?P<nm6>[^\s\/]+))?"
				."([\s\/](?P<nm7>[^\s\/]+))?"
				."([\s\/](?P<nm8>[^\s\/]+))?"
				."([\s\/](?P<nm9>[^\s\/]+))?"
				."$/ui", $this->text);
		}

		usort($xtrList, function($a, $b) {
			return $b->getRealCount() - $a->getRealCount();
		});


		$xtr = reset($xtrList);	// $xtr = extracted data, picks the match with the most matches done

		if ($xtr->hasValue()) {
			$names = array_filter(array_map(function($nm){
					return trim((string)$nm);
				}, [$xtr->nm1, $xtr->nm2, $xtr->nm3, $xtr->nm4, $xtr->nm5, $xtr->nm6, $xtr->nm7, $xtr->nm8, $xtr->nm9, ]));

			// extract suffix
			$lname = end($names);

			// format: MR
			// format: MS
			if ($this->options['extractSuffix'] && in_array($lname, array_keys($this->suffixes))) {
				$ret->title = $this->suffixes[$lname][0];
				$ret->type = $this->suffixes[$lname][1];
				$names = array_slice($names, 0, -1);
			}
			// format: AREMR
			// format: AREMS
			elseif ($this->options['extractSuffix'] && $this->options['allowInlineSuffix'] && $sfx=$extractor->findAndStore("/.+(?P<suffix>".$extractor->getRegexpToMatchAnyOf(array_keys($this->suffixes)).")$/ui", $lname)) {
				$ret->title = $this->suffixes[$sfx->suffix][0];
				$ret->type = $this->suffixes[$sfx->suffix][1];

				$lname = substr($lname, 0, -1*strlen($sfx->suffix));

				$names = array_merge(array_slice($names, 0, -1), [$lname]);
			}


			// fill data
			$ret->lastName = $xtr->lastName;
			$ret->firstName = implode(" ", $names);
		}
		else {	// no match was possible. put the whole input data in the lastName
			// fill data
			$ret->lastName = trim($this->text);
		}

		return $ret;
	}

	/**
	 * Cleanup the output result, removing invalid stuff
	 * @param \App\Utils\BaseParser\Utils\Types\Passenger $ret
	 * @return \App\Utils\BaseParser\Utils\Types\Passenger
	 */
	protected function cleanupResult(\App\Utils\BaseParser\Utils\Types\Passenger $ret)
	{
		$ret->firstName = trim((string)$ret->firstName, "\t .,/");
		$ret->lastName = trim((string)$ret->lastName, "\t .,/");

		$ret->firstName = preg_replace("/[\s]+/", ' ', $ret->firstName);
		$ret->lastName = preg_replace("/[\s]+/", ' ', $ret->lastName);

		return $ret;
	}

	/**
	 * Extract the passenger name from the input string
	 * @return \App\Utils\BaseParser\Utils\Types\Passenger
	 */
	public function extract()
	{
		$ret = new \App\Utils\BaseParser\Utils\Types\Passenger();
		return $this->cleanupResult($this->doExtract($ret));
	}
}
