package org.unicode.cldr.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ibm.icu.impl.Relation;

public class UExtension {
    static SupplementalDataInfo data = SupplementalDataInfo.getInstance(CLDRPaths.SUPPLEMENTAL_DIRECTORY);

    static Pattern SEP = PatternCache.get("[-_]");
    static Pattern SPACE = PatternCache.get("\\s");
    static Pattern ALPHANUM = PatternCache.get("[0-9A-Za-z]{2,8}");
    static Pattern CODEPOINTS = PatternCache.get("(10|[0-9A-Fa-f])?[0-9A-Fa-f]{4}(\\s(10|[0-9A-Fa-f])?[0-9A-Fa-f]{4})*");
    static Relation<String, String> validKeyTypes = data.getBcp47Keys();

    private boolean validating = false;
    private SortedMap<String, List<String>> keyTypes = new TreeMap<>();
    private Set<String> attributes = new TreeSet<>();

    public Set<String> getKeys() {
        return keyTypes.keySet();
    }

    public List<String> getTypes(String key) {
        return keyTypes.get(key);
    }

    public Set<String> getAttributes() {
        return attributes;
    }

    public boolean isValidating() {
        return validating;
    }

    public UExtension setValidating(boolean validating) {
        this.validating = validating;
        return this;
    }

    /**
     * Parses the subtags after the -u-
     *
     * @param source
     * @return
     */
    public UExtension parse(String source) {
        // the subtags that are up to the first two letter are attributes
        String key = null;
        List<String> list = null;
        Set<String> validSubtypes = null;
        Matcher alphanum = ALPHANUM.matcher("");

        for (String subtag : SEP.split(source)) {
            if (!alphanum.reset(subtag).matches()) {
                throw new IllegalArgumentException("Invalid subtag contents, must be [0-9 A-Z a-z]{2,8}: " + subtag);
            }
            subtag = subtag.toLowerCase(Locale.ENGLISH); // normalize
            if (subtag.length() == 2) { // key
                if (list != null) { // check size of previous list
                    if (list.size() == 0 || !key.equals("vt") && list.size() > 1) {
                        throw new IllegalArgumentException("Illegal number of subtypes for: " + key + "\t" + list);
                    }
                }
                key = subtag;
                if (validating) {
                    validSubtypes = validKeyTypes.getAll(key);
                    if (validSubtypes == null) {
                        throw new IllegalArgumentException("Invalid key: " + key);
                    }
                }
                list = keyTypes.get(key);
                if (list != null) {
                    throw new IllegalArgumentException("Multiple keys with same value: " + subtag);
                }
                list = new ArrayList<>();
                keyTypes.put(key, list);
            } else { // add subtype
                if (key == null) {
                    if (validating) {
                        throw new IllegalArgumentException("No attributes currently valid: " + subtag);
                    }
                    attributes.add(subtag);
                    break;
                }
                if (validating) {
                    if (key.equals("vt")) {
                        if (!CODEPOINTS.matcher(subtag).matches()) {
                            throw new IllegalArgumentException("Illegal subtypes: " + key + "-" + subtag);
                        }
                    } else if (!validSubtypes.contains(subtag)) {
                        throw new IllegalArgumentException("Illegal subtypes: " + key + "-" + subtag);
                    }
                }
                list.add(subtag);
            }
        }
        // protect
        attributes = Collections.unmodifiableSet(attributes);
        for (String key2 : keyTypes.keySet()) {
            list = keyTypes.get(key2);
            keyTypes.put(key2, Collections.unmodifiableList(list));
        }
        keyTypes = Collections.unmodifiableSortedMap(keyTypes);
        return this;
    }

    @Override
    public String toString() {
        return "{attributes=" + attributes + ", keyTypes=" + keyTypes + "}";
    }
}
