package de.sebse.fuplanner.tools;

import org.intellij.lang.annotations.Language;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex {
    public static boolean has(@Language("Regexp") String regex, String match) {
        try {
            regex(regex, match, 0);
            return true;
        } catch (NoSuchFieldException e) {
            return false;
        }
    }
    public static String regex(@Language("Regexp") String regex, String match) throws NoSuchFieldException {
        return regex(regex, match, 1);
    }

    public static String regex(@Language("Regexp") String regex, String match, int group) throws NoSuchFieldException {
        Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
        Matcher matcher = pattern.matcher(match);
        if (!matcher.find()) {
            throw new NoSuchFieldException(String.format("Pattern: %s - String: %s", regex, match));
        }
        return matcher.group(group);
    }

    public static Iterable<MatchResult> allMatches(@Language("Regexp") String regex, final CharSequence input) {
        final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
        return () -> new Iterator<MatchResult>() {
            // Use a matcher internally.
            final Matcher matcher = pattern.matcher(input);
            // Keep a match around that supports any interleaving of hasNext/next calls.
            MatchResult pending;

            public boolean hasNext() {
                // Lazily fill pending, and avoid calling find() multiple times if the
                // clients call hasNext() repeatedly before sampling via next().
                if (pending == null && matcher.find()) {
                    pending = matcher.toMatchResult();
                }
                return pending != null;
            }

            public MatchResult next() {
                // Fill pending if necessary (as when clients call next() without
                // checking hasNext()), throw if not possible.
                if (!hasNext()) { throw new NoSuchElementException(); }
                // Consume pending so next call to hasNext() does a find().
                MatchResult next = pending;
                pending = null;
                return next;
            }

            /** Required to satisfy the interface, but unsupported. */
            public void remove() { throw new UnsupportedOperationException(); }
        };
    }
}
