### ----------------------------------------------------
### -- Intl::LanguageTag
### -- Licenses: Artistic-2.0
### -- Authors: Matthew Stephen Stuckwisch
### -- File: lib/Intl/LanguageTag/BCP47.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
P47.REGISTER-SHORTCUT: 'region-override',    *.extensions<u><rg>;
LanguageTag::BCP47.REGISTER-SHORTCUT: 'subdivision',        *.extensions<u><sd>;
LanguageTag::BCP47.REGISTER-SHORTCUT: 'sentence-break',     *.extensions<u><ss>;
LanguageTag::BCP47.REGISTER-SHORTCUT: 'timezone',           *.extensions<u><tz>;
LanguageTag::BCP47.REGISTER-SHORTCUT: 'uni-variant',        *.extensions<u><va>;


#multi sub filter-language-tags(@available where { @available.all ~~ LanguageTag}, $wants, :$basic = False) is export {
#    filter-language-tags(@available, ($wants,), :$basic);
#}

# Note that in general, the SOURCE language is expected to have a larger/longer
# language tag than the filter.  If the filter is longer, it can never match.
# TODO: We really should use multi dispatch here
#| Implements RFC 4647
multi sub filter-language-tags(
        LanguageTag::BCP47() @source, #= The language tags to be filtered (generally longer)
        LanguageTag::BCP47() \filter, #= The language tags to use as a filter (generally shorter)
        Bool :$basic = False,       #= Use basic filtering mode (faster, but potentially less accurate)
) is export {
    return filter-language-tags-basic(@source, filter) if $basic;
    #my @extended-filters = do for @filter { $_ ~~ Str ?? LanguageTagFilter.new: $_ !! $_};
    filter-language-tags-extended(@source, filter);
}

# Basic filtering, according to RFC4647 § 3.3.1
sub filter-language-tags-basic(
        LanguageTag::BCP47() @source, # expect language tag
        LanguageTag::BCP47() \filter,
        --> Seq
) {
    my Str $filter = ~filter;
    do gather for @source -> $source-tag {
        take $source-tag if (
            || $source-tag.Str.lc eq $filter                 # exact match
            || $source-tag.Str.lc.starts-with($filter ~ '-') # prefix match, ending on a tag
           #|| $filter eq '*'                                # wildcard matches all
        )
    }
}

sub filter-language-tags-extended(
        LanguageTag::BCP47() @source, # filter or tag
        LanguageTag::BCP47() \filter, # filter
        --> Seq
) {
    die "NYI";
    do gather for @source -> $source-tag {
        take $source-tag if $source-tag ~~ filter
    }
}

#| Implements RFC4647 § 3.4 Lookup
sub lookup-language-tags(
        LanguageTag::BCP47  @available-tags,
        LanguageTag::BCP47  @preferred-tags,
        LanguageTag::BCP47  $default = LanguageTag::BCP47.new('en'),
        Bool               :$single  = False
        #--> Seq
) {
    # NOTE: The following is a description of the previous algorithm.
    #       It was perhaps more accurate, but more complicated and will
    #       require some extra rewriting to be performant with the new
    #       language tag format.  In the meantime, a simple version is done
    # The general assumption is that the PREFERENCES are longer than what is
    # AVAILABLE.  Anything that is available but is more specific than the
    # preferences will fail never match.
    #
    # WARNING: this is a first attempt, I do not guarantee how well it works, but
    #          it seems accurate enough.  It does NOT handle wildcards yet, sorry.
    #
    # RFC4647 specifically indicates that subtags are to be progressively shed
    # until a match is found, and the "best match" is the longest possible match.
    # The exact process for comparing a best match between two or more stated
    # preferences is not provided.  Imagine the following:
    #    AVAILABLE      PREFERRED
    #    ar             en-UK
    #    en             en-US
    #    en-US          en
    #    es             es-ES
    # In practice, this combination might not happen but it must be contemplated.
    # A naïve approach is to test if en-UK exists (it doesn't), then strip it of
    # the UK and then match "en".  But the user has explicitly also preferred
    # en-US over bare en, so we cannot test "en" until we have tried en-US.
    # The approach used instead (and, incidentally, faster) is to check for which
    # preferred values does each available begin, and store the longest at each pass.
    # 1. Check that @preferred starts with "ar".
    #     -> (en-UK => "", en-US => "", en => "", es-ES => "")
    # 2. Check that @preferred starts with "en".
    #     -> (en-UK => en, en-US => en, en => en, es-ES => ())
    # 3. Check that @preferred starts with "en-US"
    #     -> (en-UK => en, en-US => en-US, en => en, es-ES => "")
    #                               ^^^^^ longer match than just en
    # 4. Check that @preferred starts with "es"
    #     -> (en-UK => en, en-US => en-US, en => en, es-ES => es)
    # TODO: finish documenting the algorithm
    #
    # NOTE: this does not currently handle wildcards, which will make the logic
    # substantially more complicated, reduce speed/efficiency, and probably not
    # be used.  When such a routine is coded, it may be a good idea to detect
    # a wildcard presence and use an alternate algorithm.

    # Initially stringify and add a hyphen to avoid potentially overlapping tags
    # of different lengths (otherwise "es" [Spanish] might match "est" [Estonian],
    # and the same can happen with extension/variant tags)

    my $available = @available-tags>>.Str>>.split('-').any;
    my @preferred = @preferred-tags>>.Str;

    my @results;
    for @preferred -> $preferred {
        my @subtags = $preferred.split("-");
        while @subtags {
            @subtags.pop if @subtags.tail.chars == 1;
            @results.push: LanguageTag::BCP47.new(@subtags)
                if @subtags ~~ $available;
            @subtags.pop;
        }
    }
    @results.push: $default;
    return @results.head if $single;
    @results;
}

sub EXPORT(*@types) {
    my %export;
    %export<LanguageTag::BCP47>                                = LanguageTag::BCP47;
    %export<LanguageTag::BCP47::Extension::TransformedContent> = TransformedContent    if @types.any eq 'ext-t';
    %export<LanguageTag::BCP47::TransformedContent>            = TransformedContent    if @types.any eq 't';
    %export<LanguageTag::BCP47::Extension::UnicodeLocale>      = UnicodeLocale         if @types.any eq 'ext-u';
    %export<LanguageTag::BCP47::UnicodeLocale>                 = UnicodeLocale         if @types.any eq 'u';
    %export<&filter-language-tags>                             = &filter-language-tags if @types.any eq 'tools';
    %export<&lookup-language-tags>                             = &lookup-language-tags if @types.any eq 'tools';
    %export.Map
}


