/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, GeoTools Project Managment Committee (PMC)
 *    
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.styling;

import org.geotools.filter.FilterAttributeExtractor;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.ExpressionVisitor;


/**
 * A simple visitor whose purpose is to extract the set of attributes used by a
 * Style, that is, those that the Style expects to find in order to work
 * properly
 *
 * @author wolf
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/styling/StyleAttributeExtractor.java $
 */


/**
  * ===========================================================================
  * if you're modifying this, you probably should also take a look at StyleAttributeExtractorTruncated
  * ===========================================================================
  *
 */

public class StyleAttributeExtractor extends FilterAttributeExtractor
    implements StyleVisitor {

	/**
	 *   if the default geometry is used, this will be true.  See GEOS-469
	 */
	boolean defaultGeometryUsed = false;

	/**
	 * reads the read-only-property.
	 * See GEOS-469
	 *
	 * @return true if any of the symbolizers visted use the default geometry.
	 */
	public boolean getDefaultGeometryUsed()
	{
		return defaultGeometryUsed;
	}

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Style)
     */
    public void visit(Style style) {
        FeatureTypeStyle[] ftStyles = style.getFeatureTypeStyles();

        for (int i = 0; i < ftStyles.length; i++) {
            ftStyles[i].accept(this);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Rule)
     */
    public void visit(Rule rule) {
        Filter filter = rule.getFilter();

        if (filter != null) {
            filter.accept( this, null );
        }

        Symbolizer[] symbolizers = rule.getSymbolizers();

        if (symbolizers != null) {
            for (int i = 0; i < symbolizers.length; i++) {
                Symbolizer symbolizer = symbolizers[i];
                symbolizer.accept(this);
            }
        }

        Graphic[] legendGraphics = rule.getLegendGraphic();

        if (legendGraphics != null) {
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.FeatureTypeStyle)
     */
    public void visit(FeatureTypeStyle fts) {
        Rule[] rules = fts.getRules();

        for (int i = 0; i < rules.length; i++) {
            Rule rule = rules[i];
            rule.accept(this);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Fill)
     */
    public void visit(Fill fill) {
        if (fill.getBackgroundColor() != null) {
            fill.getBackgroundColor().accept((ExpressionVisitor) this, null);
        }

        if (fill.getColor() != null) {
            fill.getColor().accept(this, null );
        }

        if (fill.getGraphicFill() != null) {
            fill.getGraphicFill().accept(this);
        }

        if (fill.getOpacity() != null) {
            fill.getOpacity().accept(this, null );
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Stroke)
     */
    public void visit(Stroke stroke) {
        if (stroke.getColor() != null) {
            stroke.getColor().accept(this, null );
        }

        if (stroke.getDashOffset() != null) {
            stroke.getDashOffset().accept(this, null );
        }

        if (stroke.getGraphicFill() != null) {
            stroke.getGraphicFill().accept(this);
        }

        if (stroke.getGraphicStroke() != null) {
            stroke.getGraphicStroke().accept(this);
        }

        if (stroke.getLineCap() != null) {
            stroke.getLineCap().accept(this, null);
        }

        if (stroke.getLineJoin() != null) {
            stroke.getLineJoin().accept(this, null);
        }

        if (stroke.getOpacity() != null) {
            stroke.getOpacity().accept(this, null);
        }

        if (stroke.getWidth() != null) {
            stroke.getWidth().accept(this, null );
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Symbolizer)
     */
    public void visit(Symbolizer sym) {
        if (sym instanceof PointSymbolizer) {
            visit((PointSymbolizer) sym);
        }

        if (sym instanceof LineSymbolizer) {
            visit((LineSymbolizer) sym);
        }

        if (sym instanceof PolygonSymbolizer) {
            visit((PolygonSymbolizer) sym);
        }

        if (sym instanceof TextSymbolizer) {
            visit((TextSymbolizer) sym);
        }

        if (sym instanceof RasterSymbolizer) {
            visit((RasterSymbolizer) sym);
        }
    }

    public void visit(RasterSymbolizer rs) {
        if (rs.getGeometryPropertyName() != null) {
            attributeNames.add(rs.getGeometryPropertyName());

            // FIXME
            // LiteRenderer2 trhwos an Exception:
            //  Do not know how to deep copy org.geotools.coverage.grid.GridCoverage2D
            // attributeNames.add("grid");
        }

        if (rs.getImageOutline() != null) {
            rs.getImageOutline().accept(this);
        }

        if (rs.getOpacity() != null) {
            rs.getOpacity().accept(this, null);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.PointSymbolizer)
     */
    public void visit(PointSymbolizer ps) {
        if (ps.getGeometryPropertyName() != null) {
            attributeNames.add(ps.getGeometryPropertyName());
        }
        else
        {
        	this.defaultGeometryUsed = true; // they want the default geometry (see GEOS-469)
        }

        if (ps.getGraphic() != null) {
            ps.getGraphic().accept(this);
        }

    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.LineSymbolizer)
     */
    public void visit(LineSymbolizer line) {
        if (line.getGeometryPropertyName() != null) {
            attributeNames.add(line.getGeometryPropertyName());
        }
        else
        {
        	this.defaultGeometryUsed = true; // they want the default geometry (see GEOS-469)
        }

        if (line.getStroke() != null) {
            line.getStroke().accept(this);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.PolygonSymbolizer)
     */
    public void visit(PolygonSymbolizer poly) {
        if (poly.getGeometryPropertyName() != null) {
            attributeNames.add(poly.getGeometryPropertyName());
        }
        else
        {
        	this.defaultGeometryUsed = true; // they want the default geometry (see GEOS-469)
        }

        if (poly.getStroke() != null) {
            poly.getStroke().accept(this);
        }

        if (poly.getFill() != null) {
            poly.getFill().accept(this);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.TextSymbolizer)
     */
    public void visit(TextSymbolizer text) {
        if (text.getGeometryPropertyName() != null) {
            attributeNames.add(text.getGeometryPropertyName());
        }
        else
        {
        	this.defaultGeometryUsed = true; // they want the default geometry (see GEOS-469)
        }

        if (text instanceof TextSymbolizer2)
        {
        	if ( ((TextSymbolizer2)text).getGraphic() !=null)
        		((TextSymbolizer2)text).getGraphic().accept(this);
        }

        if (text.getFill() != null) {
            text.getFill().accept(this);
        }

        if (text.getHalo() != null) {
            text.getHalo().accept(this);
        }

        if (text.getFonts() != null) {
            Font[] fonts = text.getFonts();

            for (int i = 0; i < fonts.length; i++) {
                Font font = fonts[i];

                if (font.getFontFamily() != null) {
                    font.getFontFamily().accept(this,null);
                }

                if (font.getFontSize() != null) {
                    font.getFontSize().accept(this,null);
                }

                if (font.getFontStyle() != null) {
                    font.getFontStyle().accept(this,null);
                }

                if (font.getFontWeight() != null) {
                    font.getFontWeight().accept(this,null);
                }
            }
        }

        if (text.getHalo() != null) {
            text.getHalo().accept(this);
        }

        if (text.getLabel() != null) {
            text.getLabel().accept(this,null);
        }

        if (text.getPlacement() != null) {
            text.getPlacement().accept(this);
        }

        if (text.getPriority() != null) {
            text.getPriority().accept(this,null);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Graphic)
     */
    public void visit(Graphic gr) {
        if (gr.getSymbols() != null) {
            Symbol[] symbols = gr.getSymbols();

            for (int i = 0; i < symbols.length; i++) {
                Symbol symbol = symbols[i];
                symbol.accept(this);
            }
        }

        if (gr.getOpacity() != null) {
            gr.getOpacity().accept(this,null);
        }

        if (gr.getRotation() != null) {
            gr.getRotation().accept(this,null);
        }

        if (gr.getSize() != null) {
            gr.getSize().accept(this,null);
        }
        
        if (gr.getDisplacement() != null)
            gr.getDisplacement().accept(this);
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Mark)
     */
    public void visit(Mark mark) {
        if (mark.getFill() != null) {
            mark.getFill().accept(this);
        }

        if (mark.getStroke() != null) {
            mark.getStroke().accept(this);
        }

        if (mark.getRotation() != null) {
            mark.getRotation().accept(this,null);
        }

        if (mark.getSize() != null) {
            mark.getSize().accept(this,null);
        }
        
        if(mark.getWellKnownName() != null)
            mark.getWellKnownName().accept(this,null);
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.ExternalGraphic)
     */
    public void visit(ExternalGraphic exgr) {
        // nothing to do
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.PointPlacement)
     */
    public void visit(PointPlacement pp) {
        if (pp.getAnchorPoint() != null) {
            pp.getAnchorPoint().accept(this);
        }

        if (pp.getDisplacement() != null) {
            pp.getDisplacement().accept(this);
        }

        if (pp.getRotation() != null) {
            pp.getRotation().accept(this,null);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.AnchorPoint)
     */
    public void visit(AnchorPoint ap) {
        if (ap.getAnchorPointX() != null) {
            ap.getAnchorPointX().accept(this,null);
        }

        if (ap.getAnchorPointY() != null) {
            ap.getAnchorPointY().accept(this,null);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Displacement)
     */
    public void visit(Displacement dis) {
        if (dis.getDisplacementX() != null) {
            dis.getDisplacementX().accept(this,null);
        }

        if (dis.getDisplacementY() != null) {
            dis.getDisplacementY().accept(this,null);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.LinePlacement)
     */
    public void visit(LinePlacement lp) {
        if (lp.getPerpendicularOffset() != null) {
            lp.getPerpendicularOffset().accept(this,null);
        }
    }

    /**
     * @see org.geotools.styling.StyleVisitor#visit(org.geotools.styling.Halo)
     */
    public void visit(Halo halo) {
        if (halo.getFill() != null) {
            halo.getFill().accept(this);
        }

        if (halo.getRadius() != null) {
            halo.getRadius().accept(this,null);
        }
    }

    public void visit(StyledLayerDescriptor sld) {
        StyledLayer[] layers = sld.getStyledLayers();

        for (int i = 0; i < layers.length; i++) {
            if (layers[i] instanceof NamedLayer) {
                ((NamedLayer) layers[i]).accept(this);
            } else if (layers[i] instanceof UserLayer) {
                ((UserLayer) layers[i]).accept(this);
            }
        }
    }

    public void visit(NamedLayer layer) {
        Style[] styles = layer.getStyles();

        for (int i = 0; i < styles.length; i++) {
            styles[i].accept(this);
        }
    }

    public void visit(UserLayer layer) {
        Style[] styles = layer.getUserStyles();

        for (int i = 0; i < styles.length; i++) {
            styles[i].accept(this);
        }
    }

    public void visit(FeatureTypeConstraint ftc) {
        // TODO Auto-generated method stub
    }

	public void visit(ColorMap map) {
		ColorMapEntry[] entries = map.getColorMapEntries();

		for (int i = 0; i < entries.length; i++) {
			entries[i].accept(this);
		}
	}

	public void visit(ColorMapEntry entry) {
		// TODO Auto-generated method stub
	}
}
