package com.cyrilmottier.android.queryhighlight.text.format;

import android.graphics.Typeface;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.StyleSpan;
import android.widget.TextView;
import com.cyrilmottier.android.queryhighlight.text.Normalizer;

import java.util.Locale;
import java.util.Objects;

public final class QueryHighlighter {

    public enum Mode {
        CHARACTERS, WORDS
    }

    public static abstract class QueryNormalizer {

        public static final QueryNormalizer FOR_SEARCH = new QueryNormalizer() {
            @Override
            public CharSequence normalize(CharSequence source) {
                return Normalizer.forSearch(source);
            }
        };

        public static final QueryNormalizer CASE = new QueryNormalizer() {
            @Override
            public CharSequence normalize(CharSequence source) {
                if (TextUtils.isEmpty(source)) {
                    return source;
                }
                return source.toString().toUpperCase(Locale.ROOT);
            }
        };

        public static final QueryNormalizer NONE = new QueryNormalizer() {
            @Override
            public CharSequence normalize(CharSequence source) {
                return source;
            }
        };

        public abstract CharSequence normalize(CharSequence source);
    }

    private CharacterStyle mHighlightStyle = new StyleSpan(Typeface.BOLD);
    private QueryNormalizer mQueryNormalizer = QueryNormalizer.NONE;
    private Mode mMode = Mode.WORDS;

    public QueryHighlighter setHighlightStyle(CharacterStyle highlightStyle) {
        mHighlightStyle = Objects.requireNonNull(highlightStyle, "highlightStyle cannot be null");
        return this;
    }

    public QueryHighlighter setQueryNormalizer(QueryNormalizer queryNormalizer) {
        mQueryNormalizer = Objects.requireNonNull(queryNormalizer, "queryNormalizer cannot be null");
        return this;
    }

    public QueryHighlighter setMode(Mode mode) {
        mMode = Objects.requireNonNull(mode, "mode cannot be null");
        return this;
    }

    public CharSequence apply(CharSequence text, CharSequence wordPrefix) {
        final CharSequence normalizedText = mQueryNormalizer.normalize(text);
        final CharSequence normalizedWordPrefix = mQueryNormalizer.normalize(wordPrefix);

        final int index = indexOfQuery(normalizedText, normalizedWordPrefix);
        if (index != -1) {
            final SpannableString result = new SpannableString(text);
            result.setSpan(mHighlightStyle, index, index + normalizedWordPrefix.length(), 0);
            return result;
        } else {
            return text;
        }
    }

    public void setText(TextView view, CharSequence text, CharSequence query) {
        view.setText(apply(text, query));
    }

    private int indexOfQuery(CharSequence text, CharSequence query) {
        if (query == null || text == null) {
            return -1;
        }

        final int textLength = text.length();
        final int queryLength = query.length();

        if (queryLength == 0 || textLength < queryLength) {
            return -1;
        }

        for (int i = 0; i <= textLength - queryLength; i++) {
            // Only match word prefixes
            if (mMode == Mode.WORDS && i > 0 && text.charAt(i - 1) != ' ') {
                continue;
            }

            int j;
            for (j = 0; j < queryLength; j++) {
                if (text.charAt(i + j) != query.charAt(j)) {
                    break;
                }
            }
            if (j == queryLength) {
                return i;
            }
        }

        return -1;
    }

}