package ru.yandex.webmaster3.core.regions;

import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

import com.google.common.collect.Sets;

import ru.yandex.webmaster3.core.data.L10nEnum;
import ru.yandex.webmaster3.core.data.W3RegionInfo;

/**
 * @author avhaliullin
 */
public final class RegionUtils {
    public static final class RegionTypes {
        private RegionTypes() {
        }

        // Не общепринятый тип, введен для виртуального региона "весь мир"
        public static final int WHOLE_WORLD = -10;

        public static final int HIDDEN = -1;
        public static final int OTHERS = 0;
        public static final int CONTINENT = 1;

        // например СНГ
        public static final int MACRO_REGION = 2;
        public static final int COUNTRY = 3;

        //цитата с вики: "заморская территория (почти тоже самое, что страна)"
        public static final int FOREIGN_LAND = 12;
        // федеральный округ
        public static final int FEDERAL_DISTRICT = 4;
        // субъект федерации
        public static final int FEDERAL_SUBJECT = 5;
        // район субъекта федерации
        public static final int FEDERAL_SUBJECT_DISTRICT = 10;

        public static final int CITY = 6;
        public static final int VILLAGE = 7;

        public static final Set<Integer> COUNTRY_LIKE = Collections.unmodifiableSet(
                Sets.newHashSet(
                        COUNTRY,
                        FOREIGN_LAND
                )
        );

        public static final Set<Integer> CITY_LIKE = Collections.unmodifiableSet(
                Sets.newHashSet(
                        CITY,
                        VILLAGE
                )
        );

        public static final Set<Integer> COUNTRY_SUBDIVISION = Collections.unmodifiableSet(
                Sets.newHashSet(
                        FEDERAL_DISTRICT,
                        FEDERAL_SUBJECT,
                        FEDERAL_SUBJECT_DISTRICT
                )
        );

        public static final Set<Integer> NON_HIDDEN_REGION_TYPES = Collections.unmodifiableSet(
                Sets.newHashSet(
                        WHOLE_WORLD,
                        CONTINENT,
                        MACRO_REGION,//
                        COUNTRY,//
                        FOREIGN_LAND,//
                        FEDERAL_DISTRICT,//
                        FEDERAL_SUBJECT,//
                        FEDERAL_SUBJECT_DISTRICT,//
                        CITY, //
                        VILLAGE//
                )
        );
    }

    public static final int NOT_SPECIFIED_REGION_ID = 29;

    public static final W3RegionInfo WHOLE_WORLD_REGION =
            new W3RegionInfo(null, 0, RegionTypes.WHOLE_WORLD, new EnumMap<>(L10nEnum.class), "", false);

    public static final Predicate<W3RegionInfo> FILTER_ALLOW_REGION_NOT_SPECIFIED =
            region -> region.getId() == NOT_SPECIFIED_REGION_ID;

    public static Predicate<W3RegionInfo> filterTypeIn(Set<Integer> types) {
        return region -> types.contains(region.getType());
    }

    private static final Set<Integer> VISIBLE_REGION_TYPES_NO_WHOLE_WORLD;
    static {
        Set<Integer> regions = new HashSet<>();
        regions.add(RegionTypes.MACRO_REGION);
        regions.addAll(RegionTypes.COUNTRY_SUBDIVISION);
        regions.addAll(RegionTypes.COUNTRY_LIKE);
        regions.addAll(RegionTypes.CITY_LIKE);

        VISIBLE_REGION_TYPES_NO_WHOLE_WORLD = Collections.unmodifiableSet(regions);
    }

    public static final Predicate<W3RegionInfo> NON_HIDDEN_REGION_TYPES_PREDICATE =
            filterTypeIn(RegionTypes.NON_HIDDEN_REGION_TYPES)
            .or(FILTER_ALLOW_REGION_NOT_SPECIFIED);

    public static final Predicate<W3RegionInfo> VISIBILITY_PREDICATE =
            filterTypeIn(VISIBLE_REGION_TYPES_NO_WHOLE_WORLD);

    public static final Predicate<W3RegionInfo> VISIBLE_OR_WHOLE_WORLD_PREDICATE =
            VISIBILITY_PREDICATE
            .or(r -> r == WHOLE_WORLD_REGION);

    private static final Set<Integer> VISIBLE_CHILDREN_TYPES = new HashSet<>();
    static {
        VISIBLE_CHILDREN_TYPES.add(RegionTypes.CONTINENT);
        VISIBLE_CHILDREN_TYPES.addAll(RegionTypes.COUNTRY_SUBDIVISION);
        VISIBLE_CHILDREN_TYPES.addAll(RegionTypes.COUNTRY_LIKE);
        VISIBLE_CHILDREN_TYPES.addAll(RegionTypes.CITY_LIKE);
    }

    public static final Predicate<W3RegionInfo> CHILDREN_VISIBILITY_PREDICATE =
            filterTypeIn(VISIBLE_CHILDREN_TYPES);

    private static final Set<Integer> VISIBLE_PARENT_TYPES = new HashSet<>();
    static {
        VISIBLE_PARENT_TYPES.add(RegionTypes.CONTINENT);
        VISIBLE_PARENT_TYPES.add(RegionTypes.WHOLE_WORLD);
        VISIBLE_PARENT_TYPES.add(RegionTypes.MACRO_REGION);
        VISIBLE_PARENT_TYPES.addAll(RegionTypes.COUNTRY_SUBDIVISION);
        VISIBLE_PARENT_TYPES.addAll(RegionTypes.COUNTRY_LIKE);
        VISIBLE_PARENT_TYPES.addAll(RegionTypes.CITY_LIKE);
    }

    public static final Predicate<W3RegionInfo> PARENT_VISIBILITY_PREDICATE =
            filterTypeIn(VISIBLE_PARENT_TYPES);
}
