<?php
/**
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * This file is part of Nominatim. (https://nominatim.org)
 *
 * Copyright (C) 2022 by the Nominatim developer community.
 * For a full list of authors see the git log.
 */

namespace Nominatim\Token;

/**
 * A house number token.
 */
class HouseNumber
{
    /// Database word id, if available.
    private $iId;
    /// Normalized house number.
    private $sToken;

    public function __construct($iId, $sToken)
    {
        $this->iId = $iId;
        $this->sToken = $sToken;
    }

    public function getId()
    {
        return $this->iId;
    }

    /**
     * Check if the token can be added to the given search.
     * Derive new searches by adding this token to an existing search.
     *
     * @param object  $oSearch      Partial search description derived so far.
     * @param object  $oPosition    Description of the token position within
                                    the query.
     *
     * @return True if the token is compatible with the search configuration
     *         given the position.
     */
    public function isExtendable($oSearch, $oPosition)
    {
        return !$oSearch->hasHousenumber()
               && !$oSearch->hasOperator(\Nominatim\Operator::POSTCODE)
               && $oPosition->maybePhrase('street');
    }

    /**
     * Derive new searches by adding this token to an existing search.
     *
     * @param object  $oSearch      Partial search description derived so far.
     * @param object  $oPosition    Description of the token position within
                                    the query.
     *
     * @return SearchDescription[] List of derived search descriptions.
     */
    public function extendSearch($oSearch, $oPosition)
    {
        $aNewSearches = array();

        // sanity check: if the housenumber is not mainly made
        // up of numbers, add a penalty
        $iSearchCost = 1;
        if (preg_match('/\\d/', $this->sToken) === 0
            || preg_match_all('/[^0-9 ]/', $this->sToken, $aMatches) > 3) {
            $iSearchCost += strlen($this->sToken) - 1;
        }
        if (!$oSearch->hasOperator(\Nominatim\Operator::NONE)) {
            $iSearchCost++;
        }
        if (empty($this->iId)) {
            $iSearchCost++;
        }
        // also must not appear in the middle of the address
        if ($oSearch->hasAddress() || $oSearch->hasPostcode()) {
            $iSearchCost++;
        }

        $oNewSearch = $oSearch->clone($iSearchCost);
        $oNewSearch->setHousenumber($this->sToken);
        $aNewSearches[] = $oNewSearch;

        // Housenumbers may appear in the name when the place has its own
        // address terms.
        if ($this->iId !== null
            && ($oSearch->getNamePhrase() >= 0 || !$oSearch->hasName())
            && !$oSearch->hasAddress()
        ) {
            $oNewSearch = $oSearch->clone($iSearchCost);
            $oNewSearch->setHousenumberAsName($this->iId);

            $aNewSearches[] = $oNewSearch;
        }

        return $aNewSearches;
    }


    public function debugInfo()
    {
        return array(
                'ID' => $this->iId,
                'Type' => 'house number',
                'Info' => array('nr' => $this->sToken)
               );
    }

    public function debugCode()
    {
        return 'H';
    }
}