#define __SP_CANVAS_BPATH_C__

/*
 * Simple bezier bpath CanvasItem for inkscape
 *
 * Authors:
 *   Lauris Kaplinski <lauris@ximian.com>
 *
 * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc.
 *
 * Released under GNU GPL
 *
 */

#include <libart_lgpl/art_rect.h>
#include <libart_lgpl/art_vpath.h>
#include <libart_lgpl/art_bpath.h>
#include <libart_lgpl/art_svp.h>
#include <libart_lgpl/art_vpath_bpath.h>
#include <libart_lgpl/art_svp_vpath.h>
#include <libart_lgpl/art_svp_point.h>
#include <libart_lgpl/art_rect_svp.h>
#include <libart_lgpl/art_rgb_svp.h>
#include "sp-canvas.h"
#include "sp-canvas-util.h"
#include "canvas-bpath.h"

#ifdef bpath_liv
#include <config.h>
#include "../livarot/Shape.h"
#include "../livarot/Path.h"
#include "../livarot/AlphaLigne.h"
#include "../livarot/Ligne.h"
#include "../livarot/BitLigne.h"
#include "../libnr/nr-point.h"
#include "../libnr/nr-matrix.h"
#include "../libnr/nr-point-ops.h"
#include "../libnr/nr-matrix-ops.h"
#include "../libnr/nr-rect-l.h"
#include "../libnr/nr-pixops.h"
#include "../libnr/nr-compose.h"

void nr_pixblock_render_bpath_rgba (Shape* theS,uint32_t color,NRRectL &area,char* destBuf,int stride);
#endif

static void sp_canvas_bpath_class_init (SPCanvasBPathClass *klass);
static void sp_canvas_bpath_init (SPCanvasBPath *path);
static void sp_canvas_bpath_destroy (GtkObject *object);

static void sp_canvas_bpath_update (SPCanvasItem *item, double *affine, unsigned int flags);
static void sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf);
static double sp_canvas_bpath_point (SPCanvasItem *item, double x, double y, SPCanvasItem **actual_item);
 
static SPCanvasItemClass *parent_class;

GtkType
sp_canvas_bpath_get_type (void)
{
	static GtkType type = 0;
	if (!type) {
		GtkTypeInfo info = {
			"SPCanvasBPath",
			sizeof (SPCanvasBPath),
			sizeof (SPCanvasBPathClass),
			(GtkClassInitFunc) sp_canvas_bpath_class_init,
			(GtkObjectInitFunc) sp_canvas_bpath_init,
			NULL, NULL, NULL
		};
		type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info);
	}
	return type;
}

static void
sp_canvas_bpath_class_init (SPCanvasBPathClass *klass)
{
	GtkObjectClass *object_class;
	SPCanvasItemClass *item_class;

	object_class = GTK_OBJECT_CLASS (klass);
	item_class = (SPCanvasItemClass *) klass;

	parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM);

	object_class->destroy = sp_canvas_bpath_destroy;

	item_class->update = sp_canvas_bpath_update;
	item_class->render = sp_canvas_bpath_render;
	item_class->point = sp_canvas_bpath_point;
}

static void
sp_canvas_bpath_init (SPCanvasBPath * bpath)
{
	bpath->fill_rgba = 0x000000ff;
	bpath->fill_rule = SP_WIND_RULE_EVENODD;

	bpath->stroke_rgba = 0x00000000;
	bpath->stroke_width = 1.0;
	bpath->stroke_linejoin = SP_STROKE_LINEJOIN_MITER;
	bpath->stroke_linecap = SP_STROKE_LINECAP_BUTT;
	bpath->stroke_miterlimit = 11.0;

#ifdef bpath_liv
  bpath->fill_shp=NULL;
  bpath->stroke_shp=NULL;
#else
	bpath->fill_svp = NULL;
	bpath->stroke_svp = NULL;
#endif
}

static void
sp_canvas_bpath_destroy (GtkObject *object)
{
	SPCanvasBPath *cbp;

	cbp = SP_CANVAS_BPATH (object);
#ifdef bpath_liv
	if (cbp->fill_shp) {
		delete cbp->fill_shp;
		cbp->fill_shp = NULL;
	}
  
	if (cbp->stroke_shp) {
		delete cbp->stroke_shp;
		cbp->stroke_shp = NULL;
	}
#else
	if (cbp->fill_svp) {
		art_svp_free (cbp->fill_svp);
		cbp->fill_svp = NULL;
	}

	if (cbp->stroke_svp) {
		art_svp_free (cbp->stroke_svp);
		cbp->stroke_svp = NULL;
	}
#endif
	if (cbp->curve) {
		cbp->curve = sp_curve_unref (cbp->curve);
	}

	if (GTK_OBJECT_CLASS (parent_class)->destroy)
		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

static void
sp_canvas_bpath_update (SPCanvasItem *item, double *affine, unsigned int flags)
{
	SPCanvasBPath *cbp;
	ArtDRect dbox;
	ArtIRect ibox;

	cbp = SP_CANVAS_BPATH (item);

	sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2);

	if (((SPCanvasItemClass *) parent_class)->update)
		((SPCanvasItemClass *) parent_class)->update (item, affine, flags);

	sp_canvas_item_reset_bounds (item);

#ifdef bpath_liv
	if (cbp->fill_shp) {
		delete cbp->fill_shp;
		cbp->fill_shp = NULL;
	}
  
	if (cbp->stroke_shp) {
		delete cbp->stroke_shp;
		cbp->stroke_shp = NULL;
	}
#else
	if (cbp->fill_svp) {
		art_svp_free (cbp->fill_svp);
		cbp->fill_svp = NULL;
	}

	if (cbp->stroke_svp) {
		art_svp_free (cbp->stroke_svp);
		cbp->stroke_svp = NULL;
	}
#endif
	if (!cbp->curve) return;

	dbox.x0 = dbox.y0 = 0.0;
	dbox.x1 = dbox.y1 = -1.0;

	if ((cbp->fill_rgba & 0xff) || (cbp->stroke_rgba & 0xff)) {
#ifdef bpath_liv
    Path*  thePath=new Path;
    {
      NR::Matrix   tempMat(affine[0],affine[1],affine[2],affine[3],affine[4],affine[5]);
      thePath->LoadArtBPath(cbp->curve->bpath,tempMat,true);
    }
    thePath->Convert(0.25);
		if ((cbp->fill_rgba & 0xff) && (cbp->curve->end > 2)) {
      Shape* theShape=new Shape;
      thePath->Fill(theShape,0);
      if ( cbp->fill_shp == NULL ) cbp->fill_shp=new Shape;
      if ( cbp->fill_rule == SP_WIND_RULE_EVENODD ) {
        cbp->fill_shp->ConvertToShape(theShape,fill_oddEven);
      } else {
        cbp->fill_shp->ConvertToShape(theShape,fill_nonZero);
      }
      delete theShape;
      cbp->fill_shp->CalcBBox();
      if ( cbp->fill_shp->leftX < cbp->fill_shp->rightX ) {
        if ( dbox.x0 >= dbox.x1 ) {
          dbox.x0=cbp->fill_shp->leftX;dbox.x1=cbp->fill_shp->rightX;
          dbox.y0=cbp->fill_shp->topY;dbox.y1=cbp->fill_shp->bottomY;
        } else {
          if ( cbp->fill_shp->leftX < dbox.x0 ) dbox.x0=cbp->fill_shp->leftX;
          if ( cbp->fill_shp->rightX > dbox.x1 ) dbox.x1=cbp->fill_shp->rightX;
          if ( cbp->fill_shp->topY < dbox.y0 ) dbox.y0=cbp->fill_shp->topY;
          if ( cbp->fill_shp->bottomY > dbox.y1 ) dbox.y1=cbp->fill_shp->bottomY;
        }
      }
    }
		if ((cbp->stroke_rgba & 0xff) && (cbp->curve->end > 1)) {
      JoinType join=join_straight;
//      Shape* theShape=new Shape;
      ButtType butt=butt_straight;
      if ( cbp->stroke_shp == NULL ) cbp->stroke_shp=new Shape;
      if ( cbp->stroke_linecap == SP_STROKE_LINECAP_BUTT ) butt=butt_straight;
      if ( cbp->stroke_linecap == SP_STROKE_LINECAP_ROUND ) butt=butt_round;
      if ( cbp->stroke_linecap == SP_STROKE_LINECAP_SQUARE ) butt=butt_square;
      if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_MITER ) join=join_pointy;
      if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_ROUND ) join=join_round;
      if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_BEVEL ) join=join_straight;
      thePath->Stroke(cbp->stroke_shp,false,0.5*cbp->stroke_width, join,butt,cbp->stroke_width*cbp->stroke_miterlimit );
 //     thePath->Stroke(theShape,false,0.5*cbp->stroke_width, join,butt,cbp->stroke_width*cbp->stroke_miterlimit );
 //     cbp->stroke_shp->ConvertToShape(theShape,fill_nonZero);
      
      cbp->stroke_shp->CalcBBox();
      if ( cbp->stroke_shp->leftX < cbp->stroke_shp->rightX ) {
        if ( dbox.x0 >= dbox.x1 ) {
          dbox.x0=cbp->stroke_shp->leftX;dbox.x1=cbp->stroke_shp->rightX;
          dbox.y0=cbp->stroke_shp->topY;dbox.y1=cbp->stroke_shp->bottomY;
        } else {
          if ( cbp->stroke_shp->leftX < dbox.x0 ) dbox.x0=cbp->stroke_shp->leftX;
          if ( cbp->stroke_shp->rightX > dbox.x1 ) dbox.x1=cbp->stroke_shp->rightX;
          if ( cbp->stroke_shp->topY < dbox.y0 ) dbox.y0=cbp->stroke_shp->topY;
          if ( cbp->stroke_shp->bottomY > dbox.y1 ) dbox.y1=cbp->stroke_shp->bottomY;
        }
      }
//      delete theShape;
    }
    delete thePath;
#else
    ArtDRect pbox;
		ArtBpath *bp;
		ArtVpath *vp, *pp;
		bp = art_bpath_affine_transform (cbp->curve->bpath, affine);
		vp = art_bez_path_to_vec (bp, 0.25);
		art_free (bp);
		pp = art_vpath_perturb (vp);
		art_free (vp);

		if ((cbp->fill_rgba & 0xff) && (cbp->curve->end > 2)) {
			ArtSVP *svpa, *svpb;
			svpa = art_svp_from_vpath (pp);
			svpb = art_svp_uncross (svpa);
			art_svp_free (svpa);
			cbp->fill_svp = art_svp_rewind_uncrossed (svpb, (ArtWindRule)cbp->fill_rule);
			art_svp_free (svpb);
			art_drect_svp (&pbox, cbp->fill_svp);
			art_drect_union (&dbox, &dbox, &pbox);
		}

		if ((cbp->stroke_rgba & 0xff) && (cbp->curve->end > 1)) {
			cbp->stroke_svp = art_svp_vpath_stroke (pp, (ArtPathStrokeJoinType)cbp->stroke_linejoin, (ArtPathStrokeCapType)cbp->stroke_linecap,
								cbp->stroke_width, cbp->stroke_miterlimit, 0.25);
			art_drect_svp (&pbox, cbp->stroke_svp);
			art_drect_union (&dbox, &dbox, &pbox);
		}

		art_free (pp);
#endif
	}


	art_drect_to_irect (&ibox, &dbox);

	item->x1 = ibox.x0;
	item->y1 = ibox.y0;
	item->x2 = ibox.x1;
	item->y2 = ibox.y1;

	sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2);
}

static void
sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf)
{
	SPCanvasBPath *cbp;

	cbp = SP_CANVAS_BPATH (item);

	if (buf->is_bg) {
		sp_canvas_clear_buffer (buf);
		buf->is_bg = FALSE;
		buf->is_buf = TRUE;
	}
#ifdef bpath_liv
  NRRectL  area;
  area.x0=buf->rect.x0;
  area.x1=buf->rect.x1;
  area.y0=buf->rect.y0;
  area.y1=buf->rect.y1;
  if ( cbp->fill_shp ) {
    nr_pixblock_render_bpath_rgba (cbp->fill_shp,cbp->fill_rgba,area,(char*)buf->buf, buf->buf_rowstride);
  }
  if ( cbp->stroke_shp ) {
    nr_pixblock_render_bpath_rgba (cbp->stroke_shp,cbp->stroke_rgba,area,(char*)buf->buf, buf->buf_rowstride);
  }
#else
	if (cbp->fill_svp) {
		art_rgb_svp_alpha (cbp->fill_svp, buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, cbp->fill_rgba,
				   buf->buf, buf->buf_rowstride,
				   NULL);
	}

	if (cbp->stroke_svp) {
		art_rgb_svp_alpha (cbp->stroke_svp, buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, cbp->stroke_rgba,
				   buf->buf, buf->buf_rowstride,
				   NULL);
	}
#endif
}

#define BIGVAL 1e18

static double
sp_canvas_bpath_point (SPCanvasItem *item, double x, double y, SPCanvasItem **actual_item)
{
	SPCanvasBPath *cbp;
	gdouble dist;

	cbp = SP_CANVAS_BPATH (item);

#ifdef bpath_liv
  {
    NR::Point const thePt(x, y);
		if (cbp->fill_shp ) {
			if (cbp->fill_shp->PtWinding(thePt) > 0 ) {
        *actual_item = item;
        return 0.0;
      }
		}
		if (cbp->stroke_shp ) {
			if (cbp->stroke_shp->PtWinding(thePt) > 0 ) {
        *actual_item = item;
        return 0.0;
      }
      dist=cbp->stroke_shp->Distance(thePt);
      return dist;
		}
    if (cbp->fill_shp) {
      dist=cbp->fill_shp->Distance(thePt);
      return dist;
    }
  }
#else
	gint wind;
	if (cbp->fill_svp) {
		wind = art_svp_point_wind (cbp->fill_svp, x, y);
		if (wind) {
			*actual_item = item;
			return 0.0;
		}
	}

	if (cbp->stroke_svp) {
		wind = art_svp_point_wind (cbp->stroke_svp, x, y);
		if (wind) {
			*actual_item = item;
			return 0.0;
		}
		dist = art_svp_point_dist (cbp->stroke_svp, x, y);
		return dist;
	}

	if (cbp->fill_svp) {
		dist = art_svp_point_dist (cbp->fill_svp, x, y);
		return dist;
	}
#endif
  
	return BIGVAL;
}

SPCanvasItem *
sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve)
{
	SPCanvasItem *item;

	g_return_val_if_fail (parent != NULL, NULL);
	g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);

	item = sp_canvas_item_new (parent, SP_TYPE_CANVAS_BPATH, NULL);

	sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (item), curve);

	return item;
}

void
sp_canvas_bpath_set_bpath (SPCanvasBPath *cbp, SPCurve *curve)
{
	g_return_if_fail (cbp != NULL);
	g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));

	if (cbp->curve) {
		cbp->curve = sp_curve_unref (cbp->curve);
	}

	if (curve) {
		cbp->curve = sp_curve_ref (curve);
	}

	sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
}

void
sp_canvas_bpath_set_fill (SPCanvasBPath *cbp, guint32 rgba, SPWindRule rule)
{
	g_return_if_fail (cbp != NULL);
	g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));

	cbp->fill_rgba = rgba;
	cbp->fill_rule = rule;

	sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
}

void
sp_canvas_bpath_set_stroke (SPCanvasBPath *cbp, guint32 rgba, gdouble width, SPStrokeJoinType join, SPStrokeCapType cap)
{
	g_return_if_fail (cbp != NULL);
	g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));

	cbp->stroke_rgba = rgba;
	cbp->stroke_width = MAX (width, 0.1);
	cbp->stroke_linejoin = join;
	cbp->stroke_linecap = cap;

	sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
}

#ifdef bpath_liv

static void
bpath_run_A8_OR (raster_info &dest,void *data,int st,float vst,int en,float ven)
{
  union {
    uint8_t  comp[4];
    uint32_t col;
  } tempCol;
  if ( st >= en ) return;
  tempCol.col=*(uint32_t*)data;

	unsigned int r, g, b, a;
	r = NR_RGBA32_R (tempCol.col);
	g = NR_RGBA32_G (tempCol.col);
	b = NR_RGBA32_B (tempCol.col);
	a = NR_RGBA32_A (tempCol.col);
	if (a == 0) return;
  
  vst*=a;
  ven*=a;
  
  if ( vst < 0 ) vst=0;
  if ( vst > 255 ) vst=255;
  if ( ven < 0 ) ven=0;
  if ( ven > 255 ) ven=255;
  float      sv=vst;
  float      dv=ven-vst;
  int        len=en-st;
  uint8_t*   d=(uint8_t*)dest.buffer;

  d+=3*(st-dest.startPix);
  if ( fabsf(dv) < 0.001 ) {
    if ( sv > 249.999 ) {
      /* Simple copy */
      while (len > 0) {
        d[0] = NR_COMPOSEN11 (r, 255, d[0]);
        d[1] = NR_COMPOSEN11 (g, 255, d[1]);
        d[2] = NR_COMPOSEN11 (b, 255, d[2]);
        d += 3;
        len -= 1;
      }
    } else {
      unsigned int c0_24=(int)sv;
      c0_24&=0xFF;
      while (len > 0) {
        d[0] = NR_COMPOSEN11 (r, c0_24, d[0]);
        d[1] = NR_COMPOSEN11 (g, c0_24, d[1]);
        d[2] = NR_COMPOSEN11 (b, c0_24, d[2]);
        d += 3;
        len -= 1;
      }
    }
  } else {
    if ( en <= st+1 ) {
      sv=0.5*(vst+ven);
      unsigned int c0_24=(int)sv;
      c0_24&=0xFF;
      d[0] = NR_COMPOSEN11 (r, c0_24, d[0]);
      d[1] = NR_COMPOSEN11 (g, c0_24, d[1]);
      d[2] = NR_COMPOSEN11 (b, c0_24, d[2]);
    } else {
      dv/=len;
      sv+=0.5*dv; // correction trapezoidale
      sv*=65536;
      dv*=65536;
      int c0_24 = static_cast<int>(CLAMP(sv, 0, 16777216));
      int s0_24 = static_cast<int>(dv);
      while (len > 0) {
        unsigned int ca;
        /* Draw */
        ca = c0_24 >> 16;
        if ( ca > 255 ) ca=255;
        d[0] = NR_COMPOSEN11 (r, ca, d[0]);
        d[1] = NR_COMPOSEN11 (g, ca, d[1]);
        d[2] = NR_COMPOSEN11 (b, ca, d[2]);
        d += 3;
        c0_24 += s0_24;
        c0_24 = CLAMP (c0_24, 0, 16777216);
        len -= 1;
      }
    }
  }
}

void nr_pixblock_render_bpath_rgba (Shape* theS,uint32_t color,NRRectL &area,char* destBuf,int stride)
{

  theS->CalcBBox();
  float  l=theS->leftX,r=theS->rightX,t=theS->topY,b=theS->bottomY;
  int    il,ir,it,ib;
  il=(int)floorf(l);
  ir=(int)ceilf(r);
  it=(int)floorf(t);
  ib=(int)ceilf(b);

  if ( il >= area.x1 || ir <= area.x0 || it >= area.y1 || ib <= area.y0 ) return;
  if ( il < area.x0 ) il=area.x0;
  if ( it < area.y0 ) it=area.y0;
  if ( ir > area.x1 ) ir=area.x1;
  if ( ib > area.y1 ) ib=area.y1;
  
/*  // version par FloatLigne
  int    curPt;
  float  curY;
  theS->BeginRaster(curY,curPt,1.0);

  FloatLigne* theI=new FloatLigne();
  IntLigne*   theIL=new IntLigne();

  theS->Scan(curY,curPt,(float)(it),1.0);

  char* mdata=(char*)destBuf;
  uint32_t* ligStart=((uint32_t*)(mdata+(3*(il-area.x0)+stride*(it-area.y0))));
  for (int y=it;y<ib;y++) {
    theI->Reset();
    if ( y&0x00000003 ) {
      theS->Scan(curY,curPt,((float)(y+1)),theI,false,1.0);
    } else {
      theS->Scan(curY,curPt,((float)(y+1)),theI,true,1.0);
    }
    theI->Flatten();
    theIL->Copy(theI);

    raster_info  dest;
    dest.startPix=il;
    dest.endPix=ir;
    dest.sth=il;
    dest.stv=y;
    dest.buffer=ligStart;
    theIL->Raster(dest,&color,bpath_run_A8_OR);
    ligStart=((uint32_t*)(((char*)ligStart)+stride));
  }
  theS->EndRaster();
  delete theI;
  delete theIL;  */

  // version par BitLigne directe
  int    curPt;
  float  curY;
  theS->BeginQuickRaster(curY,curPt,1.0);
  
  BitLigne*   theI[4];
  for (int i=0;i<4;i++) theI[i]=new BitLigne(il,ir);
  IntLigne*   theIL=new IntLigne();
  
  theS->QuickScan(curY,curPt,(float)(it),true,0.25);
  
  char* mdata=(char*)destBuf;
  uint32_t* ligStart=((uint32_t*)(mdata+(3*(il-area.x0)+stride*(it-area.y0))));
  for (int y=it;y<ib;y++) {
    for (int i=0;i<4;i++) theI[i]->Reset();
    if ( y&3 ) {
      theS->QuickScan(curY,curPt,((float)(y+0.25)),fill_oddEven,theI[0],false,0.25);
      theS->QuickScan(curY,curPt,((float)(y+0.5)),fill_oddEven,theI[1],false,0.25);
      theS->QuickScan(curY,curPt,((float)(y+0.75)),fill_oddEven,theI[2],false,0.25);
      theS->QuickScan(curY,curPt,((float)(y+1.0)),fill_oddEven,theI[3],false,0.25);
    } else {
      theS->QuickScan(curY,curPt,((float)(y+0.25)),fill_oddEven,theI[0],true,0.25);
      theS->QuickScan(curY,curPt,((float)(y+0.5)),fill_oddEven,theI[1],true,0.25);
      theS->QuickScan(curY,curPt,((float)(y+0.75)),fill_oddEven,theI[2],true,0.25);
      theS->QuickScan(curY,curPt,((float)(y+1.0)),fill_oddEven,theI[3],true,0.25);
    }
    theIL->Copy(4,theI);
 //   theI[0]->Affiche();
 //   theIL->Affiche();
     
    raster_info  dest;
    dest.startPix=il;
    dest.endPix=ir;
    dest.sth=il;
    dest.stv=y;
    dest.buffer=ligStart;
    theIL->Raster(dest,&color,bpath_run_A8_OR);
    ligStart=((uint32_t*)(((char*)ligStart)+stride));
  }
  theS->EndQuickRaster();
  for (int i=0;i<4;i++) delete theI[i];
  delete theIL;    
}

#endif

