/* Based on GTK code by the Chromium Authors. The original header for that code
 * continues below */

/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * 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; 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
 */

#include <stdio.h>

#include <gdk/gdk.h>
#include <SkCanvas.h>
#include <SkBitmap.h>
#include <SkDevice.h>

#include "GdkSkia.h"

static GdkGC *gdk_skia_create_gc      (GdkDrawable     *drawable,
                                       GdkGCValues     *values,
                                       GdkGCValuesMask  mask);
static void   gdk_skia_draw_rectangle (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       gboolean         filled,
                                       gint             x,
                                       gint             y,
                                       gint             width,
                                       gint             height);
static void   gdk_skia_draw_arc       (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       gboolean         filled,
                                       gint             x,
                                       gint             y,
                                       gint             width,
                                       gint             height,
                                       gint             angle1,
                                       gint             angle2);
static void   gdk_skia_draw_polygon   (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       gboolean         filled,
                                       GdkPoint        *points,
                                       gint             npoints);
static void   gdk_skia_draw_text      (GdkDrawable     *drawable,
                                       GdkFont         *font,
                                       GdkGC           *gc,
                                       gint             x,
                                       gint             y,
                                       const gchar     *text,
                                       gint             text_length);
static void   gdk_skia_draw_text_wc   (GdkDrawable     *drawable,
                                       GdkFont         *font,
                                       GdkGC           *gc,
                                       gint             x,
                                       gint             y,
                                       const GdkWChar  *text,
                                       gint             text_length);
static void   gdk_skia_draw_drawable  (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       GdkPixmap       *src,
                                       gint             xsrc,
                                       gint             ysrc,
                                       gint             xdest,
                                       gint             ydest,
                                       gint             width,
                                       gint             height);
static void   gdk_skia_draw_points    (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       GdkPoint        *points,
                                       gint             npoints);
static void   gdk_skia_draw_segments  (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       GdkSegment      *segs,
                                       gint             nsegs);
static void   gdk_skia_draw_lines     (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       GdkPoint        *points,
                                       gint             npoints);

static void gdk_skia_draw_glyphs             (GdkDrawable      *drawable,
                                              GdkGC            *gc,
                                              PangoFont        *font,
                                              gint              x,
                                              gint              y,
                                              PangoGlyphString *glyphs);
static void gdk_skia_draw_glyphs_transformed (GdkDrawable      *drawable,
                                              GdkGC            *gc,
                                              PangoMatrix      *matrix,
                                              PangoFont        *font,
                                              gint              x,
                                              gint              y,
                                              PangoGlyphString *glyphs);

static void   gdk_skia_draw_image     (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       GdkImage        *image,
                                       gint             xsrc,
                                       gint             ysrc,
                                       gint             xdest,
                                       gint             ydest,
                                       gint             width,
                                       gint             height);
static void   gdk_skia_draw_pixbuf    (GdkDrawable     *drawable,
                                       GdkGC           *gc,
                                       GdkPixbuf       *pixbuf,
                                       gint             src_x,
                                       gint             src_y,
                                       gint             dest_x,
                                       gint             dest_y,
                                       gint             width,
                                       gint             height,
                                       GdkRgbDither     dither,
                                       gint             x_dither,
                                       gint             y_dither);
static void  gdk_skia_draw_trapezoids (GdkDrawable     *drawable,
                                       GdkGC	         *gc,
                                       GdkTrapezoid    *trapezoids,
                                       gint             n_trapezoids);

static void   gdk_skia_real_get_size  (GdkDrawable     *drawable,
                                       gint            *width,
                                       gint            *height);

static GdkImage* gdk_skia_copy_to_image (GdkDrawable *drawable,
                                         GdkImage    *image,
                                         gint         src_x,
                                         gint         src_y,
                                         gint         dest_x,
                                         gint         dest_y,
                                         gint         width,
                                         gint         height);

static cairo_surface_t *gdk_skia_ref_cairo_surface (GdkDrawable *drawable);

static GdkVisual*   gdk_skia_real_get_visual   (GdkDrawable *drawable);
static gint         gdk_skia_real_get_depth    (GdkDrawable *drawable);
static void         gdk_skia_real_set_colormap (GdkDrawable *drawable,
                                                  GdkColormap *cmap);
static GdkColormap* gdk_skia_real_get_colormap (GdkDrawable *drawable);
static GdkScreen*   gdk_skia_real_get_screen   (GdkDrawable *drawable);
static void gdk_skia_init       (GdkSkiaObject      *skia);
static void gdk_skia_class_init (GdkSkiaObjectClass *klass);
static void gdk_skia_finalize   (GObject              *object);

static gpointer parent_class = NULL;

// -----------------------------------------------------------------------------
// Usually GDK code is C code. However, since we are interfacing to a C++
// library, we must compile in C++ mode to parse its headers etc. Thankfully,
// these are the only non-static symbol in the file so we can just wrap them in
// an extern decl to disable name mangling and everything should be happy.
// -----------------------------------------------------------------------------
extern "C" {
GType
gdk_skia_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    object_type = g_type_register_static_simple (GDK_TYPE_DRAWABLE,
						 "GdkSkia",
						 sizeof (GdkSkiaObjectClass),
						 (GClassInitFunc) gdk_skia_class_init,
						 sizeof (GdkSkiaObject),
						 (GInstanceInitFunc) gdk_skia_init,
						 (GTypeFlags) 0);

  return object_type;
}

GdkSkia*
gdk_skia_new(SkCanvas *canvas)
{
  GdkSkia *skia = GDK_SKIA(g_object_new (GDK_TYPE_SKIA, NULL));
  reinterpret_cast<GdkSkiaObject*>(skia)->canvas = canvas;
  return skia;
}

}  // extern "C"

static void
gdk_skia_init (GdkSkiaObject *skia)
{
  /* 0 initialization is fine for us */
}

static void
gdk_skia_class_init (GdkSkiaObjectClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = gdk_skia_finalize;

  drawable_class->create_gc = gdk_skia_create_gc;
  drawable_class->draw_rectangle = gdk_skia_draw_rectangle;
  drawable_class->draw_arc = gdk_skia_draw_arc;
  drawable_class->draw_polygon = gdk_skia_draw_polygon;
  drawable_class->draw_text = gdk_skia_draw_text;
  drawable_class->draw_text_wc = gdk_skia_draw_text_wc;
  drawable_class->draw_drawable = gdk_skia_draw_drawable;
  drawable_class->draw_points = gdk_skia_draw_points;
  drawable_class->draw_segments = gdk_skia_draw_segments;
  drawable_class->draw_lines = gdk_skia_draw_lines;
  drawable_class->draw_glyphs = gdk_skia_draw_glyphs;
  drawable_class->draw_glyphs_transformed = gdk_skia_draw_glyphs_transformed;
  drawable_class->draw_image = gdk_skia_draw_image;
  drawable_class->draw_pixbuf = gdk_skia_draw_pixbuf;
  drawable_class->draw_trapezoids = gdk_skia_draw_trapezoids;
  drawable_class->get_depth = gdk_skia_real_get_depth;
  drawable_class->get_screen = gdk_skia_real_get_screen;
  drawable_class->get_size = gdk_skia_real_get_size;
  drawable_class->set_colormap = gdk_skia_real_set_colormap;
  drawable_class->get_colormap = gdk_skia_real_get_colormap;
  drawable_class->get_visual = gdk_skia_real_get_visual;
  drawable_class->_copy_to_image = gdk_skia_copy_to_image;
  drawable_class->ref_cairo_surface = gdk_skia_ref_cairo_surface;
}

static void
gdk_skia_finalize (GObject *object)
{
  GdkSkiaObject *const skia = (GdkSkiaObject *) object;
  if (skia->surface)
    cairo_surface_destroy(skia->surface);
  G_OBJECT_CLASS (parent_class)->finalize(object);
}

#define NOTIMPLEMENTED fprintf(stderr, "GDK Skia not implemented: %s\n", __PRETTY_FUNCTION__)

static GdkGC *
gdk_skia_create_gc(GdkDrawable     *drawable,
                   GdkGCValues     *values,
                   GdkGCValuesMask  mask) {
  NOTIMPLEMENTED;
  return NULL;
}

static void
gc_set_paint(GdkGC *gc, SkPaint *paint) {
  GdkGCValues values;
  gdk_gc_get_values(gc, &values);

  paint->setARGB(255,
                 values.foreground.pixel >> 16,
                 values.foreground.pixel >> 8,
                 values.foreground.pixel);
  paint->setStrokeWidth(values.line_width);
}

static void
gdk_skia_draw_rectangle(GdkDrawable     *drawable,
                        GdkGC           *gc,
                        gboolean         filled,
                        gint             x,
                        gint             y,
                        gint             width,
                        gint             height) {
  GdkSkiaObject *skia = (GdkSkiaObject *) drawable;
  SkPaint paint;
  gc_set_paint(gc, &paint);

  if (filled) {
    paint.setStyle(SkPaint::kFill_Style);
  } else {
    paint.setStyle(SkPaint::kStroke_Style);
  }

  SkRect rect;
  rect.set(x, y, x + width, y + height);

  skia->canvas->drawRect(rect, paint);
}

static void
gdk_skia_draw_arc(GdkDrawable     *drawable,
                  GdkGC           *gc,
                  gboolean         filled,
                  gint             x,
                  gint             y,
                  gint             width,
                  gint             height,
                  gint             angle1,
                  gint             angle2) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_polygon(GdkDrawable     *drawable,
                      GdkGC           *gc,
                      gboolean         filled,
                      GdkPoint        *points,
                      gint             npoints) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_text(GdkDrawable     *drawable,
                   GdkFont         *font,
                   GdkGC           *gc,
                   gint             x,
                   gint             y,
                   const gchar     *text,
                   gint             text_length) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_text_wc(GdkDrawable     *drawable,
                      GdkFont         *font,
                      GdkGC           *gc,
                      gint             x,
                      gint             y,
                      const GdkWChar  *text,
                      gint             text_length) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_drawable(GdkDrawable     *drawable,
                       GdkGC           *gc,
                       GdkPixmap       *src,
                       gint             xsrc,
                       gint             ysrc,
                       gint             xdest,
                       gint             ydest,
                       gint             width,
                       gint             height) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_points(GdkDrawable     *drawable,
                     GdkGC           *gc,
                     GdkPoint        *points,
                     gint             npoints) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_segments(GdkDrawable     *drawable,
                       GdkGC           *gc,
                       GdkSegment      *segs,
                       gint             nsegs) {
  GdkSkiaObject *skia = (GdkSkiaObject *) drawable;
  GdkGCValues values;
  SkPaint paint;
  int nskip = 1;

  gc_set_paint(gc, &paint);
  gdk_gc_get_values(gc, &values);

  // TODO(jhawkins): how to set line width in skia?

  // GDK_LINE_ON_OFF_DASH only renders the even segments
  if (values.line_style == GDK_LINE_ON_OFF_DASH)
    nskip++;

  for (int i = 0; i < nsegs; i += nskip) {
    // TODO(jhawkins): GDK_LINE_DOUBLE_DASH
    skia->canvas->drawLine(segs[i].x1, segs[i].y1,
                           segs[i].x2, segs[i].y2, paint);
  }
}

static void
gdk_skia_draw_lines(GdkDrawable     *drawable,
                    GdkGC           *gc,
                    GdkPoint        *points,
                    gint             npoints) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_glyphs(GdkDrawable      *drawable,
                     GdkGC            *gc,
                     PangoFont        *font,
                     gint              x,
                     gint              y,
                     PangoGlyphString *glyphs) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_glyphs_transformed(GdkDrawable      *drawable,
                                 GdkGC            *gc,
                                 PangoMatrix      *matrix,
                                 PangoFont        *font,
                                 gint              x,
                                 gint              y,
                                 PangoGlyphString *glyphs) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_image(GdkDrawable     *drawable,
                    GdkGC           *gc,
                    GdkImage        *image,
                    gint             xsrc,
                    gint             ysrc,
                    gint             xdest,
                    gint             ydest,
                    gint             width,
                    gint             height) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_pixbuf(GdkDrawable     *drawable,
                     GdkGC           *gc,
                     GdkPixbuf       *pixbuf,
                     gint             src_x,
                     gint             src_y,
                     gint             dest_x,
                     gint             dest_y,
                     gint             width,
                     gint             height,
                     GdkRgbDither     dither,
                     gint             x_dither,
                     gint             y_dither) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_draw_trapezoids(GdkDrawable     *drawable,
                         GdkGC	         *gc,
                         GdkTrapezoid    *trapezoids,
                         gint             n_trapezoids) {
  NOTIMPLEMENTED;
}

static void
gdk_skia_real_get_size(GdkDrawable     *drawable,
                       gint            *width,
                       gint            *height) {
  GdkSkiaObject *const skia = (GdkSkiaObject *) drawable;
  SkDevice *const dev = skia->canvas->getDevice();
  *width = dev->width();
  *height = dev->height();
}

static GdkImage*
gdk_skia_copy_to_image(GdkDrawable *drawable,
                       GdkImage    *image,
                       gint         src_x,
                       gint         src_y,
                       gint         dest_x,
                       gint         dest_y,
                       gint         width,
                       gint         height) {
  NOTIMPLEMENTED;
  return NULL;
}

static cairo_surface_t *
gdk_skia_ref_cairo_surface(GdkDrawable *drawable) {
  GdkSkiaObject *const skia = (GdkSkiaObject *) drawable;

  if (!skia->surface) {
    SkDevice *const dev = skia->canvas->getDevice();
    const SkBitmap *const bm = &dev->accessBitmap(true);

    skia->surface = cairo_image_surface_create_for_data
      ((unsigned char *) bm->getPixels(),
       CAIRO_FORMAT_ARGB32, dev->width(), dev->height(), bm->rowBytes());
  }

  SkMatrix matrix = skia->canvas->getTotalMatrix();
  int x_shift = SkScalarRound(matrix.getTranslateX());
  int y_shift = SkScalarRound(matrix.getTranslateY());
  cairo_surface_set_device_offset(skia->surface, x_shift, y_shift);

  return cairo_surface_reference(skia->surface);
}

static GdkVisual*
gdk_skia_real_get_visual(GdkDrawable *drawable) {
  NOTIMPLEMENTED;
  return NULL;
}

static gint
gdk_skia_real_get_depth(GdkDrawable *drawable) {
  GdkSkiaObject *skia = (GdkSkiaObject *) drawable;
  const SkBitmap::Config config = skia->canvas->getDevice()->config();

  switch (config) {
    case SkBitmap::kARGB_8888_Config:
      return 24;
    default:
      // NOTREACHED
      *reinterpret_cast<char*>(NULL) = 0;
      return 0;
  }
}

static void
gdk_skia_real_set_colormap(GdkDrawable *drawable,
                           GdkColormap *cmap) {
  NOTIMPLEMENTED;
}

static GdkColormap*
gdk_skia_real_get_colormap(GdkDrawable *drawable) {
  NOTIMPLEMENTED;
  return NULL;
}

static GdkScreen*
gdk_skia_real_get_screen(GdkDrawable *drawable) {
  NOTIMPLEMENTED;
  return NULL;
}