#include <SDL/SDL.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <SDL/SDL_ttf.h>

extern Uint32 cWhite, cBlack, cGrey, cRed, cBlue,
              cForeground, cBackground, cBorder, cPointer, cScrollbar;
extern const SDL_Color def_text_col;

const Uint32
  TDIST=14;  // distance text lines

struct Int2 {
  int x,y;
  Int2(int x,int y);
  Int2();
  void set(int x,int y);
  bool operator!=(Int2 b);
};

struct Point {
  short x, y;
  Point();
  Point(short x,short y);
  void set(short x,short y);
  bool operator==(Point b);
  bool operator!=(Point b);
};

struct Color5 {
  Uint32 c[5];
  void set_color(Uint32 c0,Uint32 c1,Uint32 c2,Uint32 c3,Uint32 c4);
};

struct Rect:SDL_Rect {
  Rect();
  Rect(Sint16 x,Sint16 y,Uint16 dx,Uint16 dy);
  void set(Sint16 x,Sint16 y,Uint16 dx,Uint16 dy);
};

static Rect tmp_rect;

static Rect *rp(int x,int y,int w,int h) { // creates pointer to Rect. Each source file gets its own instance!
  tmp_rect.set(x,y,w,h);
  return &tmp_rect;
}

static Rect *rp() { return &tmp_rect; }

struct Id {
  int id1,id2;
  Id(int);
  Id(int,int);
};

struct Label {
  struct RenderText *render_t;
  void (*draw_cmd)(SDL_Surface *win,int nr,int y_off);
  const char *str;  // the text
  Label(const char* txt);
  Label(void (*draw_cmd)(SDL_Surface *win,int nr,int y_off));
  Label(const char *txt,void (*dr)(SDL_Surface *win,int nr,int y_off));
  void draw(SDL_Surface *win,int nr,Point pnt);
};

struct Style {
  const int st;
  Uint32 param;
  int param2;
  Style(int st);
  Style(int st,int par);
  Style(int st,int par,int par2);
};

struct RenderText {
  static const int dim=128-' ';
  SDL_Surface *chars[dim+1];
  int ch_wid[dim];
  TTF_Font *ttf_font;
  SDL_Color text_col;
  RenderText(TTF_Font*,SDL_Color text_col);
  int draw_string(SDL_Surface *win,const char *s,Point);
  int text_width(const char *s);
  void set_char(const int ind);
};

struct WinBase {
  SDL_Surface *win,   // parent window
              *title;
  WinBase *parent;
  WinBase **children;
  struct OnTopWin *ontopw;
  Rect area,    // relative to parent window
       tw_area, // relative to top window
       title_area; // title surface
  const Point title_top;
  int lst_child,end_child;
  Uint32 bgcol;
  bool hidden;
  Id id;
  WinBase(WinBase *pw,const char *t,int x,int y,int dx,int dy,Uint32 bgcol,Id id);
  ~WinBase();
  virtual void draw()=0;
  void reloc_title(int dx,int dy); // shift title area
  void add_child(WinBase* child);
  void remove_child(WinBase* child);
  void clear();
  void clear(Rect);
  void clear(Rect,Uint32 col,bool upd);
  void hide();
  void show();
  void draw_raised(Rect *rect,Uint32 col,bool up);
  void draw_gradient(Rect rect,Color5* col,bool vertical=false,bool hollow=false);
  void border(WinBase *child,int wid=1);
  WinBase *in_a_win(int x,int y);
  void draw_blit_recur();   // recursivily draw surface, blit own and children's surface plus title to parent
  void draw_blit_upd();     // draw surface, blit surface to parents
  void blit_upd(Rect *rect=0); // blit surface to parents
  void upd();               // update top window; if on-top window, no test for other on-top windows
  void widen(int dx,int dy); // widen win
  void move(int dx,int dy);  // move win
  bool move_if_ok(int delta_x,int delta_y); // guarded move
  Point tit_os();
  void keep_on_top();  // staying visible
};

struct TopWin:WinBase {
  void (*display_cmd)();
  TopWin(const char* title,Rect rect,Uint32 init_flag,Uint32 video_flag,void (*draw)(),void (*set_icon)()=0);
  void draw();
};

struct Button:WinBase {
  bool is_down;
  Style style;
  Label label;
  void (*cmd)(Id);
  Button(WinBase *pw,Style,Rect,Label lab,void (*cmd)(Id),Id id=0);
  void draw();
};

struct SubWin:WinBase {  // sub window
  void(*display_cmd)(Rect exp_rect,Id);
  void (*del_cmd)(Id);
  SubWin(const char* title,Rect rect,bool do_map,Uint32 bg,void(*disp_cmd)(Rect,Id),void(*del_cmd)(Id),Id id=0);
};

struct BgrWin:WinBase {  // background window
  void (*display_cmd)(BgrWin*);
  void (*down_cmd)(Id id,int x,int y,int but);
  void (*moved_cmd)(Id id,int x,int y,int but);
  void (*up_cmd)(Id id,int x,int y,int but);
  BgrWin(WinBase *pw,
         Rect,
         const char* title,
         void (*display_cmd)(BgrWin*),
         void (*down_cmd)(Id id,int,int,int),
         void (*moved_cmd)(Id id,int,int,int),
         void (*up_cmd)(Id id,int,int,int),
         Uint32 wcol,
         Id id=0);
  //void move_contents_h(int delta);  // move contents horizontal
  void draw();
};

// non-editable text window
struct TextWin:WinBase {
  static const int SMAX=200;    // string length
  int linenr,
      lmax;
  Style style;
  char (*textbuf)[SMAX];
  
  TextWin(WinBase *pw,Style,Rect rect,int lmax,const char* title,Id id=0);
  void draw();
  void add_text(const char*,bool do_draw);
  void reset();
};

// radio buttons
struct RButton {
  Label label;
  void (*cmd)(Id id,int nr,int fire);
  RButton();
  RButton(int nr,Label lab);
};

struct RButWinData {
  int butnr,
      rb_max;
  RButton *but,
          *act_button;
  RButWinData();
  int next();
};

struct RButWin:WinBase {
  int y_off;
  const int b_dist; // button distance
  bool maybe_z;  // unselect with second click?
  void (*rb_cmd)(Id id,int nr,int fire);
  RButWinData def_buttons, // default buttons
              *d;
  Style style;
  RButWin(WinBase *parent,Style,Rect,const char *title,bool mbz,void(*cmd)(Id id,int nr,int fire),Id id=0);
  ~RButWin();
  void draw_rbutton(RButton *rb);
  void draw();
  void set_rbut(RButton *rb,int fire,bool do_draw=true); // if fire, then cmd is called
  void set_rbutnr(int nr,int fire,bool do_draw=true);
  int act_rbutnr();
  void reset();   // reset and clear
  RButton *add_rbut(Label lab);
  RButton *is_in_rbutton(SDL_MouseButtonEvent *ev);
};

// extern radio button
struct RExtButton:WinBase {
  struct ExtRButCtrl *rxb_ctr;
  Style style;
  Label label;
  RExtButton(WinBase *pw,Style,Rect,Label,Id id=0);
  void draw();
};

struct ExtRButCtrl {
  int butnr;
  bool maybe_z;  // can all buttons be unselected?
  Style style;
  RExtButton *act_lbut;
  void (*reb_cmd)(Id id,bool is_act);
  ExtRButCtrl(Style,void (*cmd)(Id id,bool is_act));
  int next();
  void set_rbut(RExtButton*,int fire);
  void reset();
  RExtButton *add_extrbut(WinBase *pw,Rect,Label lab,Id id);
};

// horizontal slider
struct HSlider:WinBase {
  int sdx,
      minv,maxv;
  int def_data,  // default buffer for data
      *d;
  const char *lab_left,
             *lab_right;
  char *text;
  Style style;
  void (*cmd)(HSlider*,int fire,bool rel);
  HSlider(WinBase *parent,Style,Rect rect,int minval,int maxval,const char* t,
          void (*cmd)(HSlider*,int fire,bool rel),Id id=0);
  int &value();
  void draw_sliderbar(int x);
  void calc_hslval(int x);
  void draw();
  void set_hsval(int val,int fire,bool do_draw=true);
};

// vertical slider
struct VSlider:WinBase {
  int sdy,
      minv,maxv;
  int def_data,  // default buffer for data
      *d;
  const char *lab_top,
             *lab_bottom;
  char *text;
  Style style;
  void (*cmd)(VSlider*,int fire,bool rel);
  VSlider(WinBase *parent,Style,Rect rect,int minval,int maxval,const char* t,
          void (*cmd)(VSlider*,int fire,bool rel),Id id=0);
  int &value();
  void draw_sliderbar(int x);
  void calc_vslval(int x);
  void draw();
  void set_vsval(int val,int fire,bool do_draw=true);
};

// 2-dimensional slider
struct HVSlider:WinBase {
  int x_inset,  // distance active area -> dy
      sdx, sdy; // active area
  Int2 minv,maxv,
       def_data,  // default buffer for data
       *d;
  const char *lab_left,
             *lab_right,
             *lab_top,
             *lab_bottom;
  char *text_x,*text_y;
  Style style;
  void (*cmd)(HVSlider*,int fire,bool rel);
  HVSlider(WinBase *pw,Style,Rect rect,int x_ins,Int2 minval,Int2 maxval,const char* t,
           void (*cmd)(HVSlider*,int fire,bool rel),Id id=0);
  void draw_2dim_slider(Int2 i2);
  void calc_hvslval(Int2 i2);
  Int2& value();
  void draw();
  void set_hvsval(Int2,int fire,bool do_draw=true);
};

// check box
struct CheckBox:WinBase {
  bool def_val,
       *d;
  Label label;
  Style style;
  void (*cmd)(CheckBox*);
  CheckBox(WinBase *pw,Style,Rect,Label lab,void (*cmd)(CheckBox*),Id id=0);
  bool &value();
  void draw();
  void set_cbval(bool,int fire,bool do_draw=true);
};

// horizontal scrollbar
struct HScrollbar:WinBase {
  int range, p0, xpos, wid,
      value;
  Style style;
  const int ssdim; // soft scroll area
  void (*cmd)(Id id,int val,int range);
  HScrollbar(WinBase *pw,Style,Rect,int r,void (*cmd)(Id,int,int),Id id=0);
  void calc_params(int r);
  void draw();
  void set_range(int range);
  void calc_xpos(int newx);
  void set_xpos(int newx);
  void inc_value(bool incr);
  bool in_ss_area(SDL_MouseButtonEvent *ev,bool *dir);
};

// vertical scrollbar
struct VScrollbar:WinBase {
  int range, p0, ypos, height,
      value;
  Style style;
  const int ssdim; // soft scroll area
  void (*cmd)(Id id,int val,int range);
  VScrollbar(WinBase *pw,Style,Rect,int r,void (*cmd)(Id,int,int),Id id=0);
  void calc_params(int r);
  void draw();
  void set_range(int);
  void calc_ypos(int newy);
  void set_ypos(int newy);
  void inc_value(bool incr);
  bool in_ss_area(SDL_MouseButtonEvent *ev,bool *dir);
};

// editable text window
struct EditWin:WinBase {
  int linenr,
      lmax,
      y_off;
  bool editable;
  struct Line **lines;
  void(*cmd)(Id,int,int);
  struct {
    int x,y;
  } cursor;
  EditWin(int pw,Rect,bool edble,void(*cmd)(Id,int ctrl_key,int key),Id id=0);
  void print_line(int vpos);
  void set_y_off(int yoff);
  void set_cursor(int x,int y);
  void unset_cursor();
  void read_file(FILE* in);
  void set_line(char *s,int n);
  void insert_line(const char *s,int n);
  void write_file(FILE *out);
  char *get_text(char *buf,int maxlen);
  void reset();
  void get_info(int* nr_of_lines,int* cursor_ypos,int *nr_chars);
};

struct DialogWin:WinBase {
  Rect textr;  // text area
  int cursor,
      cmd_id;
  const char *label;
  struct Line *lin;
  void (*cmd)(const char* text,int cmd_id);
  DialogWin(WinBase *pw,Rect,Id _id=0);
  void print_line();
  void set_cursor(int pos);
  void unset_cursor();
  void draw();
  void dialog_label(const char *s,Uint32 col=0);
  void dialog_def(const char *str,void(*cmd)(const char* text,int cmdid),int cmd_id);
  bool handle_key(SDL_keysym *key,bool down);
  void dok();
};

struct Lamp {
  WinBase *pwin;
  Rect rect;
  Uint32 col;
  bool hidden;
  Lamp(WinBase *pw,Rect);
  void draw();
  void set_color(Uint32 col);
  void show();
};

struct Message {
  WinBase *pwin;
  Style style;
  const char *label;
  Point lab_pt; // label Point
  Rect mes_r;   // message Rect
  Uint32 bgcol; // background color for message
  Message(WinBase *pw,Style,const char* lab,Point top);
  void draw_label(bool upd=false);
  void draw_mes(const char *form,...);
  void draw(const char *form,...);
private:
  void draw_message(const char *form,va_list ap);
};

struct CmdMenu {
  Rect mrect;
  int nr_buttons;
  Button *src;
  RButWin *buttons;
  SDL_Surface *backup;
  CmdMenu(Button *src);
  bool init(int wid,int nr_buttons,void (*menu_cmd)(Id id,int nr,int fire));
  void close();
  void add_mbut(Label lab);
};

extern bool sdl_running;
extern RenderText *draw_ttf,
                  *draw_title_ttf,
                  *draw_mono_ttf;
extern Point alert_position;
extern const char *def_fontpath;

void send_uev(int cmd,int param1=0,int param2=0);
extern void (*handle_uev)(int cmd,int param,int param2); // user event
extern void (*handle_kev)(SDL_keysym *key,bool down);    // keyboard event
extern void (*handle_rsev)(int dw,int dh);               // resize event
void set_text(char *&txt,const char *form,...);
void say(const char *form,...);
void err(const char *form,...);
void alert(const char *form,...);
void get_events();
Uint32 calc_color(Uint32 c);
SDL_Surface *create_pixmap(const char* pm_data[]);
SDL_Cursor *init_system_cursor(const char *image[]);
void update(Rect *rect,WinBase *parent=0); // keeps alert window on top
