package org.openstreetmap.josm.gui.mappaint;

import java.awt.Color;
import java.util.Collection;
import java.util.LinkedList;

import javax.swing.ImageIcon;

import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.tools.ColorHelper;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

public class ElemStyleHandler extends DefaultHandler
{
    boolean inDoc, inRule, inCondition, inLine, inLineMod, inIcon, inArea, inScaleMax, inScaleMin;
    boolean hadLine, hadLineMod, hadIcon, hadArea;
    ElemStyles styles;
    String styleName;
    RuleElem rule = new RuleElem();

    static class RuleElem {
        Rule rule = new Rule();
        Collection<Rule> rules;
        long scaleMax;
        long scaleMin;
        LineElemStyle line = new LineElemStyle();
        LineElemStyle linemod = new LineElemStyle();
        AreaElemStyle area = new AreaElemStyle();
        IconElemStyle icon = new IconElemStyle();
        public void init()
        {
            rules = null;
            scaleMax = 1000000000;
            scaleMin = 0;
            line.init();
            rule.init();
            linemod.init();
            area.init();
            icon.init();
        }
    }

    public ElemStyleHandler(String name) {
        styleName = name;
        inDoc=inRule=inCondition=inLine=inIcon=inArea=false;
        rule.init();
        styles = MapPaintStyles.getStyles();
    }

    Color convertColor(String colString)
    {
        int i = colString.indexOf("#");
        Color ret;
        if(i < 0) {
            ret = Main.pref.getColor("mappaint."+styleName+"."+colString, Color.red);
        } else if(i == 0) {
            ret = ColorHelper.html2color(colString);
        } else {
            ret = Main.pref.getColor("mappaint."+styleName+"."+colString.substring(0,i),
                    ColorHelper.html2color(colString.substring(i)));
        }
        return ret;
    }

    @Override public void startDocument() {
        inDoc = true;
    }

    @Override public void endDocument() {
        inDoc = false;
    }

    private void error(String message) {
        System.out.println(styleName + " (" + rule.rule.key + "=" + rule.rule.value + "): " + message);
    }

    private void startElementLine(String qName, Attributes atts, LineElemStyle line) {
        for (int count=0; count<atts.getLength(); count++)
        {
            if(atts.getQName(count).equals("width"))
            {
                String val = atts.getValue(count);
                if(val.startsWith("+"))
                {
                    line.setWidth(Integer.parseInt(val.substring(1)));
                    line.widthMode = LineElemStyle.WidthMode.OFFSET;
                }
                else if(val.startsWith("-"))
                {
                    line.setWidth(Integer.parseInt(val));
                    line.widthMode = LineElemStyle.WidthMode.OFFSET;
                }
                else if(val.endsWith("%"))
                {
                    line.setWidth(Integer.parseInt(val.substring(0, val.length()-1)));
                    line.widthMode = LineElemStyle.WidthMode.PERCENT;
                } else {
                    line.setWidth(Integer.parseInt(val));
                }
            }
            else if (atts.getQName(count).equals("colour")) {
                line.color=convertColor(atts.getValue(count));
            } else if (atts.getQName(count).equals("realwidth")) {
                line.realWidth=Integer.parseInt(atts.getValue(count));
            } else if (atts.getQName(count).equals("dashed")) {
                float[] dashed;
                try {
                    String[] parts = atts.getValue(count).split(",");
                    dashed = new float[parts.length];
                    for (int i = 0; i < parts.length; i++) {
                        dashed[i] = (Integer.parseInt(parts[i]));
                    }
                } catch (NumberFormatException nfe) {
                    boolean isDashed = Boolean.parseBoolean(atts.getValue(count));
                    if(isDashed) {
                        dashed = new float[]{9};
                    } else {
                        dashed = new float[0];
                    }
                }
                line.setDashed(dashed);
            } else if (atts.getQName(count).equals("dashedcolour")) {
                line.dashedColor=convertColor(atts.getValue(count));
            } else if(atts.getQName(count).equals("priority")) {
                line.priority = Integer.parseInt(atts.getValue(count));
            } else if(atts.getQName(count).equals("mode")) {
                line.over = !atts.getValue(count).equals("under");
            } else {
                error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
            }
        }
    }

    @Override public void startElement(String uri,String name, String qName, Attributes atts) {
        if (inDoc==true)
        {
            if (qName.equals("rule")) {
                inRule=true;
            } else if (qName.equals("rules"))
            {
                if(styleName == null)
                {
                    String n = atts.getValue("name");
                    if(n == null) {
                        n = "standard";
                    }
                    styleName = n;
                }
            }
            else if (qName.equals("scale_max")) {
                inScaleMax = true;
            } else if (qName.equals("scale_min")) {
                inScaleMin = true;
            } else if (qName.equals("condition") && inRule)
            {
                inCondition=true;
                Rule r = rule.rule;
                if(r.key != null)
                {
                    if(rule.rules == null) {
                        rule.rules = new LinkedList<Rule>();
                    }
                    rule.rules.add(new Rule(rule.rule));
                    r = new Rule();
                    rule.rules.add(r);
                }
                for (int count=0; count<atts.getLength(); count++)
                {
                    if(atts.getQName(count).equals("k")) {
                        r.key = atts.getValue(count);
                    } else if(atts.getQName(count).equals("v")) {
                        r.value = atts.getValue(count);
                    } else if(atts.getQName(count).equals("b")) {
                        r.boolValue = atts.getValue(count);
                    } else {
                        error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
                    }
                }
                if(r.key == null) {
                    error("The condition has no key!");
                }
            }
            else if (qName.equals("line"))
            {
                hadLine = inLine = true;
                startElementLine(qName, atts, rule.line);
                if(rule.line.widthMode != LineElemStyle.WidthMode.ABSOLUTE) {
                    error("Relative widths are not possible for normal lines");
                    rule.line.widthMode = LineElemStyle.WidthMode.ABSOLUTE;
                }
            }
            else if (qName.equals("linemod"))
            {
                hadLineMod = inLineMod = true;
                startElementLine(qName, atts, rule.linemod);
            }
            else if (qName.equals("icon"))
            {
                inIcon = true;
                for (int count=0; count<atts.getLength(); count++)
                {
                    if (atts.getQName(count).equals("src")) {
                        ImageIcon icon = MapPaintStyles.getIcon(atts.getValue(count), styleName);
                        hadIcon = (icon != null);
                        rule.icon.icon = icon;
                    } else if (atts.getQName(count).equals("annotate")) {
                        rule.icon.annotate = Boolean.parseBoolean (atts.getValue(count));
                    } else if(atts.getQName(count).equals("priority")) {
                        rule.icon.priority = Integer.parseInt(atts.getValue(count));
                    } else {
                        error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
                    }
                }
            }
            else if (qName.equals("area"))
            {
                hadArea = inArea = true;
                for (int count=0; count<atts.getLength(); count++)
                {
                    if (atts.getQName(count).equals("colour")) {
                        rule.area.color=convertColor(atts.getValue(count));
                    } else if (atts.getQName(count).equals("closed")) {
                        rule.area.closed=Boolean.parseBoolean(atts.getValue(count));
                    } else if(atts.getQName(count).equals("priority")) {
                        rule.area.priority = Integer.parseInt(atts.getValue(count));
                    } else {
                        error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
                    }
                }
            } else {
                error("The element \"" + qName + "\" is unknown!");
            }
        }
    }

    @Override public void endElement(String uri,String name, String qName)
    {
        if (inRule && qName.equals("rule"))
        {
            if(hadLine)
            {
                styles.add(styleName, rule.rule, rule.rules,
                        new LineElemStyle(rule.line, rule.scaleMax, rule.scaleMin));
            }
            if(hadLineMod)
            {
                styles.addModifier(styleName, rule.rule, rule.rules,
                        new LineElemStyle(rule.linemod, rule.scaleMax, rule.scaleMin));
            }
            if(hadIcon)
            {
                styles.add(styleName, rule.rule, rule.rules,
                        new IconElemStyle(rule.icon, rule.scaleMax, rule.scaleMin));
            }
            if(hadArea)
            {
                styles.add(styleName, rule.rule, rule.rules,
                        new AreaElemStyle(rule.area, rule.scaleMax, rule.scaleMin));
            }
            inRule = false;
            hadLine = hadLineMod = hadIcon = hadArea = false;
            rule.init();
        }
        else if (inCondition && qName.equals("condition")) {
            inCondition = false;
        } else if (inLine && qName.equals("line")) {
            inLine = false;
        } else if (inLineMod && qName.equals("linemod")) {
            inLineMod = false;
        } else if (inIcon && qName.equals("icon")) {
            inIcon = false;
        } else if (inArea && qName.equals("area")) {
            inArea = false;
        } else if (qName.equals("scale_max")) {
            inScaleMax = false;
        } else if (qName.equals("scale_min")) {
            inScaleMin = false;
        }
    }

    @Override public void characters(char ch[], int start, int length)
    {
        if (inScaleMax == true) {
            rule.scaleMax = Long.parseLong(new String(ch, start, length));
        } else if (inScaleMin == true) {
            rule.scaleMin = Long.parseLong(new String(ch, start, length));
        }
    }
}
