import { LanguageTag } from '@phensley/language-tag' ; import { getRegionPartition } from './partition' ; import { distanceMap , DistanceMap , DistanceNode } from './autogen.distance' ; /**
 * Default distance threshold.
 */ export const DEFAULT_THRESHOLD = 50 ; /**
 * Maximum possible distance between two language tags.
 */ export const MAX_DISTANCE = 100 ; const get = ( map , want , have ) : DistanceNode | undefined => { const sub = map [ want ] ; return sub === undefined ? undefined : sub [ have ] ; } ; const getany = ( map ) : DistanceNode => { const sub = map . $ ; if ( sub !== undefined ) { const node = sub . $ ; if ( node !== undefined ) { return node ; } } throw new Error ( 'Severe error: wildcard levels missing in distance map.' ) ; } ; const _distance = ( node ) : number => ( typeof node === 'number' ? node : node [ 0 ] ) ; /**
 * Return the distance between the desired and supported locale, stopping once
 * the given threshold is exceeded.
 */ export const getDistance = ( desired , supported , threshold ? ) : number => { if ( typeof threshold !== 'number' ) { threshold = DEFAULT_THRESHOLD ; } let wildcard = false ; let map = distanceMap ; let want = desired . language ( ) ; let have = supported . language ( ) ; let node = get ( map , want , have ) ; if ( node === undefined ) { node = getany ( map ) ; wildcard = true ; } let distance = wildcard ? ( want === have ? 0 : _distance ( node ) ) : _distance ( node ) ; if ( distance >= threshold ) { return MAX_DISTANCE ; } map = ( node as [ number , DistanceMap ] ) [ 1 ] ; want = desired . script ( ) ; have = supported . script ( ) ; node = get ( map , want , have ) ; if ( node === undefined ) { node = getany ( map ) ; wildcard = true ; } else { wildcard = false ; } distance += wildcard ? ( want === have ? 0 : _distance ( node ) ) : _distance ( node ) ; if ( distance >= threshold ) { return MAX_DISTANCE ; } want = desired . region ( ) ; have = supported . region ( ) ; if ( want === have ) { return distance ; } const wantPartitions = getRegionPartition ( want ) ; const havePartitions = getRegionPartition ( have ) ; map = ( node as [ number , DistanceMap ] ) [ 1 ] ; node = get ( map , want , have ) ; if ( node === undefined ) { node = scanRegion ( map , want , wantPartitions , have , havePartitions ) ; } if ( node !== undefined ) { distance += _distance ( node ) ; return distance < threshold ? distance : MAX_DISTANCE ; } let maxDistance = 0 ; let match = false ; for ( const dpartition of wantPartitions ) { for ( const spartition of havePartitions ) { node = get ( map , dpartition , spartition ) ; if ( node !== undefined ) { maxDistance = Math . max ( maxDistance , _distance ( node ) ) ; match = true ; } } } if ( ! match ) { node = getany ( map ) ; if ( node !== undefined ) { maxDistance = Math . max ( maxDistance , _distance ( node ) ) ; } } distance += maxDistance ; return distance < threshold ? distance : MAX_DISTANCE ; } ; /**
 * Find the distance between sets of partitions.
 */ const scanRegion = ( map , want , wantPartitions , have , havePartitions ) : number | undefined => { let node = undefined ; for ( const v of wantPartitions ) { node = get ( map , v , have ) ; if ( typeof node === 'number' ) { return node ; } } for ( const v of havePartitions ) { node = get ( map , want , v ) ; if ( typeof node === 'number' ) { return node ; } } return undefined ; } ;