/*
 *                 GRAPH, Version 1.00 - 4 August 1989
 *
 *            Copyright 1989, David Gay. All Rights Reserved.
 *            This software is freely redistrubatable.
 */

/* The default/non virtual methods for class function */
#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/text.h>
#include <math.h>
#include <string.h>

#include "object.h"
#include "object/default.h"
#include "object/function.h"
#include "file.h"
#include "graph.h"
#include "uio.h"
#include "coords.h"
#include "list.h"
#include "grph.h"
#include "user/eval.h"
#include "user/gadgets.h"
#include "tracker.h"

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>

/* Draw a function */
void display_function(struct function *this)
{
    int up;
    point *pt;
    struct RWindow *const rwin = this->o.g->io.rw;
    int pen = this->selected ? (this->colour == 3 ? 2 : 3) : this->colour;

    up = TRUE;

    SetAPen(rwin->rp, pen);
    SetDrMd(rwin->rp, JAM1);

    /* Scan points (note that all types of function points have point as base c
lass) */
    for (pt = first(&this->pts); succ(pt); pt = succ(pt))
    {
        if (pt->state & EXISTS)
        {
            if (up) RMove(rwin, pt->x, pt->y);
            else RDraw(rwin, pt->x, pt->y);
            up = this->showdisc && (pt->state & DISC);
        }
        else
            up = TRUE;
    }
}

/* Select function, redraw in new colour */
static void select_function(struct function *this)
{
    this->selected = TRUE;
    if (this->o.ok && this->o.g->ok && this->o.g->io.rw && this->calc)
        display_function(this);
}

/* Deselect function */
static struct Region *deselect_function(struct function *this)
{
    this->selected = FALSE;
    if (this->o.ok && this->o.g->ok && this->o.g->io.rw && this->calc)
        display_function(this);
    return NULL; /* No refresh needed */
}

/* Did user press mouse on function ? */
static int down_function(struct function *this)
{
    int inside = FALSE;

    if (this->o.ok && this->calc)
    {
        struct graph *g = this->o.g;
        point *pt;
        long sx = ftol(g->io.rw->sx(g->io.rw, g->s.x));
        long sy = ftol(g->io.rw->sy(g->io.rw, g->s.y));
        long x0, y0, x1, y1;
        int up = TRUE;

        /* Check if mouse "near" drawn line */
        for (pt = first(&this->pts); succ(pt); pt = succ(pt))
        {
            if (pt->state & EXISTS)
            {
                x1 = ftol(g->io.rw->sx(g->io.rw, pt->x));
                y1 = ftol(g->io.rw->sy(g->io.rw, pt->y));

                if (!up) /* segment exists, calc distance to it */
                {
                    /* A little vectorial algebra */
                    long ab_x, ab_y, u_x, u_y, h, hd, dist_x, dist_y, dist;

                    ab_x = sx - x0; ab_y = sy - y0;
                    u_x = x1 - x0; u_y = y1 - y0;
                    h = (ab_x * u_x + ab_y * u_y);
                    hd = (u_x * u_x + u_y * u_y);
                    if (hd != 0 && 0 <= h && h <= hd) /* intersection on segmen
t */
                    {
                        dist_x = (hd * ab_x - h * u_x) / hd; dist_y = (hd * ab_
y - h * u_y) / hd;
                        dist = dist_x * dist_x + dist_y * dist_y;
                        if (dist < (FDIST * FDIST))
                        {
                            /* We're near segment ! */
                            inside = TRUE;
                            break;
                        }
                    }
                    /* Near point ? */
                    else if ((x1 - sx) * (x1 - sx) + (y1 - sy) * (y1 - sy) <= F
DIST * FDIST)
                    {
                        inside = TRUE;
                        break;
                    }
                }

                up = this->showdisc && (pt->state & DISC);
                x0 = x1; y0 = y1;
            }
            else
                up = TRUE;

        }
    }
    return inside;
}

/* Impossible ... */
static void move_function(struct function *this)
{
}

/* Nothing to do */
static struct Region *up_function(struct function *this)
{
    return NULL;
}

/* redraw function, calc if necessary */
static void draw_function(struct function *this, int allow_mes)
{
    if (!this->calc) this->calc = this->calcf(this, allow_mes);
    if (this->calc) display_function(this);
}

/* variable name changed. recalc necessary ? */
static void var_change_function(struct function *this, char *name)
{
    if (this->calc && FindName(&this->used, name))
    {
        free_list(&this->pts, this->sizept);
        this->calc = FALSE;
    }
}

/* Write function to file */
static int save_function(struct function *this, FILE *f)
{
    short tag = FUNCTION_TAG;
    short end = FUNCTION_END;
    short showdisc = this->showdisc; /* Can't write bitfields directly ... */
    short nicedisc = this->nicedisc;

    return WRITE(f, tag) &&
           this->save(this, f) &&    /* Easier than rewriting this for each fun
ction */
           WRITE(f, this->o.name) &&
           WRITE(f, this->vname) &&
           WRITE(f, this->min) &&
           WRITE(f, this->max) &&
           WRITE(f, this->steps) &&
           WRITE(f, this->colour) &&
           WRITE(f, showdisc) &&
           WRITE(f, nicedisc) &&
           WRITE(f, end);
}

/* Read a function from a file */
struct function *load_function(struct graph *g, FILE *f)
{
    short tag;
    struct function *this;

    if (READ(f, tag)) /* Determine which type of function */
        switch (tag)
        {
            case F_OF_X_TAG:
                this = (struct function *)load_f_of_x(g, f);
                break;
            case X_Y_TAG:
                this = (struct function *)load_x_y(g, f);
                break;
            case R_OF_T_TAG:
                this = (struct function *)load_r_of_t(g, f);
                break;
            case R_T_TAG:
                this = (struct function *)load_r_t(g, f);
                break;
            default:
                message(g, "Not a graph file", (char *)NULL);
        }
    return this;
}

/* Read "standard" part of function from file */
int load_rest(struct function *this, FILE *f)
{
    short showdisc, nicedisc, end;

    if (READ(f, this->o.name) &&
        READ(f, this->vname) &&
        READ(f, this->min) &&
        READ(f, this->max) &&
        READ(f, this->steps) &&
        READ(f, this->colour) &&
        READ(f, showdisc) && /* Can't read bitfields directly */
        READ(f, nicedisc) &&
        READ(f, end) &&
        end == FUNCTION_END)
    {
        this->showdisc = showdisc;
        this->nicedisc = nicedisc;
        return TRUE;
    }
    return FALSE;
}

/* Nothing to do (don't care about resolution) */
static int inform_function(struct function *this)
{
    return TRUE;
}

/* idem */
static void confirm_function(struct object *this, int ok)
{
}

/* Standard function init */
void init_function(struct function *this, struct graph *g, char *name)
{
    const static struct function def_f =
    {
        {
            { NULL }, NULL, "", FALSE, 0, 0,
            /* Default methods */
            (void *)uncalled, (void *)select_function, (void *)deselect_functio
n,
            (void *)down_function, (void *)move_function, (void *)up_function,
            (void *)uncalled, (void *)draw_function, (void *)ref_uncalled,
            (void *)uncalled, (void *)var_change_function, (void *)save_functio
n,
            (void *)inform_function, (void *)confirm_function
        },
        (void *)uncalled, (void *)uncalled,
        "", NOVAL, NOVAL, INOVAL,
        1, TRUE, TRUE, FALSE, FALSE
    };

    *this = def_f;
    this->o.g = g;
    strcpy(this->o.name, name);
    init_var_list(&this->used);
}

/* Create a new function (ask user which type he wants) */
struct function *new_function(struct graph *g)
{
    struct Requester *req;
    struct Memory *m;
    struct Gadget *gl = NULL, *fx, *xy, *rt, *r2;
    char name[FNAMELEN];
    int ret = FALSE;
    struct function *o = NULL;

    name[0] = '\0';

    if ((m = NewMemory()) &&
        (req = InitReq(50, 20, 230, 105, m)) &&
        SetReqBorder(req, 1, m) &&
        AddIntuiText(&req->ReqText, "Add Function", 67, 6, m) &&
        AddText(&gl, 0, "Name ", FALSE, name, FNAMELEN, TRUE, 0, RELVERIFY, 51,
 20, 100, 10, TRUE, m) &&
        (fx = AddRadio(&gl, 0, "f(x)", TRUE, SELECTED, RELVERIFY, 28, 11, 40, 1
0, 10, m)) &&
        (rt = AddRadio(&gl, 0, "r(theta)", TRUE, 0, RELVERIFY, 26, 105, 40, 10,
 10, m)) &&
        (xy = AddRadio(&gl, 0, "x(t),y(t)", TRUE, 0, RELVERIFY, 22, 11, 60, 10,
 10, m)) &&
        (r2 = AddRadio(&gl, 0, "r(t),theta(t)", TRUE, 0, RELVERIFY, 14, 105, 60
, 10, 10, m)) &&
        AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 25, 80, 65, 15, FALSE
, m) &&
        AddBox(&gl, FALSE, "Cancel", 0, RELVERIFY | ENDGADGET, 140, 80, 65, 15,
 FALSE, m))
    {
        SetReqGadgets(req, gl);
        if (ret = DoRequest(req, g, std_ghandler))
        {
            strip(name);
            if (*name)
            {
                if (fx->Flags & SELECTED) o = (struct function *)new_f_of_x(g,
name);
                else if (xy->Flags & SELECTED) o = (struct function *)new_x_y(g
, name);
                else if (rt->Flags & SELECTED) o = (struct function *)new_r_of_
t(g, name);
                else o = (struct function *)new_r_t(g, name);
            }
            else
                message(g, "Blank names not allowed", (char *)NULL);
        }
    }
    Free(m);
    return o;
}

