/*
* gravity.cc
* DIN Is Noise is copyright (c) 2006-2022 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/

#include "gravity.h"
#include "font.h"
#include "vector2d.h"
#include "drone.h"
#include "console.h"
#include "din.h"

#include <fstream>

using namespace std;

#define NUDGEUP(W) W.set_pos (W.extents.left, W.extents.bottom + 3);

gravity_t::gravity_t () {

  handlesize = 6;
  strength = 0;
  hitt = NOTHING;
  lmb_clicked = 0;
	mag0 = 50;
  mag = 0;
	keep_size = 0;
  widget* w [] = {&lbl, &fold, &zero, &left, &right, &up, &down, &base2manual, &base2drone, &tip2manual, &tip2drone, &selbase, &seltip, &selboth, &here};
  makefam (&lbl, &w[1], 14); 
  const char* txt [] = {
    "Gravity", "", "0", "", "", "", "",
    "Base to manual", "Base to drone", "Tip to manual", "Tip to drone", "Select base",  "Select tip", "Select both", "Reset"
  };
  for (int i = 0; i < 15; ++i) {
    label* lbl = dynamic_cast<label*>(w[i]);
    lbl->set_color (1.0f, 0.6f, 0.4f);
    lbl->set_text (txt[i]);
  }

  arrow_button* ab [] = {&left, &right, &up, &down};
  int dirr [] = {arrow_button::left, arrow_button::right, arrow_button::up, arrow_button::down};
  for (int i = 0, j = 1; i < 4; ++i, ++j) {
    arrow_button* abi = ab[i];
    abi->id = j;
    abi->set_dir (dirr[i]);
    abi->set_listener(this);
  }

  LISTEN(fold,this)
  LISTEN(zero,this)
  LISTEN(reset,this)

  LISTEN(base2manual, &b2ml)
  LISTEN(base2drone, &b2dl)
  LISTEN(tip2manual, &t2ml)
  LISTEN(tip2drone, &t2dl)
  LISTEN(selbase, &sell)
  LISTEN(seltip, &sell)
  LISTEN(selboth, &sell)

  LISTEN(here,&herel)

  base2manual.id = base2drone.id = selbase.id = 0;
  tip2manual.id = tip2drone.id = seltip.id = 1;
  selboth.id = 2;

  MOVE(lbl)

}

int gravity_t::handle_input () {

  HANDLEINPUT(fold)
  else if (unfolded()) {
    widget* w [] = {&lbl, &fold, &zero, &left, &right, &up, &down, &base2manual, &base2drone, &tip2manual, &tip2drone, &selbase, &seltip, &selboth, &here, 0};
    for (widget** p = w; *p != 0; ++p) if ((*p)->handle_input ()) break; 
  }
  return 1;
}

int gravity_t::handle_input2 () {

  HANDLEINPUT(lbl)
	if (is_lmb (this)) {
		if (lmb_clicked == 0) {
			lmb_clicked = 1;
			if (hitt != NOTHING) {
				hitt = NEXT_TO_NOTHING;
			} else {
				if (hit (tip, din0.win_mousex, din0.win_mousey)) {
					hitt = TIP; 
					is_lmb.tie = this;
				} else 
				if (hit (base, din0.win_mousex, din0.win_mousey)) {
					hitt = BASE; 
					is_lmb.tie = this;
				}
			}
		}
	} else {
		if (hitt == NOTHING) ;
    else 
		if (hitt == NEXT_TO_NOTHING) {
			stop_editing ();
		} else if (hitt == TIP) {
			set (tip, din0.win_mousex, din0.win_mousey);
      ldwx [1] = ldwy[1] = -1;
		} else if (hitt == BASE) {
			set (base, din0.win_mousex, din0.win_mousey);
      ldwx [0] = ldwy[0] = -1;
		} 
		lmb_clicked = 0;
	}
  return hitt;
}

void gravity_t::draw () {
  fold.draw ();
  lbl.draw ();
  if (unfolded()) {
    widget* w [] = {
      &lbl, &fold, &zero, &left, &right, &up, &down, &base2manual, &base2drone, &tip2manual, &tip2drone, &selbase, &seltip, &selboth, &here, 0
    };
    for (widget** p = w; *p != 0; ++p) (*p)->draw (); 
  }
}

void gravity_t::drawrrow () {
  glColor3f (1, 0.6, 0.4);
  glVertexPointer (2, GL_INT, 0, gl_base);
  glDrawArrays (GL_LINE_LOOP, 0, 4);
  glVertexPointer (2, GL_FLOAT, 0, gl_arrow);
  glDrawArrays (GL_LINES, 0, 6);
}

void gravity_t::calcbase2tip () {
  direction (base2tip, base, tip);
  perpendicular (p_base2tip, base2tip);
  gx = strength * base2tip.x; 
  gy = strength * base2tip.y;
}

void gravity_t::calcvisual () {
  bottomleft (base.x - handlesize, base.y - handlesize);
  topright (base.x + handlesize, base.y + handlesize);
  gl_base[0]=bottomleft.x; gl_base[1]=bottomleft.y;
  gl_base[2]=topright.x; gl_base[3]=gl_base[1];
  gl_base[4]=gl_base[2];gl_base[5]=topright.y;
  gl_base[6]=gl_base[0];gl_base[7]=gl_base[5];
  int cap = 0;
  int da = 0;
  make_arrow (gl_arrow, 0, cap, da, base.x, base.y, base2tip.x, base2tip.y, p_base2tip.x, p_base2tip.y, 0.6f, 0.2f);
}

void gravity_t::calc (int calc_mag) {
  calcbase2tip ();
  calcvisual ();
  if (calc_mag) mag = magnitude (base2tip);
}

void gravity_t::calcui () {

  win2view (base.x, base.y, textpos.x, textpos.y, din0.win, view);
  if (!view.inside (textpos.x, textpos.y)) {
    textpos.x = view.midx;
    textpos.y = view.midy;
  }

  static const int ds = 20, ds2 = ds / 2;

  textpos.x += ds;

  fold.set_pos (textpos.x, textpos.y - ds2);
  lbl.set_pos (fold.extents.right + ds2, fold.extents.bottom);

  int xx = textpos.x, yy = fold.extents.bottom - line_height;

  {
    widget* w [] = {&zero, &left, &right, &up, &down, &here, 0};
    for (widget** p = w; *p != 0; ++p) {
      widget* wi = *p;
      wi->set_pos (xx, yy);
      xx = wi->extents.right + ds;
    }
  }

  {
    widget* w[] = {&left, &right, &up, &down, 0};
    for (widget** p = w; *p != 0; ++p) {
      widget& wp = **p;
      NUDGEUP (wp);
    }
  }

  yy -= line_height;
  base2drone.set_pos (textpos.x, yy);
  base2manual.set_pos (base2drone.extents.right + ds, yy);
  tip2drone.set_pos (textpos.x, yy -= line_height);
  tip2manual.set_pos (tip2drone.extents.right + ds, yy);

  {
    xx = textpos.x;
    yy -= line_height;
    widget* w[] = {&selbase, &seltip, &selboth, 0};
    for (widget** p = w; *p != 0; ++p) {
      widget& wp = **p;
      wp.set_pos (xx, yy);
      xx = wp.extents.right + ds;
    }

  }

}

void gravity_t::set (point<float>& what, float mx, float my, int calc_mag) {
  what.x = mx;
  what.y = my;
  calc (calc_mag);
}

void gravity_t::move (float tox, float toy) {
  base.x = tox;
  base.y = toy;
  tip.x = tox + base2tip.x;
  tip.y = toy + base2tip.y;
  calcvisual ();
}

int gravity_t::hit (const point<float>& what, float mx, float my) {
  double m = magnitude (what.x, what.y, mx, my);
  return !(m > handlesize);
}

void gravity_t::load (ifstream& file) {
	string ignore;
  float bx, by, tx, ty; 
  file >> ignore >> bx >> by >> tx >> ty;
  set (base, bx, by);
  set (tip, tx, ty);
	file >> strength >> visible >> keep_size;
}

void gravity_t::save (ofstream& file) {
  file << "gravity " << base.x << spc << base.y << spc << tip.x << spc << tip.y << spc << strength << spc << visible << spc << keep_size << endl;
}

void gravity_t::doreset (int ks) {
	keep_size = ks;
  if (!keep_size) mag = mag0;
  preset (down.id);
}

void gravity_t::preset (int id) {
	float xx [] = {base.x, float (base.x - mag), float(base.x + mag), base.x, base.x};
  float yy [] = {base.y, base.y, base.y, float(base.y + mag), float(base.y - mag)};
  tip.x = xx [id]; 
  tip.y = yy [id];
  calc (0);
}

void gravity_t::track (int i, float vx, float vy) {
  point<float>* pta [] = {&base, &tip};
  point<float>& pt = *pta[i];
  set (pt, vx, vy);
}

int gravity_t::stop_editing () {
	if (hitt != NOTHING) {
		hitt = NOTHING;
		is_lmb.clear (this);
		return 1;
	}
	return 0;
}

void gravity_t::clicked (button& b) {
  if (&b == &fold) {
    if (fold.dir == arrow_button::right) {
      fold.set_dir(arrow_button::down);
    } else {
      fold.set_dir(arrow_button::right);
    }
  } else {
    if (&b == &reset) {
      doreset (keepsize.state);
    } else {
      preset (b.id);
    }
  }
}

int gravity_t::unfolded () {return fold.dir == arrow_button::down;}

CLICKED_BUTTON (gravity_t, drolis) {
  din& d = din0;
  gravity_t& g = d.dinfo.gravity;
  if (d.num_selected_drones) {
    g.tracked_drone[b.id] = d.selected_drones[0];
    g.ldwx[b.id] = g.ldwy[b.id] = -1;
  } else
    cons << RED_PSD << eol;
}

CLICKED_BUTTON (gravity_t, manlis) {
  gravity_t& g = din0.dinfo.gravity;
  g.tracked_drone[b.id] = 0;
  g.ldwx[b.id] = g.ldwy[b.id] = -1;
}

CLICKED_BUTTON (gravity_t, sellis) {
  din& d = din0;
  gravity_t& g = d.dinfo.gravity;
  if (SHIFT || CTRL) ; else d.clear_selected_drones ();
  if (b.id == 2) {
    drone* td0 = g.tracked_drone[0], *td1 = g.tracked_drone[1];
    if (td0 && td1) {
      d.add_drone_to_selection (td0);
      d.add_drone_to_selection (td1);
    }
  } else {
    drone* td = g.tracked_drone[b.id];
    if (td) d.add_drone_to_selection (td);
  }
  d.print_selected_drones ();
}

CLICKED_BUTTON (gravity_t, herelis) {
  din& d = din0;
  gravity_t& g = d.dinfo.gravity;
  int vx = g.fold.extents.left, vy = g.fold.extents.top; 
  float wx, wy;
  view2win (vx, vy, wx, wy, view, d.win);
  static const int h3 = 3 * g.handlesize;
  wx -= h3;
  g.move (wx, wy);
}
