/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */


package org.elasticsearch.common.xcontent.support.filtering;

import org.elasticsearch.common.regex.Regex;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class FilterPath {

    static final FilterPath EMPTY = new FilterPath();

    private final String filter;
    private final String segment;
    private final FilterPath next;
    private final boolean simpleWildcard;
    private final boolean doubleWildcard;

    protected FilterPath(String filter, String segment, FilterPath next) {
        this.filter = filter;
        this.segment = segment;
        this.next = next;
        this.simpleWildcard = (segment != null) && (segment.length() == 1) && (segment.charAt(0) == '*');
        this.doubleWildcard = (segment != null) && (segment.length() == 2) && (segment.charAt(0) == '*') && (segment.charAt(1) == '*');
    }

    private FilterPath() {
        this("<empty>", "", null);
    }

    public FilterPath matchProperty(String name) {
        if ((next != null) && (simpleWildcard || doubleWildcard || Regex.simpleMatch(segment, name))) {
            return next;
        }
        return null;
    }

    public boolean matches() {
        return next == null;
    }

    boolean isDoubleWildcard() {
        return doubleWildcard;
    }

    boolean isSimpleWildcard() {
        return simpleWildcard;
    }

    String getSegment() {
        return segment;
    }

    FilterPath getNext() {
        return next;
    }

    public static FilterPath[] compile(Set<String> filters) {
        if (filters == null || filters.isEmpty()) {
            return null;
        }

        List<FilterPath> paths = new ArrayList<>();
        for (String filter : filters) {
            if (filter != null) {
                filter = filter.trim();
                if (filter.length() > 0) {
                    paths.add(parse(filter, filter));
                }
            }
        }
        return paths.toArray(new FilterPath[paths.size()]);
    }

    private static FilterPath parse(final String filter, final String segment) {
        int end = segment.length();

        for (int i = 0; i < end; ) {
            char c = segment.charAt(i);

            if (c == '.') {
                String current = segment.substring(0, i).replaceAll("\\\\.", ".");
                return new FilterPath(filter, current, parse(filter, segment.substring(i + 1)));
            }
            ++i;
            if ((c == '\\') && (i < end) && (segment.charAt(i) == '.')) {
                ++i;
            }
        }
        return new FilterPath(filter, segment.replaceAll("\\\\.", "."), EMPTY);
    }

    @Override
    public String toString() {
        return "FilterPath [filter=" + filter + ", segment=" + segment + "]";
    }
}
