/* FichothequeLib_Tools - Copyright (c) 2014-2020 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.tools.parsers.span;

import java.text.ParseException;
import net.fichotheque.tools.parsers.AttsParser;


/**
 *
 * @author Vincent Calame
 */
class RefTest {

    public static short EMPTY = 1;
    public static short ONLY_REF = 2;
    public static short BOTH = 3;
    public static short NO_REF = 4;
    private final static short WAITING_FOR = 1;
    private final static short UNDETERMINED = 2;
    private final static short ON_REF = 3;
    private final static short ON_QUOTE = 4;
    private final static short WAITING_FOR_SEPARATOR = 5;
    private short state = WAITING_FOR;
    private short resultType;
    private int currentIndex;
    private int closeIndex;
    private boolean escaped;
    private StringBuilder refBuf = new StringBuilder();

    private RefTest() {
    }

    short getResultType() {
        return resultType;
    }

    String getRef() {
        if ((resultType == ONLY_REF) || (resultType == BOTH)) {
            return refBuf.toString();
        } else {
            return "";
        }
    }

    boolean hasAtts() {
        return ((resultType == NO_REF) || (resultType == BOTH));
    }

    int getCloseIndex() {
        return closeIndex;
    }

    static RefTest parse(String source, int startIndex) throws ParseException {
        RefTest refTest = new RefTest();
        int length = source.length();
        boolean continueParsing = true;
        int lastIndex = -1;
        for (int i = startIndex; i < length; i++) {
            refTest.currentIndex = i;
            char carac = source.charAt(i);
            continueParsing = refTest.checkChar(carac);
            if (!continueParsing) {
                lastIndex = i;
                break;
            }
        }
        if (continueParsing) {
            throw new ParseException("Missing closing )", refTest.currentIndex);
        } else {
            if (refTest.resultType == NO_REF) {
                refTest.closeIndex = startIndex - 1;
            } else {
                refTest.closeIndex = lastIndex;
            }
        }
        return refTest;
    }

    private boolean checkChar(char carac) throws ParseException {
        boolean continueParsing = true;
        switch (state) {
            case WAITING_FOR:
                continueParsing = checkWaitingFor(carac);
                break;
            case ON_REF:
                continueParsing = checkOnRef(carac);
                break;
            case UNDETERMINED:
                continueParsing = checkUndetermined(carac);
                break;
            case ON_QUOTE:
                continueParsing = checkOnQuote(carac);
                break;
            case WAITING_FOR_SEPARATOR:
                continueParsing = waitingForSeparator(carac);
                break;
        }
        return continueParsing;
    }

    private boolean checkWaitingFor(char carac) throws ParseException {
        if (carac == ')') {
            resultType = EMPTY;
            return false;
        } else if (carac == ' ') {
            return true;
        } else if (carac == '"') {
            state = ON_QUOTE;
            return true;
        } else {
            if (AttsParser.isValideFisrtNameChar(carac)) {
                state = UNDETERMINED;
            } else {
                state = ON_REF;
            }
            refBuf.append(carac);
            return true;
        }
    }

    private boolean checkOnRef(char carac) throws ParseException {
        if (carac == ')') {
            resultType = ONLY_REF;
            return false;
        } else if (carac == ' ') {
            resultType = BOTH;
            return false;
        } else if (carac == '"') {
            throw new ParseException("\" character inside ref", currentIndex);
        } else {
            refBuf.append(carac);
            return true;
        }
    }

    private boolean checkUndetermined(char carac) throws ParseException {
        if (carac == ')') {
            resultType = ONLY_REF;
            return false;
        } else if (carac == ' ') {
            resultType = BOTH;
            return false;
        } else if (carac == '=') {
            resultType = NO_REF;
            return false;
        } else if (carac == '"') {
            throw new ParseException("\" character inside ref", currentIndex);
        } else {
            if (!AttsParser.isValidNameChar(carac)) {
                state = ON_REF;
            }
            refBuf.append(carac);
            return true;
        }
    }

    private boolean checkOnQuote(char carac) {
        if (carac == '"') {
            if (escaped) {
                escaped = false;
                refBuf.append('"');
            } else {
                state = WAITING_FOR_SEPARATOR;
            }
            return true;
        } else if (carac == '\\') {
            if (escaped) {
                escaped = false;
                refBuf.append('\\');
            } else {
                escaped = true;
            }
            return true;
        } else {
            if (escaped) {
                escaped = false;
                refBuf.append('\\');
            }
            refBuf.append(carac);
            return true;
        }
    }

    private boolean waitingForSeparator(char carac) throws ParseException {
        if (carac == ')') {
            resultType = ONLY_REF;
            return false;
        } else if (carac == ' ') {
            resultType = BOTH;
            return false;
        } else {
            throw new ParseException("character " + carac + " following quote end", currentIndex);
        }
    }

}
