/*
 *  Eukleides version 1.5.4
 *  Copyright (c) Christian Obrecht 2004-2010
 *
 *  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 3 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "error.h"
#include "args.h"
#include "symbol.h"
#include "core.h"
#include "utils.h"
#include "conic.h"
#include "setting.h"
#include "check.h"
#include "parser.tab.h"

#define SIZE(x)	local_size*(x)

	/* Preamble and closing */

int scaled;

void put_preamble(void)
{
    static int needed = 1;

    if (!output_file) {
	if (output_name) {
	    output_file = fopen(output_name, "w");
	    if (output_file == NULL)
		fatal_error(_("Unable to open output file"));
	} else output_file = stdout;
    }
    if (needed) {
	fprintf(output_file, "%% Generated by euktopst 1.5.4\n");
	if (NEQL(scale, 1)) {
	    fprintf(output_file, "\\bgroup\n\\psset{unit=%.4fcm}\n", scale);
	    scaled = 1;
	}
	fprintf(output_file, "\\pspicture*(%.4f,%.4f)(%.4f,%.4f)\n",
		min_x, min_y, max_x, max_y);
	needed = 0;
    }
}

void put_closing(void)
{
    fprintf(output_file, "\\endpspicture\n");
    if (scaled) fprintf(output_file, "\\egroup\n");
    fprintf(output_file, "%% End of figure\n");
}

	/* Drawing */

void draw_point(void)
{
    _point *A;

    A = POP(_point);
    check_color();
    check_shape();
    check_dotsize();
    fprintf(output_file, "\\psdots");
    put_local();
    fprintf(output_file, "(%.4f,%.4f)\n", A->x, A->y);
}

void draw_vector(void)
{
    _point *A;
    _vector *v;

    A = POP(_point);
    v = POP(_vector);
    check_path_settings();
    check_arrowsize();
    fprintf(output_file, "\\psline");
    put_local();
    fprintf(output_file, "{->}(%.4f,%.4f)(%.4f,%.4f)\n",
	    A->x, A->y, A->x + v->x, A->y + v->y);
}

void put_set(_set *s)
{
    int n = 0;

    while (s != NULL) {
	if (n % 4 == 0) fputc('\n', output_file);
	fprintf(output_file, "(%.4f,%.4f)", s->p->x, s->p->y);
	s = s->next;
	n++;
    }
    fputc('\n', output_file);
}

void put_arrows(void)
{
    char b = 'c', e = 'c';

    if ((local_arrow == ARROW && local_dir == BACK) || local_arrow == ARROWS)
	b = '<';
    if ((local_arrow == ARROW && local_dir == FORTH) || local_arrow == ARROWS)
	e = '>';
    fprintf(output_file, "{%c-%c}%%", b, e);
}

void draw_path(void)
{
    _set* s;

    s = POP(_set);
    if (s == NULL || s->next == NULL) return;
    check_path_settings();
    check_arrowsize();
    fprintf(output_file, "\\psline");
    put_local();
    put_arrows();
    put_set(s);
}

void draw_polygon(void)
{
    _set* s;

    s = POP(_set);
    if (s == NULL) return;
    check_path_settings();
    fprintf(output_file, "\\pspolygon");
    put_local();
    put_set(s);
}

void fill_polygon(void)
{
    _set* s;

    s = POP(_set);
    if (s == NULL) return;
    check_color();
    fprintf(output_file, "\\pspolygon*");
    put_local();
    put_set(s);
}

void hatch_polygon(void)
{
     _set* s;

    check_hatch(POPn);
    s = POP(_set);
    if (s == NULL) return;
    fprintf(output_file, "\\pspolygon");
    put_local();
    put_set(s);
}

void draw_line(void)
{
    double m_x = min_x, m_y = min_y, M_x = max_x, M_y = max_y;
    double x[2], y[2], t, z;
    int i = 0;
    _line * l;

    l = POP(_line);
    check_path_settings();
    if (local_part == HALF) {
	if ((local_dir == FORTH && (l->a > -90 && l->a <= 90))
	    || (local_dir == BACK && (l->a <= -90 || l->a > 90))) m_x = l->x;
	else M_x = l->x;
	if ((local_dir == FORTH && l->a > 0)
	    || (local_dir == BACK && l->a <= 0)) m_y = l->y;
	else M_y = l->y;
    }
    if (l->a == 90 || l->a == -90) {
	if (l->x >= m_x && l->x <= M_x) {
	    x[0] = x[1] = l->x;
	    y[0] = m_y;
	    y[1] = M_y;
	    i = 2;
	}
    } else if (l->a == 0 || l->a == 180) {
	if (l->y >= m_y && l->y <= M_y) {
	    y[0] = y[1] = l->y;
	    x[0] = m_x;
	    x[1] = M_x;
	    i = 2;
	}
    } else {
	t = Tan(l->a);
	z = t*(m_x-l->x)+l->y;
	if (z >= m_y && z <= M_y) {
	    x[i] = m_x;
	    y[i] = z;
	    i++;
	}
	z = t*(M_x-l->x)+l->y;
	if (z >= m_y && z <= M_y) {
	    x[i] = M_x;
	    y[i] = z;
	    i++;
	}
	z =(m_y-l->y)/t+l->x;
	if (z > m_x && z < M_x && i < 2) {
	    x[i] = z ;
	    y[i] = m_y;
	    i++;
	}
	z = (M_y-l->y)/t+l->x;
	if (z > m_x && z < M_x && i < 2) {
	    x[i] = z;
	    y[i] = M_y;
	    i++;
	}
    }
    if (i == 2) {
	fprintf(output_file, "\\psline");
	put_local();
	fprintf(output_file, "{c-c}(%.4f,%.4f)(%.4f,%.4f)\n",
		x[0], y[0], x[1], y[1]);
    }
}

void draw_circle(void)
{
    _circle* c;

    c = POP(_circle);
    check_path_settings();
    fprintf(output_file, "\\pscircle");
    put_local();
    fprintf(output_file, "(%.4f,%.4f){%.4f}\n", c->x, c->y, c->r);
}

void draw_arc(void)
{
    double u, v;
    _circle* c;

    v = POPn;
    u = POPn;
    c = POP(_circle);
    check_path_settings();
    check_arrowsize();
    fprintf(output_file, "\\psarc");
    put_local();
    put_arrows();
    fprintf(output_file, "\n(%.4f,%.4f){%.4f}{%.4f}{%.4f}\n",
	    c->x, c->y, c->r, u, v);
}

void fill_circle(void)
{
    _circle* c;

    c = POP(_circle);
    check_color();
    fprintf(output_file, "\\pscircle*");
    put_local();
    fprintf(output_file, "(%.4f,%.4f){%.4f}\n", c->x, c->y, c->r);
}

void hatch_circle(void)
{
    _circle* c;

    check_hatch(POPn);
    c = POP(_circle);
    fprintf(output_file, "\\pscircle");
    put_local();
    fprintf(output_file, "(%.4f,%.4f){%.4f}\n", c->x, c->y, c->r);
}

	/* Conics */

double get_max(double x, double y)
{
    double m, l;
    
    m = hypot(min_x - x, min_y - y);
    l = hypot(max_x - x, min_y - y);
    if (l > m) m = l;
    l = hypot(max_x - x, max_y - y);
    if (l > m) m = l;
    l = hypot(min_x - x, max_y - y);
    if (l > m) m = l;
    return m;
}

void draw_parabolic_arc(double x0, double y0, double p, double f, double g,
			double c, double s)
{
    double x, y, e, t;
    int n;

    e = Acos(p/get_max(x0, y0) - 1);
    if (f < -e) f = -e;
    if (g > e) g = e;
    check_path_settings();
    fprintf(output_file, "\\pspolygon");
    put_local();
    for (t = f, n = 0; t <= g; t += local_step, n++) {
	parametric_parabola(&x, &y, t, x0, y0, p, c, s);
	if (n % 4 == 0) fputc('\n', output_file);
	fprintf(output_file,"(%.4f,%.4f)", x, y);
    }
    fputc('\n', output_file);
}

void draw_elliptic_arc(double x0, double y0, double a, double b,
		       double f, double g, double c, double s)
{
    double x, y, t;
    int n;

    check_path_settings();
    fprintf(output_file, "\\pspolygon");
    put_local();
    for (t = f, n = 0; t <= g; t += local_step, n++) {
	parametric_ellipse(&x, &y, t, x0, y0, a, b, c, s);
	if (n % 4 == 0) fputc('\n', output_file);
	fprintf(output_file,"(%.4f,%.4f)", x, y);
    }
    fputc('\n', output_file);
}

void draw_branch(double i, double j, double x0, double y0, double a, double b,
	double f, double g, double c, double s)
{
    double x, y, t;
    int n;

    if (f > i) i = f;
    if (g < j) j = g;
    check_path_settings();
    fprintf(output_file, "\\pspolygon");
    put_local();
    for (t = i, n = 0; t <= j; t += local_step, n++) {
	parametric_hyperbola(&x, &y, t, x0, y0, a, b, c, s);
	if (n % 4 == 0) fputc('\n', output_file);
	fprintf(output_file,"(%.4f,%.4f)", x, y);
    }
    fputc('\n', output_file);
}

void draw_hyperbolic_arc(double x0, double y0, double a, double b,
			 double f, double g, double c, double s)
{
    double e;

    e = Atan(b/get_max(x0, y0));
    if (f < -e) draw_branch(-180 + e, -e, x0, y0, a, b, f, g, c, s);
    if (g > e) draw_branch(e, 180 - e, x0, y0, a, b, f, g, c, s);
}

void draw_conic(void)
{
    _conic *C;
    
    C = POP(_conic);
    switch (C->type) {
	case PARABOLA:
	    draw_parabolic_arc(C->x, C->y, C->a, -180, 180,
			       Cos(C->d), Sin(C->d));
	    break;
	case ELLIPSE:
	    draw_elliptic_arc(C->x, C->y, C->a, C->b, -180, 180,
			      Cos(C->d), Sin(C->d));
	    break;
	case HYPERBOLA:
	    draw_hyperbolic_arc(C->x, C->y, C->a, C->b, -180, 180,
				Cos(C->d), Sin(C->d));
	    break;
    }
}

void draw_conic_arc(void)
{
    double f, g;
    _conic *C;
    
    g = POPn;
    f = POPn;
    C = POP(_conic);
    if (C->type == ELLIPSE && f > g) g += 360;
    if (f >= g) runtime_error("invalid boundaries");
    switch (C->type) {
	case PARABOLA:
	    draw_parabolic_arc(C->x, C->y, C->a, f, g, Cos(C->d), Sin(C->d));
	    break;
	case ELLIPSE:
	    draw_elliptic_arc(C->x, C->y, C->a, C->b, f, g,
			      Cos(C->d), Sin(C->d));
	    break;
	case HYPERBOLA:
	    draw_hyperbolic_arc(C->x, C->y, C->a, C->b, f, g,
				Cos(C->d), Sin(C->d));
	    break;
    }
}

	/* Strings */

void draw_string(_param p)
{
    double a, r, x, y;
    _point *O;
    _set *s;

    check_font();
    a = POPn;
    r = DEFAULT_DIST*local_size;
    if (p.addr) {
	s = POP(_set);
	O = get_point(&s);
	x = O->x;
	y = O->y;
	O = get_point(&s);
	x = (x + O->x)/2 + r*Cos(a);
	y = (y + O->y)/2 + r*Sin(a);
    } else {
	O = POP(_point);
	x = O->x + r*Cos(a);
	y = O->y + r*Sin(a);
    }
    fprintf(output_file, "\\rput(%.4f,%.4f){", x, y);
    put_font();
    fprintf(output_file, "%s}\n", POPs);
}

	/* Labels */

void label_segment(void)
{
    double size, a, x, y;
    _set *s;
    _point *A, *B;

    s = POP(_set);
    A = get_point(&s);
    B = get_point(&s);
    size = SIZE(.1);
    a = argument(A, B);
    x = (A->x + B->x)/2;
    y = (A->y + B->y)/2;
    fprintf(output_file, "\\pscustom");
    put_local();
    fprintf(output_file, "{\\");
    switch (local_segment) {
	case SIMPLE:
	    fprintf(output_file, "Simple");
	    break;
	case DOUBLE:
	    fprintf(output_file, "Double");
	    break;
	case TRIPLE:
	    fprintf(output_file, "Triple");
	    break;
	case CROSS:
	    fprintf(output_file, "Cross");
	    a += 45;
	    break;
    }
    fprintf(output_file, "{%.4f}{%.4f}{%.4f}{%.4f}}\n", x, y, a, size);
}

void draw_mark(const char* arrow, _point* B, double r, double a, double b)
{
    fprintf(output_file, "  \\psarc%s(%.4f,%.4f){%.4f}{%.4f}{%.4f}\n",
	    arrow, B->x, B->y, r, a, b);
}

void set_xy(_point* A, _point* B, _point* C, double d,
	double* x1, double* y1, double* x2, double* y2)
{
    double l;

    l = distance(B, A);
    if (ZERO(l)) runtime_error(_("invalid angle"));
    *x1 = d*(A->x - B->x)/l;
    *y1 = d*(A->y - B->y)/l;
    l = distance(B, C);
    if (ZERO(l)) runtime_error(_("invalid angle"));
    *x2 = d*(C->x - B->x)/l;
    *y2 = d*(C->y - B->y)/l;
}

void label_angle(void)
{
    double a, b, r, s, t,x1, y1, x2, y2;
    _point *A, *B, *C;

    check_color();
    C = POP(_point);
    B = POP(_point);
    A = POP(_point);
    a = argument(B, A);
    b = argument(B, C);
    r = SIZE(.5);
    s = .08/scale;
    fprintf(output_file, "\\pscustom");
    put_local();
    fprintf(output_file, "{\n");
    switch (local_angle) {
	case SIMPLE:
	    draw_mark("", B, r, a, b);
	    if (local_dec == DOTTED) {
		set_xy(A, B, C, SIZE(M_SQRT2/8), &x1, &y1, &x2, &y2);
		fprintf(output_file, "  \\Dot{%.4f}{%.4f}{%.4f}\n",
			B->x + x1 + x2, B->y + y1 + y2, SIZE(.05));
	    }
	    if (local_dec == DASHED) {
		fprintf(output_file, "  \\Dash{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}\n",
			 B->x, B->y, (a + b)/2, r - s, r + s);
	    }
	    break;
	case DOUBLE:
	    if (local_dec == DASHED) {
		draw_mark("", B, r, a, b);
		t = 8/local_size;
		fprintf(output_file, "  \\DoubleDash"
			"{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}\n",
			B->x, B->y, r - s, r + s, (a + b)/2 - t/2, t);
	    } else {
		fprintf(output_file, "  \\DoubleArc"
			"{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}\n",
			B->x, B->y, r - s/2, r + s/2, a, b);
	    }
	    break;
	case TRIPLE:
	    if (local_dec == DASHED) {
		draw_mark("", B, r, a, b);
		t = 8/local_size;
		fprintf(output_file, "  \\TripleDash"
			"{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}\n",
			B->x, B->y, r - s, r + s, (a + b)/2 - t, t);
	    } else {
		fprintf(output_file, "  \\TripleArc"
			"{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}\n",
			B->x, B->y, r - s, r, r + s, a, b);
	    }
	    break;
	case RIGHT:
	    set_xy(A, B, C, SIZE(.35), &x1, &y1, &x2, &y2);
	    fprintf(output_file, "  \\Right{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}{%.4f}\n",
		    B->x + x1, B->y + y1, x2, y2, -x1, -y1);
	    if (local_dec == DOTTED) {
		fprintf(output_file, "  \\Dot{%.4f}{%.4f}{%.4f}\n",
			B->x + (x1 + x2)/2, B->y + (y1 + y2)/2, SIZE(.05));
	    }
	    break;
	case FORTH:
	    draw_mark("{->}", B, r, a, b);
	    break;
	case BACK:
	    draw_mark("{<-}", B, r, a, b);
	    break;
    }
    fprintf(output_file, "}\n");
}

void label_point(_param p)
{
    double a, r;
    _symbol *s;
    _point *O;

    check_font();
    a = POPn;
    r = DEFAULT_DIST*local_size;
    s = (_symbol *)p.ptr;
    O = s->content->value.point;
    fprintf(output_file, "\\rput(%.4f,%.4f){", O->x + r*Cos(a), O->y + r*Sin(a));
    put_font();
    fprintf(output_file, "$%s$}\n", s->symbol);
}
