#ifndef rubberband_h
#define rubberband_h

#include <InterViews/handler.h>
#include <InterViews/event.h>
#include <InterViews/transformer.h>

#undef Rubberband
#undef RubberLine
#undef RubberRect

class RubberAction;
class Rubberband;
class Color;
class Brush;
class Canvas;
class Printer;

// called on rubberband release event
class RubberAction: public Resource {
  protected:
    RubberAction();
    virtual ~RubberAction();

  public:
    virtual void execute(Rubberband*);
    virtual void help();
};

class OcHandler: public Handler {
  public:
    OcHandler();
    virtual ~OcHandler();
    virtual void help();
};

class Rubberband: public OcHandler {
  public:
    Rubberband(RubberAction* = NULL, Canvas* = NULL);
    virtual ~Rubberband();
    virtual bool event(Event&);
    Coord x_begin() const, y_begin() const, x() const, y() const;  // canvas coords
    static const Color* color();
    static const Brush* brush();
    void canvas(Canvas*);
    Canvas* canvas() const {
        return canvas_;
    }
    const Transformer& transformer() const {
        return t_;
    }
    const Event& event() const {
        return *e_;
    }
    virtual void help();
    virtual void snapshot(Printer*);
    static Rubberband* current() {
        return current_;
    }

  protected:
    // subclasses manipulate the rubberband glyph
    virtual void draw(Coord x, Coord y);
    virtual void undraw(Coord x, Coord y);

    virtual void press(Event&);
    virtual void drag(Event&);
    virtual void release(Event&);

    void rubber_on(Canvas*);
    void rubber_off(Canvas*);

  private:
    Canvas* canvas_;
    Transformer t_;
    Event* e_;
    RubberAction* ra_;
    Coord x_begin_, y_begin_, x_, y_;
    static const Color* xor_color_;
    static const Brush* brush_;
    static Rubberband* current_;
};

class RubberRect: public Rubberband {
  public:
    RubberRect(RubberAction* = NULL, Canvas* = NULL);
    virtual ~RubberRect();

    virtual void draw(Coord, Coord);

    virtual void get_rect(Coord& x1, Coord& y1, Coord& x2, Coord& y2) const;
    virtual void get_rect_canvas(Coord& x1, Coord& y1, Coord& x2, Coord& y2) const;
    virtual void help();
};

class RubberLine: public Rubberband {
  public:
    RubberLine(RubberAction* = NULL, Canvas* = NULL);
    virtual ~RubberLine();

    virtual void draw(Coord, Coord);

    virtual void get_line(Coord& x1, Coord& y1, Coord& x2, Coord& y2) const;
    virtual void get_line_canvas(Coord& x1, Coord& y1, Coord& x2, Coord& y2) const;
    virtual void help();
};

inline Coord Rubberband::x() const {
    return x_;
}
inline Coord Rubberband::y() const {
    return y_;
}
inline Coord Rubberband::x_begin() const {
    return x_begin_;
}
inline Coord Rubberband::y_begin() const {
    return y_begin_;
}

/*
 * RubberAction  denoted by an object and member function to call on the object.
 * Used the FieldEditorAction as a template
 */

#if defined(__STDC__) || defined(__ANSI_CPP__) || defined(WIN32) || MAC
#define __RubberCallback(T)       T##_RubberCallback
#define RubberCallback(T)         __RubberCallback(T)
#define __RubberMemberFunction(T) T##_RubberMemberFunction
#define RubberMemberFunction(T)   __RubberMemberFunction(T)
#else
#define __RubberCallback(T)       T /**/ _RubberCallback
#define RubberCallback(T)         __RubberCallback(T)
#define __RubberMemberFunction(T) T /**/ _RubberMemberFunction
#define RubberMemberFunction(T)   __RubberMemberFunction(T)
#endif

#define declareRubberCallback(T)                             \
    typedef void (T::*RubberMemberFunction(T))(Rubberband*); \
    class RubberCallback(T)                                  \
        : public RubberAction {                              \
      public:                                                \
        RubberCallback(T)(T*, RubberMemberFunction(T));      \
        virtual ~RubberCallback(T)();                        \
                                                             \
        virtual void execute(Rubberband*);                   \
                                                             \
      private:                                               \
        T* obj_;                                             \
        RubberMemberFunction(T) func_;                       \
    };

#define implementRubberCallback(T)                                                \
    RubberCallback(T)::RubberCallback(T)(T * obj, RubberMemberFunction(T) func) { \
        obj_ = obj;                                                               \
        func_ = func;                                                             \
    }                                                                             \
                                                                                  \
    RubberCallback(T)::~RubberCallback(T)() {}                                    \
                                                                                  \
    void RubberCallback(T)::execute(Rubberband* rb) {                             \
        (obj_->*func_)(rb);                                                       \
    }

#endif
