/*
 * Copyright (C) 2010 codeaurora.org All rights reserved.
 * Copyright (C) 2011 Collabora Ltd.
 * Copyright (C) 2011 Samsung Electronics
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "TileClutter.h"

#if USE(TILED_BACKING_STORE)
#include "CairoUtilities.h"
#include "GOwnPtr.h"
#include "GraphicsContext.h"
#include "PlatformContextCairo.h"
#include "TiledBackingStore.h"
#include "TiledBackingStoreClient.h"
#include <clutter/clutter.h>
#include <webkit/webkitiwebview.h>
#include <webkit/webkitwebpage.h>

namespace WebCore {

#define TILE_TYPE_ACTOR tile_actor_get_type()

typedef struct _TileActor {
    ClutterActor parent;
} TileActor;

typedef struct _GraphicsLayerActorClass {
    ClutterActorClass parent_class;
} TileActorClass;

G_DEFINE_TYPE(TileActor, tile_actor, CLUTTER_TYPE_ACTOR);

static void
tile_actor_class_init(TileActorClass *klass)
{
}

static void
tile_actor_init(TileActor* self)
{
}

// TODO: copied from TiledBackingStoreBackendCairo, could probably be shared
// somehow.
static const unsigned checkerSize = 16;
static const unsigned checkerColorDarkGrey = 0xff555555;
static const unsigned checkerColorLightGrey = 0xffaaaaaa;

static cairo_surface_t* checkeredSurface()
{
    static cairo_surface_t* surface = 0;
    if (surface)
        return surface;

    surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, checkerSize, checkerSize);
    RefPtr<cairo_t> cr = adoptRef(cairo_create(surface));

    unsigned halfCheckerSize = checkerSize / 2;
    setSourceRGBAFromColor(cr.get(), checkerColorLightGrey);
    cairo_rectangle(cr.get(), 0, 0, halfCheckerSize, halfCheckerSize);
    cairo_rectangle(cr.get(), halfCheckerSize, halfCheckerSize, halfCheckerSize, halfCheckerSize);
    cairo_fill(cr.get());

    setSourceRGBAFromColor(cr.get(), checkerColorDarkGrey);
    cairo_rectangle(cr.get(), halfCheckerSize, 0, halfCheckerSize, halfCheckerSize);
    cairo_rectangle(cr.get(), 0, halfCheckerSize, halfCheckerSize, halfCheckerSize);
    cairo_fill(cr.get());

    return surface;
}

static void paintCheckerPattern(GraphicsContext* context, const FloatRect& target)
{
    PlatformContextCairo* platformContext = context->platformContext();
    cairo_t* cr = platformContext->cr();
    cairo_set_source_surface(cr, checkeredSurface(), 0, 0);
    cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
    cairo_rectangle(cr, target.x(), target.y(), target.width(), target.height());
    cairo_fill(cr);
}

static gboolean drawCallback(ClutterCanvas* canvas, cairo_t* cr, int width, int height, TileClutter* tile)
{
    tile->draw(canvas, cr, width, height);
    return TRUE;
}

TileClutter::TileClutter(TiledBackingStore* backingStore, const Coordinate& tileCoordinate)
    : m_backingStore(backingStore)
    , m_coordinate(tileCoordinate)
    , m_rect(m_backingStore->tileRectForCoordinate(tileCoordinate))
    , m_dirty(true)
    , m_empty(true)
{
    m_actor = adoptGRef(CLUTTER_ACTOR(g_object_new(TILE_TYPE_ACTOR, NULL)));

    GOwnPtr<gchar> tileName(g_strdup_printf("Tile at %dx%d with size %dx%d", m_rect.x(), m_rect.y(), m_rect.width(), m_rect.height()));
    clutter_actor_set_name(m_actor.get(), tileName.get());

    ClutterContent* canvas = clutter_canvas_new();
    g_signal_connect(canvas, "draw",
                     G_CALLBACK(drawCallback), this);

    clutter_canvas_set_size(CLUTTER_CANVAS(canvas),
                            m_rect.width(),
                            m_rect.height());

    clutter_actor_set_size(m_actor.get(), m_rect.width(), m_rect.height());

    clutter_actor_set_content(m_actor.get(), canvas);
    g_object_unref (canvas);

    updateBackBuffer();
}

TileClutter::~TileClutter()
{
    ClutterActor* parent = clutter_actor_get_parent(m_actor.get());
    clutter_actor_remove_child(parent, m_actor.get());
}

void TileClutter::draw(ClutterCanvas* canvas, cairo_t* cr, int width, int height)
{
    if (!isDirty() && !isEmpty())
        return;

    GraphicsContext context(cr);
    context.translate(-m_rect.x(), -m_rect.y());

    IntRect rect(m_rect.location(), IntSize(width, height));
    context.clip(rect);
    context.scale(FloatSize(m_backingStore->contentsScale(), m_backingStore->contentsScale()));

    if (isEmpty()) {
        paintCheckerPattern(&context, m_backingStore->mapToContents(rect));
        m_empty = false;
        return;
    }

    m_backingStore->client()->tiledBackingStorePaint(&context, m_backingStore->mapToContents(rect));

    m_dirty = false;
}

Vector<IntRect> TileClutter::updateBackBuffer()
{
    if (!isDirty())
        return Vector<IntRect>();

    clutter_content_invalidate(clutter_actor_get_content(m_actor.get()));

    Vector<IntRect> updateRects;
    updateRects.append(m_rect);
    return updateRects;
}

void TileClutter::resize(const WebCore::IntSize& newSize)
{
    m_dirty = true;
    m_empty = true;

    m_rect = IntRect(m_rect.location(), newSize);

    ClutterContent* canvas = clutter_actor_get_content(m_actor.get());

    clutter_canvas_set_size(CLUTTER_CANVAS(canvas),
                            m_rect.width(),
                            m_rect.height());

    clutter_actor_set_size(m_actor.get(), m_rect.width(), m_rect.height());
}

}

#endif
