/*
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package jdk.nashorn.internal.runtime.regexp.joni;

@SuppressWarnings("javadoc")
public abstract class SearchAlgorithm {

    public abstract String getName();
    public abstract int search(Regex regex, char[] text, int textP, int textEnd, int textRange);
    public abstract int searchBackward(Regex regex, char[] text, int textP, int adjustText, int textEnd, int textStart, int s_, int range_);


    public static final SearchAlgorithm NONE = new SearchAlgorithm() {

        @Override
        public final String getName() {
            return "NONE";
        }

        @Override
        public final int search(final Regex regex, final char[] text, final int textP, final int textEnd, final int textRange) {
            return textP;
        }

        @Override
        public final int searchBackward(final Regex regex, final char[] text, final int textP, final int adjustText, final int textEnd, final int textStart, final int s_, final int range_) {
            return textP;
        }

    };

    public static final SearchAlgorithm SLOW = new SearchAlgorithm() {

        @Override
        public final String getName() {
            return "EXACT";
        }

        @Override
        public final int search(final Regex regex, final char[] text, final int textP, final int textEnd, final int textRange) {
            final char[] target = regex.exact;
            final int targetP = regex.exactP;
            final int targetEnd = regex.exactEnd;


            int end = textEnd;
            end -= targetEnd - targetP - 1;

            if (end > textRange) {
                end = textRange;
            }

            int s = textP;

            while (s < end) {
                if (text[s] == target[targetP]) {
                    int p = s + 1;
                    int t = targetP + 1;
                    while (t < targetEnd) {
                        if (target[t] != text[p++]) {
                            break;
                        }
                        t++;
                    }

                    if (t == targetEnd) {
                        return s;
                    }
                }
                s++;
            }

            return -1;
        }

        @Override
        public final int searchBackward(final Regex regex, final char[] text, final int textP, final int adjustText, final int textEnd, final int textStart, final int s_, final int range_) {
            final char[] target = regex.exact;
            final int targetP = regex.exactP;
            final int targetEnd = regex.exactEnd;

            int s = textEnd;
            s -= targetEnd - targetP;

            if (s > textStart) {
                s = textStart;
            }

            while (s >= textP) {
                if (text[s] == target[targetP]) {
                    int p = s + 1;
                    int t = targetP + 1;
                    while (t < targetEnd) {
                        if (target[t] != text[p++]) {
                            break;
                        }
                        t++;
                    }
                    if (t == targetEnd) {
                        return s;
                    }
                }
                // s = enc.prevCharHead or s = s <= adjustText ? -1 : s - 1;
                s--;
            }
            return -1;
        }
    };

    public static final class SLOW_IC extends SearchAlgorithm {
        public SLOW_IC(final Regex regex) {
            //empty
        }

        @Override
        public final String getName() {
            return "EXACT_IC";
        }

        @Override
        public final int search(final Regex regex, final char[] text, final int textP, final int textEnd, final int textRange) {
            final char[] target = regex.exact;
            final int targetP = regex.exactP;
            final int targetEnd = regex.exactEnd;

            int end = textEnd;
            end -= targetEnd - targetP - 1;

            if (end > textRange) {
                end = textRange;
            }
            int s = textP;

            while (s < end) {
                if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) {
                    return s;
                }
                s++;
            }
            return -1;
        }

        @Override
        public final int searchBackward(final Regex regex, final char[] text, final int textP, final int adjustText, final int textEnd, final int textStart, final int s_, final int range_) {
            final char[] target = regex.exact;
            final int targetP = regex.exactP;
            final int targetEnd = regex.exactEnd;

            int s = textEnd;
            s -= targetEnd - targetP;

            if (s > textStart) {
                s = textStart;
            }

            while (s >= textP) {
                if (lowerCaseMatch(target, targetP, targetEnd, text, s, textEnd)) {
                    return s;
                }
                s = EncodingHelper.prevCharHead(adjustText, s);
            }
            return -1;
        }

        private static boolean lowerCaseMatch(final char[] t, final int tPp, final int tEnd,
                                       final char[] chars, final int pp, final int end) {

            for (int tP = tPp, p = pp; tP < tEnd; ) {
                if (t[tP++] != EncodingHelper.toLowerCase(chars[p++])) {
                    return false;
                }
            }
            return true;
        }
    }

    public static final SearchAlgorithm BM = new SearchAlgorithm() {

        @Override
        public final String getName() {
            return "EXACT_BM";
        }

        @Override
        public final int search(final Regex regex, final char[] text, final int textP, final int textEnd, final int textRange) {
            final char[] target = regex.exact;
            final int targetP = regex.exactP;
            final int targetEnd = regex.exactEnd;

            int end = textRange + (targetEnd - targetP) - 1;
            if (end > textEnd) {
                end = textEnd;
            }

            final int tail = targetEnd - 1;
            int s = textP + (targetEnd - targetP) - 1;

            if (regex.intMap == null) {
                while (s < end) {
                    int p = s;
                    int t = tail;

                    while (text[p] == target[t]) {
                        if (t == targetP) {
                            return p;
                        }
                        p--; t--;
                    }

                    s += regex.map[text[s] & 0xff];
                }
            } else { /* see int_map[] */
                while (s < end) {
                    int p = s;
                    int t = tail;

                    while (text[p] == target[t]) {
                        if (t == targetP) {
                            return p;
                        }
                        p--; t--;
                    }

                    s += regex.intMap[text[s] & 0xff];
                }
            }
            return -1;
        }

        private static final int BM_BACKWARD_SEARCH_LENGTH_THRESHOLD = 100;

        @Override
        public final int searchBackward(final Regex regex, final char[] text, final int textP, final int adjustText, final int textEnd, final int textStart, final int s_, final int range_) {
            final char[] target = regex.exact;
            final int targetP = regex.exactP;
            final int targetEnd = regex.exactEnd;

            if (regex.intMapBackward == null) {
                if (s_ - range_ < BM_BACKWARD_SEARCH_LENGTH_THRESHOLD) {
                    // goto exact_method;
                    return SLOW.searchBackward(regex, text, textP, adjustText, textEnd, textStart, s_, range_);
                }
                setBmBackwardSkip(regex, target, targetP, targetEnd);
            }

            int s = textEnd - (targetEnd - targetP);

            if (textStart < s) {
                s = textStart;
            }

            while (s >= textP) {
                int p = s;
                int t = targetP;
                while (t < targetEnd && text[p] == target[t]) {
                    p++; t++;
                }
                if (t == targetEnd) {
                    return s;
                }

                s -= regex.intMapBackward[text[s] & 0xff];
            }
            return -1;
        }


        private void setBmBackwardSkip(final Regex regex, final char[] chars, final int p, final int end) {
            int[] skip;
            if (regex.intMapBackward == null) {
                skip = new int[Config.CHAR_TABLE_SIZE];
                regex.intMapBackward = skip;
            } else {
                skip = regex.intMapBackward;
            }

            final int len = end - p;

            for (int i=0; i<Config.CHAR_TABLE_SIZE; i++) {
                skip[i] = len;
            }
            for (int i=len-1; i>0; i--) {
                skip[chars[i] & 0xff] = i;
            }
        }
    };

    public static final SearchAlgorithm MAP = new SearchAlgorithm() {

        @Override
        public final String getName() {
            return "MAP";
        }

        @Override
        public final int search(final Regex regex, final char[] text, final int textP, final int textEnd, final int textRange) {
            final byte[] map = regex.map;
            int s = textP;

            while (s < textRange) {
                if (text[s] > 0xff || map[text[s]] != 0) {
                    return s;
                }
                s++;
            }
            return -1;
        }

        @Override
        public final int searchBackward(final Regex regex, final char[] text, final int textP, final int adjustText, final int textEnd, final int textStart, final int s_, final int range_) {
            final byte[] map = regex.map;
            int s = textStart;

            if (s >= textEnd) {
                s = textEnd - 1;
            }
            while (s >= textP) {
                if (text[s] > 0xff || map[text[s]] != 0) {
                    return s;
                }
                s--;
            }
            return -1;
        }
    };

}
