#include <../../nrnconf.h>
#if HAVE_IV  // to end of file

#include <stdio.h>
#include <ivstream.h>
#include <InterViews/background.h>
#include <InterViews/canvas.h>
#include <InterViews/printer.h>
#include <InterViews/label.h>
#include <IV-look/kit.h>
#include <math.h>
#include "scenevie.h"
#include "mymath.h"
#include "axis.h"
#include "rect.h"
#include "graph.h"
#include "idraw.h"

/*static*/ class GAxisItem: public GraphItem {
  public:
    GAxisItem(Glyph* g)
        : GraphItem(g) {}
    virtual ~GAxisItem(){};
    virtual void save(ostream&, Coord, Coord) {}
    virtual void erase(Scene* s, GlyphIndex i, int type) {
        if (type & GraphItem::ERASE_AXIS) {
            s->remove(i);
        }
    }
};

Axis::Axis(Scene* s, DimensionName d) {
    s_ = s;
    d_ = d;
    set_range();
    location();
    init(min_, max_, pos_, ntic_);
}

Axis::Axis(Scene* s, DimensionName d, Coord x1, Coord x2) {
    s_ = s;
    d_ = d;
    min_ = x1;
    max_ = x2;
    location();
    MyMath::round_range_down(x1, x2, amin_, amax_, ntic_);
    init(float(amin_), float(amax_), pos_, ntic_);
}

Axis::Axis(Scene* s,
           DimensionName d,
           Coord x1,
           Coord x2,
           Coord pos,
           int ntic,
           int nminor,
           int invert,
           bool number) {
    s_ = s;
    d_ = d;
    init(x1, x2, pos, ntic, nminor, invert, number);
}

void Axis::init(Coord x1, Coord x2, Coord pos, int ntic, int nminor, int invert, bool number) {
    min_ = x1;
    max_ = x2;
    pos_ = pos;
    ntic_ = ntic;
    nminor_ = nminor;
    invert_ = invert;
    number_ = number;
    amin_ = x1;
    amax_ = x2;
    s_->attach(this);
    install();
}

Axis::~Axis() {
    s_->detach(this);
    //	printf("~Axis\n");
}

void Axis::size(float& min, float& max) {
    min = float(amin_);
    max = float(amax_);
}

void Axis::save(ostream& o) {
    char buf[256];
    int c;
    if (d_ == Dimension_X) {
        c = 'x';
    } else {
        c = 'y';
    }
    sprintf(buf,
            "save_window_.%caxis(%g,%g,%g,%d,%d,%d,%d)",
            c,
            amin_,
            amax_,
            pos_,
            ntic_,
            nminor_,
            invert_,
            number_);
    o << buf << endl;
}

void Axis::update(Observable*) {}

bool Axis::set_range() {
    Coord x1, x2;
    if (d_ == Dimension_X) {
        x1 = s_->x1();
        x2 = s_->x2();
    } else {
        x1 = s_->y1();
        x2 = s_->y2();
    }

    min_ = x1;
    max_ = x2;
    MyMath::round_range(x1, x2, amin_, amax_, ntic_);
    return true;
}

void Axis::location() {
    Coord x1, y1, x2, y2;
    XYView* v = XYView::current_pick_view();
    if (v && v->scene() == s_) {
        v->zin(x1, y1, x2, y2);
    } else {
        x1 = s_->x1();
        x2 = s_->x2();
        y1 = s_->y1();
        y2 = s_->y2();
    }
    if (d_ == Dimension_X) {
        if (y1 > 0) {
            pos_ = y1;
        } else if (y2 < 0) {
            pos_ = y2;
        } else {
            pos_ = 0;
        }
    } else {
        if (x1 > 0) {
            pos_ = x1;
        } else if (x2 < 0) {
            pos_ = x2;
        } else {
            pos_ = 0;
        }
    }
}
#if 1
// Zach Mainen improvements

void Axis::install() {
    int i, j;
    GlyphIndex gi;
    Line* tic = NULL;
    Coord x, y;
    char str[20];
    float tic_space;

    float y_align, x_align;
    Line* minor_tic = NULL;

    Coord l_minor = 5.;
    Coord length = 10.;

    if (invert_ == 1) {
        l_minor *= -1.;
        length *= -1.;
    }

    char pform[10];

    int addprec;
    double s = (amax_ - amin_) / float(ntic_);
    while (s < 1)
        s *= 10.;
    if (s == 1 || s == 2)
        addprec = 0;
    else
        addprec = 1;

    double logstep = -log10((amax_ - amin_) / float(ntic_));

    if (d_ == Dimension_X) {
        if (logstep >= 0 && logstep <= 5) {
            sprintf(pform, "%%0.%.0ff", logstep + addprec);
        } else {
            sprintf(pform, "%%g");
        }
        y = pos_;
        s_->append(new GAxisItem(new Line(amax_ - amin_, 0)));
        gi = s_->count() - 1;
        s_->move(gi, amin_, y);

        tic = new Line(0, length);
        tic->ref();
        minor_tic = new Line(0, l_minor);
        minor_tic->ref();
        tic_space = (amax_ - amin_) / float(ntic_);

        for (i = 0; i <= ntic_; ++i) {
            x = amin_ + i * tic_space;
            if (Math::abs(x) < 1e-10) {
                x = 0.;
            }
            if (invert_ >= 0) {
                s_->append_fixed(new GAxisItem(tic));
                gi = s_->count() - 1;
                s_->move(gi, x, y);
            }

            if (number_) {
                /*  if (i==0) x_align = 0.;
                  else if (i==ntic_) x_align = .9;
                  else x_align = 0.5;
                */
                x_align = 0.5;
                if (invert_ == 1)
                    y_align = -0.3;
                else
                    y_align = 1.5;
                sprintf(str, pform, x);
                s_->append_fixed(new GAxisItem(
                    new GLabel(str, Appear::default_color(), true, 1, x_align, y_align)));
                gi = s_->count() - 1;
                s_->move(gi, x, y);
            }

            if (i < ntic_ && invert_ >= 0) {
                for (j = 0; j < nminor_; j++) {
                    x = amin_ + i * tic_space + j * tic_space / nminor_;
                    s_->append_fixed(new GAxisItem(minor_tic));
                    gi = s_->count() - 1;
                    s_->move(gi, x, y);
                }
            }
        }
    } else {
        if (logstep >= 0 && logstep <= 5) {
            sprintf(pform, " %%0.%.0ff ", logstep + 1);
        } else {
            sprintf(pform, " %%g ");
        }
        x = pos_;
        s_->append(new GAxisItem(new Line(0, amax_ - amin_)));
        gi = s_->count() - 1;
        s_->move(gi, x, amin_);

        tic = new Line(length, 0);
        tic->ref();
        minor_tic = new Line(l_minor, 0);
        minor_tic->ref();
        tic_space = (amax_ - amin_) / float(ntic_);

        for (i = 0; i <= ntic_; ++i) {
            y = amin_ + i * tic_space;
            if (invert_ >= 0) {
                s_->append_fixed(new GAxisItem(tic));
                gi = s_->count() - 1;
                s_->move(gi, x, y);
            }

            if (number_) {
                sprintf(str, pform, y);
                y_align = 0.5;
                // if (i==0) y_align = 0.;
                // else if (i==ntic_) y_align = .66;
                // else y_align = 0.33;
                if (invert_ == 1)
                    x_align = 0;
                else
                    x_align = 1.3;
                s_->append_fixed(new GAxisItem(
                    new GLabel(str, Appear::default_color(), true, 1, x_align, y_align)));
                gi = s_->count() - 1;
                s_->move(gi, x, y);
            }

            if (i < ntic_ && invert_ >= 0) {
                for (j = 0; j < nminor_; j++) {
                    y = amin_ + i * tic_space + j * tic_space / nminor_;
                    s_->append_fixed(new GAxisItem(minor_tic));
                    gi = s_->count() - 1;
                    s_->move(gi, x, y);
                }
            }
        }
    }
    Resource::unref(tic);
    Resource::unref(minor_tic);
}
#else
void Axis::install() {
    int i;
    GlyphIndex gi;
    Coord length = 10;
    Line* tic = NULL;
    Coord x, y;
    char str[20];
    if (d_ == Dimension_X) {
        y = pos_;
        s_->append(new GAxisItem(new Line(amax_ - amin_, 0)));
        gi = s_->count() - 1;
        s_->move(gi, amin_, y);

        tic = new Line(0, length);
        tic->ref();
        for (i = 0; i <= ntic_; ++i) {
            x = amin_ + i * (amax_ - amin_) / float(ntic_);
            if (Math::abs(x) < 1e-10) {
                x = 0.;
            }
            s_->append_fixed(new GAxisItem(tic));
            gi = s_->count() - 1;
            s_->move(gi, x, y);
            sprintf(str, "%g", x);
            s_->append_fixed(
                new GAxisItem(new GLabel(str, Appear::default_color(), true, 1, .5, 1.1)));
            gi = s_->count() - 1;
            s_->move(gi, x, y);
        }
    } else {
        x = pos_;
        s_->append(new GAxisItem(new Line(0, amax_ - amin_)));
        gi = s_->count() - 1;
        s_->move(gi, x, amin_);

        tic = new Line(length, 0);
        tic->ref();
        for (i = 0; i <= ntic_; ++i) {
            y = amin_ + i * (amax_ - amin_) / float(ntic_);
            s_->append_fixed(new GAxisItem(tic));
            gi = s_->count() - 1;
            s_->move(gi, x, y);
            sprintf(str, "%g", y);
            s_->append_fixed(
                new GAxisItem(new GLabel(str, Appear::default_color(), true, 1, 1, .5)));
            gi = s_->count() - 1;
            s_->move(gi, x, y);
        }
    }
    Resource::unref(tic);
}
#endif

BoxBackground::BoxBackground()
    : Background(NULL, Scene::default_background()) {}

BoxBackground::~BoxBackground() {}

void BoxBackground::draw(Canvas* c, const Allocation& a) const {
    Background::draw(c, a);
    draw_help(c, a);
}
void BoxBackground::print(Printer* c, const Allocation& a) const {
    Background::print(c, a);
    draw_help(c, a);
}

#define IDLINE(x1, y1, x2, y2, color, br) \
    c->line(x1, y1, x2, y2, color, br);   \
    IfIdraw(line(c, x1, y1, x2, y2, color, br));

void BoxBackground::draw_help(Canvas* c, const Allocation&) const {
    // printf("BoxBackground::draw\n");
    const Color* color = Scene::default_foreground();
    Coord x1, y1, x2, y2;
    double d1, d2;
    int xtic, ytic;
    XYView& v = *XYView::current_draw_view();
    v.zin(x1, y1, x2, y2);
    MyMath::round_range_down(x1, x2, d1, d2, xtic);
    x1 = d1;
    x2 = d2;
    MyMath::round_range_down(y1, y2, d1, d2, ytic);
    y1 = d1;
    y2 = d2;
    const Transformer& tr = v.s2o();
    c->push_transform();
    c->transform(tr);
    IfIdraw(pict(tr));
    Coord l, r, b, t;
    tr.inverse_transform(x1, y1, l, b);
    tr.inverse_transform(x2, y2, r, t);
    const Brush* br = Appear::default_brush();
    c->rect(l, b, r, t, color, br);
    IfIdraw(rect(c, l, b, r, t, color, br, false));
    const Coord tic = 10;
    Coord x, y;

    Coord dtic = (r - l) / xtic;
    Coord dx = (x2 - x1) / xtic;
    int i;
    for (i = 0; i <= xtic; ++i) {
        x = l + i * dtic;
        if (i > 0 && i < xtic) {
            IDLINE(x, b, x, b + tic, color, br);
            IDLINE(x, t, x, t - tic, color, br);
        }
        tic_label(x, b - 5, x1 + i * dx, .5, 1, c);
    }
    dtic = (t - b) / ytic;
    dx = (y2 - y1) / ytic;
    for (i = 0; i <= ytic; ++i) {
        y = b + i * dtic;
        if (i > 0 && i < ytic) {
            IDLINE(l, y, l + tic, y, color, br);
            IDLINE(r, y, r - tic, y, color, br);
        }
        tic_label(l - 5, y, y1 + i * dx, 1, .5, c);
    }

    c->clip_rect(l, b, r, t);
    c->pop_transform();
    IfIdraw(end());
}

void BoxBackground::tic_label(Coord x1, Coord y1, Coord val, float xa, float ya, Canvas* c) const {
    char buf[20];
    sprintf(buf, "%g", val);
    Glyph* g = new Label(buf, WidgetKit::instance()->font(), Appear::default_color());
    g->ref();
    Requisition req;
    g->request(req);
    Allocation a;
    a.x_allotment().origin(x1 - xa * req.x_requirement().natural());
    a.y_allotment().origin(y1 - ya * req.y_requirement().natural());
    g->draw(c, a);
    g->unref();
    if (OcIdraw::idraw_stream) {
        Transformer t;
        t.translate(a.x(), a.y());
        OcIdraw::text(c, buf, t, NULL, Appear::default_color());
    }
}

AxisBackground::AxisBackground()
    : Background(NULL, Scene::default_background()) {}

AxisBackground::~AxisBackground() {}

void AxisBackground::draw(Canvas* c, const Allocation& a) const {
    Background::draw(c, a);
    draw_help(c, a);
}
void AxisBackground::print(Printer* c, const Allocation& a) const {
    Background::print(c, a);
    draw_help(c, a);
}

void AxisBackground::draw_help(Canvas* c, const Allocation&) const {
    // printf("AxisBackground::draw\n");
    const Color* color = Scene::default_foreground();
    Coord x1, y1, x2, y2;
    double d1, d2;
    int xtic, ytic;
    XYView& v = *XYView::current_draw_view();
    v.zin(x1, y1, x2, y2);
    MyMath::round_range_down(x1, x2, d1, d2, xtic);
    x1 = d1;
    x2 = d2;
    MyMath::round_range_down(y1, y2, d1, d2, ytic);
    y1 = d1;
    y2 = d2;
    const Transformer& tr = v.s2o();
    c->push_transform();
    c->transform(tr);
    IfIdraw(pict(tr));
    Coord l, r, b, t;
    tr.inverse_transform(x1, y1, l, b);
    tr.inverse_transform(x2, y2, r, t);
    Coord xorg, yorg, xo, yo;
    if (MyMath::inside(0, x1, x2)) {
        xorg = 0;
    } else {
        xorg = x1;
    }
    if (MyMath::inside(0, y1, y2)) {
        yorg = 0;
    } else {
        yorg = y1;
    }
    tr.inverse_transform(xorg, yorg, xo, yo);
    const Brush* br = Appear::default_brush();
    IDLINE(l, yo, r, yo, color, br);
    IDLINE(xo, b, xo, t, color, br);
    const Coord tic = 10;
    Coord x, y;

    Coord dtic = (r - l) / xtic;
    Coord dx = (x2 - x1) / xtic;
    int i;
    for (i = 0; i <= xtic; ++i) {
        x = l + i * dtic;
        IDLINE(x, yo, x, yo + tic, color, br);
        tic_label(x, yo - 5, x1 + i * dx, .5, 1, c);
    }
    dtic = (t - b) / ytic;
    dx = (y2 - y1) / ytic;
    for (i = 0; i <= ytic; ++i) {
        y = b + i * dtic;
        IDLINE(xo, y, xo + tic, y, color, br);
        tic_label(xo - 5, y, y1 + i * dx, 1, .5, c);
    }

    c->pop_transform();
    IfIdraw(end());
}

void AxisBackground::tic_label(Coord x1, Coord y1, Coord val, float xa, float ya, Canvas* c) const {
    char buf[20];
    sprintf(buf, "%g", val);
    Glyph* g = new Label(buf, WidgetKit::instance()->font(), Appear::default_color());
    g->ref();
    Requisition req;
    g->request(req);
    Allocation a;
    a.x_allotment().origin(x1 - xa * req.x_requirement().natural());
    a.y_allotment().origin(y1 - ya * req.y_requirement().natural());
    g->draw(c, a);
    g->unref();
    if (OcIdraw::idraw_stream) {
        Transformer t;
        t.translate(a.x(), a.y());
        OcIdraw::text(c, buf, t, NULL, Appear::default_color());
    }
}

#endif
