/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - standard tools
 *  Copyright (C) 2020 Tibor 'Igor2' Palinkas
 *  (copied from camv-rnd by the author)
 *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/sch-rnd
 *    contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html
 *    mailing list: http://www.repo.hu/projects/sch-rnd/contact.html
 */

#include <libcschem/config.h>
#include <libcschem/cnc_line.h>
#include <libcschem/cnc_pen.h>
#include <libcschem/operation.h>
#include <libcschem/util_wirenet.h>
#include "sch-rnd/crosshair.h"
#include "sch-rnd/conf_core.h"
#include <librnd/core/compat_misc.h>
#include <librnd/core/misc_util.h>
#include <librnd/hid/hid.h>
#include <librnd/hid/hid_inlines.h>

typedef enum {
	CSCH_TOOL_LINEDIR_ALL = 0,
	CSCH_TOOL_LINEDIR_HV,
	CSCH_TOOL_LINEDIR_VH
} csch_tool_line_dir_t;

typedef struct {
	/* config */
	csch_tool_line_dir_t dir;
	unsigned dir_temp_inv:1;
	csch_cpen_t *stroke;
	const char *stroke_name;

	/* statates */
	csch_coord_t x1, y1, x2, y2; /* in cschem coords, not in screen coords */
	int clicked; /* 0 means waiting for x1,y1, 1 means waiting for x2,y2 */
} csch_tool_line_t;

csch_tool_line_t csch_tool_line;




/* returns whether the line is finished */
static int line_release(rnd_design_t *hl)
{
	switch(csch_tool_line.clicked) {
		case 0:
			csch_tool_line.x1 = csch_tool_line.x2 = P2C(sch_rnd_crosshair_x);
			csch_tool_line.y1 = csch_tool_line.y2 = P2C(sch_rnd_crosshair_y);
			csch_tool_line.clicked = 1;
			return 0;
		case 1:
			csch_tool_line.x2 = P2C(sch_rnd_crosshair_x);
			csch_tool_line.y2 = P2C(sch_rnd_crosshair_y);
			return 1;
	}
	return 0;
}

static int line_is_zero_len(void)
{
	if (!csch_tool_line.clicked) return 0;
	if (csch_tool_line.x1 != csch_tool_line.x2) return 0;
	if (csch_tool_line.y1 != csch_tool_line.y2) return 0;
	return 1;
}

/* called after a line is created; stops or continues drawing, depending on
   the config */
static void line_continue(rnd_design_t *hl)
{
	if (conf_core.editor.line_cont) {
		csch_tool_line.x1 = csch_tool_line.x2;
		csch_tool_line.y1 = csch_tool_line.y2;
	}
	else
		csch_tool_line.clicked = 0;
}


static void line_adjust_attached_objects(rnd_design_t *hl)
{
	if (csch_tool_line.clicked == 1) {
		csch_coord_t chx = P2C(sch_rnd_crosshair_x), chy = P2C(sch_rnd_crosshair_y);
		if ((csch_tool_line.dir == CSCH_TOOL_LINEDIR_ALL) && rnd_gui->control_is_pressed(rnd_gui)) {
			double rad = atan2(chy - csch_tool_line.y1, chy - csch_tool_line.x1);
			double deg = rad * RND_RAD_TO_DEG;
			double len = rnd_distance(chx, chy, csch_tool_line.x1, csch_tool_line.y1);
			deg = rnd_round(deg/15.0) * 15.0;
			rad = deg / RND_RAD_TO_DEG;
			csch_tool_line.x2 = csch_tool_line.x1 + cos(rad) * len;
			csch_tool_line.y2 = csch_tool_line.y1 + sin(rad) * len;
		}
		else {
			csch_tool_line.dir_temp_inv = rnd_gui->shift_is_pressed(rnd_gui);
			csch_tool_line.x2 = chx;
			csch_tool_line.y2 = chy;
		}
	}
}

/* returns 1 if the segment is valid; idx is 0 or 1 */
static int line_get_seg(rnd_design_t *hl, int idx, csch_coord_t *x1, csch_coord_t *y1, csch_coord_t *x2, csch_coord_t *y2)
{
	csch_tool_line_dir_t tmpdir = csch_tool_line.dir;
	if ((tmpdir == CSCH_TOOL_LINEDIR_HV) || (tmpdir == CSCH_TOOL_LINEDIR_VH)) {
		int refr = !!conf_core.editor.line_refraction;
		if (csch_tool_line.dir_temp_inv)
			refr = !refr;
		if (refr) {
			if (tmpdir == CSCH_TOOL_LINEDIR_HV) tmpdir = CSCH_TOOL_LINEDIR_VH;
			else tmpdir = CSCH_TOOL_LINEDIR_HV;
		}
	}

	switch(tmpdir) {
		case CSCH_TOOL_LINEDIR_ALL:
			if (idx == 0) {
				*x1 = csch_tool_line.x1; *y1 = csch_tool_line.y1;
				*x2 = csch_tool_line.x2; *y2 = csch_tool_line.y2;
				return 1;
			}
			return 0;
		case CSCH_TOOL_LINEDIR_HV:
			if (idx == 0) {
				*x1 = csch_tool_line.x1; *y1 = csch_tool_line.y1;
				*x2 = csch_tool_line.x2; *y2 = csch_tool_line.y1;
			}
			else {
				*x1 = csch_tool_line.x2; *y1 = csch_tool_line.y1;
				*x2 = csch_tool_line.x2; *y2 = csch_tool_line.y2;
			}
			return 1;
		case CSCH_TOOL_LINEDIR_VH:
			if (idx == 0) {
				*x1 = csch_tool_line.x1; *y1 = csch_tool_line.y1;
				*x2 = csch_tool_line.x1; *y2 = csch_tool_line.y2;
			}
			else {
				*x1 = csch_tool_line.x1; *y1 = csch_tool_line.y2;
				*x2 = csch_tool_line.x2; *y2 = csch_tool_line.y2;
			}
			return 1;
	}
	return 0;
}

static void line_draw_attached(rnd_design_t *hl, csch_cgrp_t *parent)
{
	csch_coord_t x1, y1, x2, y2;
	rnd_coord_t ps = (csch_tool_line.stroke == NULL) ? C2P(1000) : C2P(csch_tool_line.stroke->size);

	rnd_hid_set_line_cap(sch_rnd_crosshair_gc, rnd_cap_round);
	rnd_hid_set_line_width(sch_rnd_crosshair_gc, ps);

	switch(csch_tool_line.clicked) {
		case 0:
/*rnd_printf("%mm %mm\n", sch_rnd_crosshair_x, sch_rnd_crosshair_y);*/
			rnd_render->draw_line(sch_rnd_crosshair_gc, sch_rnd_crosshair_x, sch_rnd_crosshair_y, sch_rnd_crosshair_x, sch_rnd_crosshair_y);
			break;
		case 1:
			if (line_get_seg(hl, 0, &x1, &y1, &x2, &y2))
				rnd_render->draw_line(sch_rnd_crosshair_gc, C2P(x1), C2P(y1), C2P(x2), C2P(y2));
			if (line_get_seg(hl, 1, &x1, &y1, &x2, &y2))
				rnd_render->draw_line(sch_rnd_crosshair_gc, C2P(x1), C2P(y1), C2P(x2), C2P(y2));
			break;
	}
}

static void line_init(void)
{
	csch_tool_line.clicked = 0;
}

static void line_uninit(void)
{
	csch_tool_line.clicked = 0;
}

static void line_create_all(rnd_design_t *hl, csch_cgrp_t *parent, int wirenet)
{
	csch_sheet_t *sheet = (csch_sheet_t *)hl;
	int n, chg = 0;

	for(n = 0; n < 2; n++) {
		csch_coord_t x1, y1, x2, y2;
		if (line_get_seg(hl, n, &x1, &y1, &x2, &y2)) {
			if ((x1 == x2) && (y1 == y2))
				continue;
			if (!wirenet) {
				csch_line_t *line = (csch_line_t *)csch_op_create(sheet, parent, CSCH_CTYPE_LINE);

				if (line == NULL) {
					rnd_message(RND_MSG_ERROR, "Failed to create line object\n");
					return;
				}

				line->spec.p1.x = x1; line->spec.p1.y = y1;
				line->spec.p2.x = x2; line->spec.p2.y = y2;

				line->hdr.stroke_name = csch_comm_str(sheet, csch_tool_line.stroke_name, 1);
				csch_line_update(sheet, line, 1);
				chg++;
			}
			else {
				csch_wirenet_draw(sheet, csch_comm_str(sheet, csch_tool_line.stroke_name, 1), x1, y1, x2, y2);
				chg++;
			}
		}
	}

	if (chg)
		csch_sheet_set_changed(sheet, 1);
}
