/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  (this file is based on PCB, interactive printed circuit board design)
 *  Copyright (C) 1994,1995,1996, 2004 Thomas Nau
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 *
 */

#ifndef PCB_DRAW_H
#define PCB_DRAW_H

#include "config.h"
#include <librnd/hid/hid.h>
#include "layer.h"
#include "global_typedefs.h"

/* holds information about output window */
typedef struct {
	rnd_hid_t *hid;
	rnd_hid_gc_t drillGC, fgGC, pmGC;                      /* changed from some routines */
	rnd_hid_gc_t active_padGC, backpadGC, padGC, padselGC; /* pads are drawn with this gc */
	unsigned direct:1;                                     /* starts as 1 and becomes 0 before the first compositing layer group is reset */
} pcb_output_t;

extern pcb_output_t pcb_draw_out;

typedef enum {
	PCB_PHOLE_PLATED = 1,
	PCB_PHOLE_UNPLATED = 2,
	PCB_PHOLE_BB = 4
} pcb_pstk_draw_hole_t;

/* Some low level draw callback depend on this in their void *cl */
struct pcb_draw_info_s {
	pcb_board_t *pcb;
	int exporting;                         /* 1 if doing an export, 0 if working to screen */
	const char *export_name;               /* name of the export plugin */
	char noexport_name[64];                /* "noexport:" attribute name rendered for the current exporter */
	const rnd_box_t *drawn_area;
	rnd_xform_t *xform_caller;             /* the extra transformation the caller requested (the one who has initiated the rendering, e.g. throuh pcb_draw_everything()) */
	rnd_xform_t *xform_exporter;           /* the extra transformation the exporter requested (e.g. because of cam) */
	rnd_xform_t *xform;                    /* the final transformation applied on objects */

	unsigned disable_plugin_draw:1;        /* temporarily disable calling plugin_draw instead of drawing the layer normally; useful for calling ack the layer draw routines from a plugin_draw to draw normal content of the layer */

	const pcb_layer_t *layer;

	int draw_stamp;

	union { /* fields used for specific object callbacks */
		struct {
			rnd_layergrp_id_t gid;
			int is_current;
			pcb_pstk_draw_hole_t holetype;
			pcb_layer_combining_t comb;
			const pcb_layer_t *layer1; /* first (real) layer in the target group */

			pcb_layer_type_t shape_mask; /* when gid is invalid, use this for the shapes */
		} pstk;
		struct {
			int per_side;
		} subc;
	} objcb;
};

/* Temporarily inhibid drawing if this is non-zero. A function that calls a
   lot of other functions that would call pcb_draw() a lot in turn may increase
   this value before the calls, then decrease it at the end and call pcb_draw().
   This makes sure the whole block is redrawn only once at the end. */
extern rnd_cardinal_t pcb_draw_inhibit;

#define pcb_draw_inhibit_inc() pcb_draw_inhibit++
#define pcb_draw_inhibit_dec() \
do { \
	if (pcb_draw_inhibit > 0) \
		pcb_draw_inhibit--; \
} while(0) \

/* When true, do not draw terminals but put them on the obj delay list
   using pcb_draw_delay_obj_add() - used to delay heavy terminal drawing
   to get proper overlaps */
extern rnd_bool delayed_terms_enabled;
void pcb_draw_delay_obj_add(pcb_any_obj_t *obj);

/* Append an object to the annotatation list; annotations are drawn at the
   end, after labels */
void pcb_draw_annotation_add(pcb_any_obj_t *obj);

/* the minimum box that needs to be redrawn */
extern rnd_box_t pcb_draw_invalidated;

/* Adds the update rect to the invalidated region. This schedules the object
   for redraw (by pcb_draw()). obj is anything that can be casted to rnd_box_t */
#define pcb_draw_invalidate(obj) \
do { \
	rnd_box_t *box = (rnd_box_t *)obj; \
	pcb_draw_invalidated.X1 = MIN(pcb_draw_invalidated.X1, box->X1); \
	pcb_draw_invalidated.X2 = MAX(pcb_draw_invalidated.X2, box->X2); \
	pcb_draw_invalidated.Y1 = MIN(pcb_draw_invalidated.Y1, box->Y1); \
	pcb_draw_invalidated.Y2 = MAX(pcb_draw_invalidated.Y2, box->Y2); \
} while(0)

extern int pcb_draw_force_termlab; /* force drawing terminal lables - useful for pinout previews */
extern rnd_bool pcb_draw_doing_assy;

void pcb_lighten_color(const rnd_color_t *orig, rnd_color_t *dst, double factor);

/* Draw a series of short line segments emulating a dashed line. segs is
   the number of on/off segment pairs. It is guaranteed that the line starts
   and ends with an "on" line segment. If cheap is true, allow drawing less
   segments if the line is short */
void pcb_draw_dashed_line(pcb_draw_info_t *info, rnd_hid_gc_t GC, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2, unsigned int segs, rnd_bool_t cheap);


void pcb_draw(void);
void pcb_draw_obj(pcb_any_obj_t *obj);
void pcb_draw_layer(pcb_draw_info_t *info, const pcb_layer_t *ly);
void pcb_draw_layer_noxform(pcb_board_t *pcb, const pcb_layer_t *ly, const rnd_box_t *screen);

/* Same as pcb_draw_layer(), but never draws an implicit outline and ignores
   objects that are not in the subtree of data - useful for drawing a subtree,
   e.g. a subc only */
void pcb_draw_layer_under(pcb_board_t *pcb, const pcb_layer_t *Layer, const rnd_box_t *screen, pcb_data_t *data, rnd_xform_t *xform);

/* Composite draw all layer groups matching lyt/purpi/purpose */
void pcb_draw_groups(rnd_hid_t *hid, pcb_board_t *pcb, pcb_layer_type_t lyt, int purpi, char *purpose, const rnd_box_t *screen, const rnd_color_t *default_color, pcb_layer_type_t pstk_lyt_match, int thin_draw, int invert);


void pcb_erase_obj(int, void *, void *);
void pcb_draw_pstk_names(pcb_draw_info_t *info, rnd_layergrp_id_t group, const rnd_box_t *drawn_area);

/*#define PCB_BBOX_DEBUG*/

#ifdef PCB_BBOX_DEBUG
#define PCB_DRAW_BBOX(obj) \
	do { \
		rnd_hid_set_line_width(pcb_draw_out.fgGC, 0); \
		rnd_render->draw_rect(pcb_draw_out.fgGC, obj->BoundingBox.X1, obj->BoundingBox.Y1, obj->BoundingBox.X2, obj->BoundingBox.Y2); \
	} while(0)
#else
#define PCB_DRAW_BBOX(obj)
#endif


/* Returns whether lay_id is part of a group that is composite-drawn */
int pcb_draw_layer_is_comp(rnd_layer_id_t lay_id);

/* Returns whether a group is composite-drawn */
int pcb_draw_layergrp_is_comp(const pcb_layergrp_t *g);

/* Draw (render) or invalidate a terminal label */
void pcb_term_label_draw(pcb_draw_info_t *info, rnd_coord_t x, rnd_coord_t y, double scale, rnd_bool vert, rnd_bool centered, const pcb_any_obj_t *obj);
void pcb_term_label_invalidate(rnd_coord_t x, rnd_coord_t y, double scale, rnd_bool vert, rnd_bool centered, const pcb_any_obj_t *obj);
void pcb_label_draw(pcb_draw_info_t *info, rnd_coord_t x, rnd_coord_t y, double scale, rnd_bool vert, rnd_bool centered, const char *label, int prio);
void pcb_label_invalidate(rnd_coord_t x, rnd_coord_t y, double scale, rnd_bool vert, rnd_bool centered, const char *label);


void pcb_draw_setup_default_xform_info(rnd_hid_t *hid, pcb_draw_info_t *info);
void pcb_draw_setup_default_gui_xform(rnd_xform_t *dst);


/* Schedule an object to be called again at the end for drawing its labels 
   on top of everything. */
void pcb_draw_delay_label_add(pcb_any_obj_t *obj);

/* Return non-zero if extra terminal graphics should be drawn */
#define pcb_draw_term_need_gfx(obj) \
	(((obj)->term != NULL) && !PCB_FLAG_TEST(PCB_FLAG_FOUND, (obj)) && !PCB_FLAG_TEST(PCB_FLAG_WARN, (obj)) && !PCB_FLAG_TEST(PCB_FLAG_SELECTED, (obj)))

#define pcb_draw_term_hid_permission() (rnd_render->gui)

#define PCB_DRAW_TERM_GFX_WIDTH (-3)

void pcb_expose_main(rnd_hid_t *hid, const rnd_hid_expose_ctx_t *ctx, rnd_xform_t *xform_caller);
void pcb_expose_preview(rnd_hid_t *hid, rnd_hid_expose_ctx_t *e);

extern rnd_box_t pcb_last_main_expose_region;

#endif
