// wave viewer widget using opengl

#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <pthread.h>


#include "mrpc.h"   // multires peak cache object
#include "debug.h"  // debugging macros
#include "macros.h" // other util macros
#include "widget.h" 

/* widget base class */
void widget::draw(){}
void widget::event(event_t *e){}
void widget::resize(int x, int y, int width, int height){
  this->frame_x = x;
  this->frame_y = y;
  this->frame_width = width;
  this->frame_height = height;
}
void widget::realestate(void){
    /* setup dims */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 1, 0, 1);
    glViewport(this->frame_x, this->frame_y, this->frame_width, this->frame_height);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity(); // so widget can rescale at libidum
}
widget::widget(){
  this->frame_x = 0;
  this->frame_y = 0;
  this->frame_width = 0;
  this->frame_height = 0;
  this->receiver = NULL;
  this->next = NULL;
}
widget::~widget(){}


/* container widget */
container::container() {
  widget::widget();
  this->_first = NULL;
}
container::~container(){widget::widget(); this->_first = 0;}
void container::event(event_t *e){}
void container::resize(int x, int y, int width, int height){}

void container::add(class widget *w){
    w->next = this->_first;
    this->_first = w;
    this->update();
}

class widget *container::index(int i){
  class widget *w = this->_first;
  if (i < 0) goto error;
  while (w) {
    if (i-- == 0) return w;
    w = w->next;
  }
 error: return 0;
}
void container::remove(class widget *deadman){
  class widget **w = (&this->_first);
  while (*w){
    if ((*w) == deadman){*w = (*w)->next;} // snap atom
    else {w = &(*w)->next;} // or move ptr to next
  }
  this->update();
}
int container::nb_widgets(void){
  int i, nb_widgets = 0;
  class widget *w = this->_first;
  for( ; w ; w=w->next , nb_widgets++ );
  return nb_widgets;
}
void container::update(void){
  this->resize(this->frame_x, this->frame_y, this->frame_width, this->frame_height);
}

void container::draw(void){
  class widget *w = this->_first;
  for (;w;w=w->next) w->draw();
}

/* packing widget */
pack::pack() {
  container::container();
  this->drag = -1;
  this->orientation = 0;
}
pack::~pack(){ }



void pack::resize(int x, int y, int width, int height){
  widget::resize (x,y,width,height);

  int i, nb_widgets = this->nb_widgets();
  if (!nb_widgets) return; // nothing to resize

  int x_off=0, y_off=0, x_inc=0, y_inc=0, x_size=width, y_size=height;


  // hor/ver
  if (this->orientation){
    x_inc = width / nb_widgets;
    x_size = x_inc;
  }
  else {
    y_inc = height / nb_widgets;
    y_size = y_inc;
  }
    

  class widget *w = this->index(0);

  ASSERT(w);

  while (w->next){ // slice first

    w->resize(x + x_off, y + y_off, x_size, y_size);
    y_off += y_inc;
    x_off += x_inc;
    w = w->next;
  }
  w->resize(x + x_off, y + y_off, width - x_off, height - y_off); 
  // last consumes rest (incl.roundoffs)
}


void pack::event(event_t *e){
  int nb_widgets = this->nb_widgets();
  if (!nb_widgets) return; // nothing to send to

  /* pack events */
  if ((e->type == KEY_PRESS) && (e->e.keyboard.key == 'o')){
    this->orientation ^= 1;
    this->update();
    return;
  }
  

  int index = 0;
  float fraction = (this->orientation) ? e->e.mouse.x : e->e.mouse.y;


  if (this->drag < 0){ // no dragger, do manual routing
    index = (int)(((float)nb_widgets) * fraction);
  }
  else { // keep dragger target
    index = this->drag;
  }

  //fprintf(stderr, "target widget = %d\n", index);

  // update dragger on press/release (scroll wheel does not end drag)
  if ((e->type == RELEASE) && (e->e.mouse.button <= 3)) this->drag = -1;
  if (e->type == PRESS) this->drag = index;

  // transform coords
  float shift = (((float)index) / ((float)(nb_widgets)));
  float scale = (float)(nb_widgets);
  if (this->orientation){
    e->e.mouse.x -= shift;
    e->e.mouse.x *= scale;
  }
  else {
    e->e.mouse.y -= shift;
    e->e.mouse.y *= scale;
  }

  // send event
  class widget *w = this->index(index);
  if (!w) fprintf(stderr, "no widget %d\n", index);
  else w->event(e);

}




