/*******************************************************************************
 * Copyright (c) 2009, 2018 Red Hat, Inc.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Red Hat - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.valgrind.ui.editor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;

public class SuppressionKindRule implements IRule {
    private Map<String, List<String>> kinds;
    private IToken token;
    private WordRule subrule;

    private static final IToken DUMMY_TOKEN = new Token(null);
    private static final char[] COLON = new char[] { ':' };

    public SuppressionKindRule(Map<String, List<String>> suppKinds, IToken successToken) {
        kinds = suppKinds;
        token = successToken;

        final List<String> suppKindsList = new ArrayList<>();
        for (List<String> entry : suppKinds.values()) {
            suppKindsList.addAll(entry);
        }

        subrule = new WordRule(new IWordDetector() {

            @Override
            public boolean isWordStart(char c) {
                for (String kind : suppKindsList) {
                    if (c == kind.charAt(0)) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public boolean isWordPart(char c) {
                return Character.isJavaIdentifierPart(c);
            }

        });

        for (String kind : suppKindsList) {
            subrule.addWord(kind, DUMMY_TOKEN);
        }
    }

    @Override
    public IToken evaluate(ICharacterScanner scanner) {
        IToken result = subrule.evaluate(scanner);
        StringBuilder tool = new StringBuilder();
        StringBuilder kind = new StringBuilder();

        if (!result.isUndefined()) {
            boolean match = true;

            // move to beginning of token
            int tokenLength = ((SuppressionsElementScanner) scanner).getTokenLength();
            for (int i = 0; i < tokenLength; i++) {
                scanner.unread();
            }

            // check first char before token is a ':'
            match = checkColon(scanner);
            if (match) {
                // move to beginning of line
                int preTokenLength = 0;
                while (scanner.getColumn() > 0) {
                    scanner.unread();
                    preTokenLength++;
                }

                // ensure any leading characters are whitespace
                boolean foundChar = false;
                while (preTokenLength-- > 1) { // skip colon
                    int ch = scanner.read();
                    if (match && !Character.isWhitespace(ch)) {
                        foundChar = true;
                        tool.append(Character.toChars(ch));
                    }
                    else if (foundChar) {
                        // encountered whitespace after tool name started
                        match = false;
                    }
                }

                scanner.read(); // consume colon

                // reset to end of token
                for (int i = 0; i < tokenLength; i++) {
                    int ch = scanner.read();
                    if (match) {
                        kind.append(Character.toChars(ch));
                    }
                }

                // assert kind is valid for tool
                if (match) {
                    List<String> kindList = kinds.get(tool.toString());
                    if (kindList == null || !kindList.contains(kind.toString())) {
                        match = false;
                    }
                }
                else {
                    // reset to beginning of token
                    unreadBuffer(scanner, tokenLength);
                }
            }

            if (!match) {
                result = Token.UNDEFINED;
            }
            else {
                result = token;
            }
        }

        return result;
    }

    private void unreadBuffer(ICharacterScanner scanner, int length) {
        for (int i = 0; i < length; i++) {
            scanner.unread();
        }
    }

    private boolean checkColon(ICharacterScanner scanner) {
        if (scanner.getColumn() == 0) {
            // nothing to read
            return false;
        }
        scanner.unread();
        int ch = scanner.read();
        return Arrays.equals(Character.toChars(ch), COLON);
    }

}
