/* OdLib_Css - Copyright (c) 2007-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.mapeadores.opendocument.css.parse;

import java.util.ArrayList;
import java.util.List;
import net.mapeadores.opendocument.elements.ElementMaps;
import net.mapeadores.opendocument.elements.OdElement;
import org.w3c.css.sac.AttributeCondition;
import org.w3c.css.sac.CSSException;
import org.w3c.css.sac.CombinatorCondition;
import org.w3c.css.sac.Condition;
import org.w3c.css.sac.ConditionalSelector;
import org.w3c.css.sac.DocumentHandler;
import org.w3c.css.sac.ElementSelector;
import org.w3c.css.sac.InputSource;
import org.w3c.css.sac.LexicalUnit;
import org.w3c.css.sac.SACMediaList;
import org.w3c.css.sac.Selector;
import org.w3c.css.sac.SelectorList;
import org.w3c.css.sac.SimpleSelector;


/**
 *
 * @author Vincent Calame
 */
public class OdCssDocumentHandler implements DocumentHandler {

    private final ElementMaps elementMaps = new ElementMaps();
    private final List<OdElement> elementList = new ArrayList<OdElement>();
    private final List<String> importList = new ArrayList<String>();

    public OdCssDocumentHandler() {
    }

    public List<String> getImportList() {
        return importList;
    }

    public void charset(String characterEncoding) throws CSSException {
    }

    public ElementMaps getElementMaps() {
        return elementMaps;
    }

    @Override
    public void startPage(String string, String string0) throws CSSException {
    }

    @Override
    public void endFontFace() throws CSSException {
    }

    @Override
    public void endPage(String string, String string0) throws CSSException {
    }

    @Override
    public void importStyle(String string, SACMediaList sACMediaList, String string0) throws CSSException {
        importList.add(string);
    }

    @Override
    public void namespaceDeclaration(String string, String string0) throws CSSException {
    }

    @Override
    public void startFontFace() throws CSSException {
    }

    @Override
    public void comment(String string) throws CSSException {
    }

    @Override
    public void ignorableAtRule(String string) throws CSSException {
    }

    @Override
    public void startSelector(SelectorList selectorList) throws CSSException {
        elementList.clear();
        test(selectorList);
    }

    @Override
    public void endSelector(SelectorList selectorList) throws CSSException {
        elementList.clear();
    }

    @Override
    public void endDocument(InputSource inputSource) throws CSSException {
        elementList.clear();
    }

    @Override
    public void startDocument(InputSource inputSource) throws CSSException {
    }

    @Override
    public void property(String name, LexicalUnit lexicalUnit, boolean b) throws CSSException {
        if (!elementList.isEmpty()) {
            String value = getValue(lexicalUnit);
            putAttribute(name, value);
        }
    }

    @Override
    public void endMedia(SACMediaList sACMediaList) throws CSSException {
    }

    @Override
    public void startMedia(SACMediaList sACMediaList) throws CSSException {
    }

    private String getValue(LexicalUnit lexicalUnit) {
        StringBuilder buf = new StringBuilder();
        buf.append(toString(lexicalUnit));
        LexicalUnit next = lexicalUnit.getNextLexicalUnit();
        while (next != null) {
            buf.append(' ');
            buf.append(toString(next));
            next = next.getNextLexicalUnit();
        }
        return buf.toString();
    }

    private String toString(LexicalUnit lexicalUnit) {
        short type = lexicalUnit.getLexicalUnitType();
        switch (type) {
            case LexicalUnit.SAC_INTEGER:
                return Integer.toString(lexicalUnit.getIntegerValue(), 10);
            case LexicalUnit.SAC_REAL:
                return lexicalUnit.getFloatValue() + "";
            case LexicalUnit.SAC_EM:
            case LexicalUnit.SAC_EX:
            case LexicalUnit.SAC_PIXEL:
            case LexicalUnit.SAC_INCH:
            case LexicalUnit.SAC_CENTIMETER:
            case LexicalUnit.SAC_MILLIMETER:
            case LexicalUnit.SAC_POINT:
            case LexicalUnit.SAC_PICA:
            case LexicalUnit.SAC_PERCENTAGE:
            case LexicalUnit.SAC_DEGREE:
            case LexicalUnit.SAC_GRADIAN:
            case LexicalUnit.SAC_RADIAN:
            case LexicalUnit.SAC_MILLISECOND:
            case LexicalUnit.SAC_SECOND:
            case LexicalUnit.SAC_HERTZ:
            case LexicalUnit.SAC_KILOHERTZ:
            case LexicalUnit.SAC_DIMENSION:
                float f = lexicalUnit.getFloatValue();
                int i = (int) f;
                if (((float) i) == f) {
                    return i + lexicalUnit.getDimensionUnitText();
                } else {
                    return f + lexicalUnit.getDimensionUnitText();
                }

            case LexicalUnit.SAC_RGBCOLOR:
                return colorToString(lexicalUnit.getParameters());
            case LexicalUnit.SAC_STRING_VALUE:
            case LexicalUnit.SAC_IDENT:
            case LexicalUnit.SAC_ATTR:
                return lexicalUnit.getStringValue();
            case LexicalUnit.SAC_OPERATOR_COMMA:
                return ",";
            case LexicalUnit.SAC_OPERATOR_PLUS:
                return "+";

            case LexicalUnit.SAC_OPERATOR_MINUS:
                return "-";

            case LexicalUnit.SAC_OPERATOR_MULTIPLY:
                return "*";

            case LexicalUnit.SAC_OPERATOR_SLASH:
                return "/";

            case LexicalUnit.SAC_OPERATOR_MOD:
                return "%";

            case LexicalUnit.SAC_OPERATOR_EXP:
                return "^";

            case LexicalUnit.SAC_OPERATOR_LT:
                return "<";

            case LexicalUnit.SAC_OPERATOR_GT:
                return ">";

            case LexicalUnit.SAC_OPERATOR_LE:
                return "<=";
            case LexicalUnit.SAC_OPERATOR_GE:
                return "=>";
            case LexicalUnit.SAC_OPERATOR_TILDE:
                return "~";
            default:
                return "";
        }
    }

    private String colorToString(LexicalUnit redLexicalUnit) {
        StringBuffer buf = new StringBuffer();
        buf.append("#");
        appendHexaColor(buf, redLexicalUnit.getIntegerValue());
        LexicalUnit green = redLexicalUnit.getNextLexicalUnit().getNextLexicalUnit();
        appendHexaColor(buf, green.getIntegerValue());
        LexicalUnit blue = green.getNextLexicalUnit().getNextLexicalUnit();
        appendHexaColor(buf, blue.getIntegerValue());
        return buf.toString();
    }

    private void appendHexaColor(StringBuffer buf, int i) {
        String s = Integer.toString(i, 16);
        if (s.length() == 1) {
            buf.append('0');
        }
        buf.append(s);
    }

    private void putAttribute(String name, String value) {
        for (int i = 0; i < elementList.size(); i++) {
            OdElement odClass = elementList.get(i);
            odClass.putAttribute(name, value);
        }
    }

    private void test(SelectorList selectorList) {
        int size = selectorList.getLength();
        for (int i = 0; i < size; i++) {
            Selector selector = selectorList.item(i);
            OdElement abstractElement = processSelector(selector);
            if (abstractElement != null) {
                elementList.add(abstractElement);
            }
        }
    }

    private OdElement processSelector(Selector selector) {
        if (selector instanceof ElementSelector) {
            return processElementSelector((ElementSelector) selector);
        }
        if (selector instanceof ConditionalSelector) {
            return processConditionalSelector((ConditionalSelector) selector);
        }
        return null;
    }

    private OdElement processElementSelector(ElementSelector elementSelector) {
        String localName = elementSelector.getLocalName();
        if (localName == null) {
            return null;
        }
        return elementMaps.getElement(localName, null, true);
    }

    private OdElement processConditionalSelector(ConditionalSelector sel) {
        SimpleSelector ssel = sel.getSimpleSelector();
        if (!(ssel instanceof ElementSelector)) {
            return null;
        }
        String localName = ((ElementSelector) ssel).getLocalName();
        if (localName == null) {
            return null;
        }
        Condition condition = sel.getCondition();
        short conditionType = condition.getConditionType();
        if (condition.getConditionType() == Condition.SAC_CLASS_CONDITION) {
            String styleName = ((AttributeCondition) condition).getValue();
            return elementMaps.getElement(localName, styleName, true);
        } else if (condition.getConditionType() == Condition.SAC_AND_CONDITION) {
            CombinatorCondition combinatorCondition = (CombinatorCondition) condition;
            Condition first = combinatorCondition.getFirstCondition();
            Condition second = combinatorCondition.getSecondCondition();
            if ((first.getConditionType() == Condition.SAC_CLASS_CONDITION) && (second.getConditionType() == Condition.SAC_ATTRIBUTE_CONDITION)) {
                String styleName = ((AttributeCondition) first).getValue();
                AttributeCondition attributeCondition = (AttributeCondition) second;
                String attributeName = attributeCondition.getLocalName();
                String attributeValue = attributeCondition.getValue();
                return elementMaps.getElement(localName, styleName, attributeName, attributeValue, true);
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

}
