/* FichothequeLib_Tools - Copyright (c) 2023 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.extraction.syntaxes;


/**
 *
 * @author Vincent Calame
 */
public final class XmlResolver extends AbstractStepResolver {

    private final static short ERROR_STEP = 0;
    private final static short START_STEP = 1;
    private final static short TAG_STEP = 2;
    private final static short ATTRIBUTE_STEP = 3;
    private final static short ONQUOTE_STEP = 5;
    private final static short WAITING_ATTRIBUTE_OPERATOR_STEP = 11;
    private final static short WAITING_TAG_STEP = 12;
    private final static short WAITING_TAG_CONTENT_STEP = 13;
    private final static short WAITING_QUOTE_STEP = 14;
    private final static short NAMESPACE_STEP = 20;
    private final static short ATTRIBUTE_NAMESPACE_STEP = 21;
    private int step;
    private char currentQuote;

    private XmlResolver() {
        step = START_STEP;
    }

    @Override
    public void resolveChar(char carac) {
        switch (carac) {
            case ' ':
                resolveSpace();
                break;
            default:
                appendChar(carac);
        }
    }

    private void resolveSpace() {
        boolean appendToCurrent = false;
        switch (step) {
            case ONQUOTE_STEP:
            case ERROR_STEP:
            case START_STEP:
            case WAITING_TAG_STEP:
                appendToCurrent = true;
                break;
            case TAG_STEP:
                flush();
                step = WAITING_TAG_CONTENT_STEP;
                break;
            case ATTRIBUTE_STEP:
                flush();
                step = WAITING_ATTRIBUTE_OPERATOR_STEP;
                break;
        }
        if (appendToCurrent) {
            appendToCurrent(' ');
        } else {
            appendSpaceToFinal();
        }
    }

    private void appendChar(char carac) {
        boolean appendToCurrent = true;
        switch (step) {
            case START_STEP: {
                switch (carac) {
                    case '<':
                        flush();
                        appendXmlChar("<");
                        step = TAG_STEP;
                        appendToCurrent = false;
                        break;
                    case '@':
                        flush();
                        appendToFinal("cm-xml-attr-arobase", "@");
                        step = ATTRIBUTE_STEP;
                        appendToCurrent = false;
                        break;
                }
                break;
            }
            case ATTRIBUTE_STEP: {
                switch (carac) {
                    case '=':
                        flush();
                        appendOperator("=");
                        step = WAITING_QUOTE_STEP;
                        appendToCurrent = false;
                        break;
                    case ':':
                        step = ATTRIBUTE_NAMESPACE_STEP;
                        flush();
                        appendOperator(":");
                        appendToCurrent = false;
                        step = ATTRIBUTE_STEP;
                        break;
                }
                break;
            }
            case TAG_STEP: {
                switch (carac) {
                    case '>':
                        flush();
                        appendXmlChar(">");
                        step = WAITING_TAG_STEP;
                        appendToCurrent = false;
                        break;
                    case '/':
                        flush();
                        appendXmlChar("/");
                        appendToCurrent = false;
                        break;
                    case ':':
                        step = NAMESPACE_STEP;
                        flush();
                        appendOperator(":");
                        appendToCurrent = false;
                        step = TAG_STEP;
                        break;
                }
                break;
            }
            case ONQUOTE_STEP: {
                if (carac == currentQuote) {
                    appendToCurrent(carac);
                    flush();
                    step = WAITING_TAG_CONTENT_STEP;
                    appendToCurrent = false;
                }
                break;
            }
            case WAITING_ATTRIBUTE_OPERATOR_STEP: {
                switch (carac) {
                    case '=':
                        appendOperator("=");
                        appendToCurrent = false;
                        step = WAITING_QUOTE_STEP;
                        break;
                    default:
                        step = ERROR_STEP;
                }
                break;
            }
            case WAITING_TAG_STEP: {
                switch (carac) {
                    case '<':
                        flush();
                        appendXmlChar("<");
                        step = TAG_STEP;
                        appendToCurrent = false;
                        break;
                }
                break;
            }
            case WAITING_TAG_CONTENT_STEP: {
                switch (carac) {
                    case '>':
                        appendXmlChar(">");
                        step = WAITING_TAG_STEP;
                        appendToCurrent = false;
                        break;
                    case '/':
                        step = TAG_STEP;
                        break;
                    default:
                        step = ATTRIBUTE_STEP;
                }
                break;
            }
            case WAITING_QUOTE_STEP: {
                switch (carac) {
                    case '\'':
                    case '"':
                        currentQuote = carac;
                        step = ONQUOTE_STEP;
                        break;
                    default:
                        step = ERROR_STEP;
                }
                break;
            }
        }
        if (appendToCurrent) {
            appendToCurrent(carac);
        }
    }

    private void appendXmlChar(String text) {
        appendToFinal("cm-xml-char", text);
    }


    @Override
    public String getStepClassName(String textPart, boolean end) {
        switch (step) {
            case TAG_STEP:
                return "cm-xml-tag";
            case ATTRIBUTE_STEP:
                return "cm-xml-attr-name";
            case ERROR_STEP:
                return "cm-error";
            case ONQUOTE_STEP:
                return "cm-string";
            case NAMESPACE_STEP:
                return "cm-xml-namespace";
            case ATTRIBUTE_NAMESPACE_STEP:
                return "cm-xml-attr-ns";
            default:
                return null;
        }
    }

    public static String resolve(String value) {
        if (value.isEmpty()) {
            return "";
        }
        XmlResolver resolver = new XmlResolver();
        resolver.resolveText(value);
        return resolver.end();
    }


}
