/*
 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
 *               2007 Rob Buis <buis@kde.org>
 *               2008 Dirk Schulze <krit@webkit.org>
 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"

#if ENABLE(SVG)
#include "RenderSVGResource.h"

#include "RenderSVGResourceClipper.h"
#include "RenderSVGResourceContainer.h"
#include "RenderSVGResourceFilter.h"
#include "RenderSVGResourceMarker.h"
#include "RenderSVGResourceMasker.h"
#include "RenderSVGResourceSolidColor.h"
#include "SVGURIReference.h"

namespace WebCore {

static inline void registerPendingResource(const AtomicString& id, const SVGPaint::SVGPaintType& paintType, const RenderObject* object)
{
    if (paintType != SVGPaint::SVG_PAINTTYPE_URI)
        return;

    SVGElement* svgElement = static_cast<SVGElement*>(object->node());
    ASSERT(svgElement);
    ASSERT(svgElement->isStyled());

    object->document()->accessSVGExtensions()->addPendingResource(id, static_cast<SVGStyledElement*>(svgElement));
}

inline void RenderSVGResource::adjustColorForPseudoRules(const RenderStyle* style, bool useFillPaint, Color& color)
{
    if (style->insideLink() != InsideVisitedLink)
        return;

    RenderStyle* visitedStyle = style->getCachedPseudoStyle(VISITED_LINK);
    SVGPaint* visitedPaint = useFillPaint ? visitedStyle->svgStyle()->fillPaint() : visitedStyle->svgStyle()->strokePaint();
    if (visitedPaint->paintType() == SVGPaint::SVG_PAINTTYPE_URI)
        return;
        
    Color visitedColor;
    if (visitedPaint->paintType() == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR)
        visitedColor = visitedStyle->color();
    else
        visitedColor = visitedPaint->color();

    if (visitedColor.isValid())
        color = Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), color.alpha());
}

// FIXME: This method and strokePaintingResource() should be refactored, to share even more code
RenderSVGResource* RenderSVGResource::fillPaintingResource(const RenderObject* object, const RenderStyle* style)
{
    ASSERT(object);
    ASSERT(style);

    const SVGRenderStyle* svgStyle = style->svgStyle();
    if (!svgStyle || !svgStyle->hasFill())
        return 0;

    SVGPaint* fillPaint = svgStyle->fillPaint();
    ASSERT(fillPaint);

    RenderSVGResource* fillPaintingResource = 0;

    SVGPaint::SVGPaintType paintType = fillPaint->paintType();
    if (paintType == SVGPaint::SVG_PAINTTYPE_URI
        || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR) {
        AtomicString id(SVGURIReference::getTarget(fillPaint->uri()));
        fillPaintingResource = getRenderSVGResourceContainerById(object->document(), id);

        if (!fillPaintingResource)
            registerPendingResource(id, paintType, object);
    }

    if (paintType != SVGPaint::SVG_PAINTTYPE_URI && !fillPaintingResource) {
        RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource();
        fillPaintingResource = solidResource;

        Color fillColor;
        if (fillPaint->paintType() == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR)
            fillColor = style->visitedDependentColor(CSSPropertyColor);
        else
            fillColor = fillPaint->color();

        adjustColorForPseudoRules(style, true /* useFillPaint */, fillColor);

        // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT
        if (fillColor.isValid())
            solidResource->setColor(fillColor);
        else
            fillPaintingResource = 0;
    }

    if (!fillPaintingResource) {
        // default value (black), see bug 11017
        RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource();
        solidResource->setColor(Color::black);
        fillPaintingResource = solidResource;
    }

    return fillPaintingResource;
}

RenderSVGResource* RenderSVGResource::strokePaintingResource(const RenderObject* object, const RenderStyle* style)
{
    ASSERT(object);
    ASSERT(style);

    const SVGRenderStyle* svgStyle = style->svgStyle();
    if (!svgStyle || !svgStyle->hasStroke())
        return 0;

    SVGPaint* strokePaint = svgStyle->strokePaint();
    ASSERT(strokePaint);

    RenderSVGResource* strokePaintingResource = 0;
    FloatRect objectBoundingBox = object->objectBoundingBox();

    SVGPaint::SVGPaintType paintType = strokePaint->paintType();
    if (!objectBoundingBox.isEmpty()
        && (paintType == SVGPaint::SVG_PAINTTYPE_URI || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR)) {
        AtomicString id(SVGURIReference::getTarget(strokePaint->uri()));
        strokePaintingResource = getRenderSVGResourceContainerById(object->document(), id);

        if (!strokePaintingResource)
            registerPendingResource(id, paintType, object);
    }

    if (paintType != SVGPaint::SVG_PAINTTYPE_URI && !strokePaintingResource) {
        RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource();
        strokePaintingResource = solidResource;

        Color strokeColor;
        if (strokePaint->paintType() == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR)
            strokeColor = style->visitedDependentColor(CSSPropertyColor);
        else
            strokeColor = strokePaint->color();

        adjustColorForPseudoRules(style, false /* useFillPaint */, strokeColor);

        // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT
        if (strokeColor.isValid())
            solidResource->setColor(strokeColor);
        else
            strokePaintingResource = 0;
    }

    if (!strokePaintingResource) {
        // default value (black), see bug 11017
        RenderSVGResourceSolidColor* solidResource = sharedSolidPaintingResource();
        solidResource->setColor(Color::black);
        strokePaintingResource = solidResource;
    }

    return strokePaintingResource;
}

RenderSVGResourceSolidColor* RenderSVGResource::sharedSolidPaintingResource()
{
    static RenderSVGResourceSolidColor* s_sharedSolidPaintingResource = 0;
    if (!s_sharedSolidPaintingResource)
        s_sharedSolidPaintingResource = new RenderSVGResourceSolidColor;
    return s_sharedSolidPaintingResource;
}

void RenderSVGResource::markForLayoutAndResourceInvalidation(RenderObject* object, bool needsBoundariesUpdate)
{
    ASSERT(object);
    ASSERT(object->node());
    ASSERT(object->node()->isSVGElement());

    // Eventually mark the renderer needing a boundaries update
    if (needsBoundariesUpdate)
        object->setNeedsBoundariesUpdate();

    markForLayoutAndParentResourceInvalidation(object);
}

static inline void invalidatePaintingResource(SVGPaint* paint, RenderObject* object)
{
    ASSERT(paint);

    SVGPaint::SVGPaintType paintType = paint->paintType();
    if (paintType != SVGPaint::SVG_PAINTTYPE_URI && paintType != SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR)
        return;

    AtomicString id(SVGURIReference::getTarget(paint->uri()));
    if (RenderSVGResourceContainer* paintingResource = getRenderSVGResourceContainerById(object->document(), id))
        paintingResource->invalidateClient(object);
}

void RenderSVGResource::invalidateAllResourcesOfRenderer(RenderObject* object)
{
    ASSERT(object);
    ASSERT(object->style());

    Document* document = object->document();
    ASSERT(document);

    const SVGRenderStyle* svgStyle = object->style()->svgStyle();
    ASSERT(svgStyle);

    // Masker
    if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(document, svgStyle->maskerResource()))
        masker->invalidateClient(object);

    // Clipper
    if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(document, svgStyle->clipperResource()))
        clipper->invalidateClient(object);

    // Filter
#if ENABLE(FILTERS)
    if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(document, svgStyle->filterResource()))
        filter->invalidateClient(object);
#endif

    // Markers
    if (RenderSVGResourceMarker* startMarker = getRenderSVGResourceById<RenderSVGResourceMarker>(document, svgStyle->markerStartResource()))
        startMarker->invalidateClient(object);
    if (RenderSVGResourceMarker* midMarker = getRenderSVGResourceById<RenderSVGResourceMarker>(document, svgStyle->markerMidResource()))
        midMarker->invalidateClient(object);
    if (RenderSVGResourceMarker* endMarker = getRenderSVGResourceById<RenderSVGResourceMarker>(document, svgStyle->markerEndResource()))
        endMarker->invalidateClient(object);

    // Gradients/Patterns
    if (svgStyle->hasFill())
        invalidatePaintingResource(svgStyle->fillPaint(), object);
    if (svgStyle->hasStroke())
        invalidatePaintingResource(svgStyle->strokePaint(), object);
}

void RenderSVGResource::markForLayoutAndParentResourceInvalidation(RenderObject* object, bool needsLayout)
{
    ASSERT(object);
    if (needsLayout)
        object->setNeedsLayout(true);

    // Invalidate resources in ancestor chain, if needed.
    RenderObject* current = object->parent();
    while (current) {
        if (current->isSVGResourceContainer()) 
            current->toRenderSVGResourceContainer()->invalidateClients();

        current = current->parent();
    }
}

}

#endif
