#define BALLS_NUM       7
#define SPRINGS_NUM     3

#define STRETCH         500.0
#define GRAVITY         50.0 //not really gravity
#define BALL_RADIUS     5
#define BASE_SIZE       10

CMass balls[BALLS_NUM];
CSpring springs[SPRINGS_NUM];
F64 collision_t;

U0 DrawIt(CTask *task,CDC *dc)
{
  I64 i,
        cx=task->pix_width>>1,
        cy=task->pix_height>>1;
  Bool snd_on=FALSE;
  dc->color=BLACK;
  GrPrint(dc,0,0,"Protect your base.");
  GrRect(dc,cx-BASE_SIZE,cy-BASE_SIZE,BASE_SIZE*2,BASE_SIZE*2);
  dc->color=CYAN;
  GrRect(dc,cx-BASE_SIZE+2,cy-BASE_SIZE+2,BASE_SIZE*2-4,BASE_SIZE*2-4);
  dc->color=YELLOW;
  GrLine(dc,balls[0].x,balls[0].y,
        ms.pos.x-task->pix_left-task->scroll_x,
        ms.pos.y-task->pix_top-task->scroll_y);
  for (i=0;i<SPRINGS_NUM;i++)
    GrLine(dc,springs[i].end1->x,springs[i].end1->y,
          springs[i].end2->x,springs[i].end2->y);

  dc->color=LTCYAN;
  GrCircle(dc,balls[0].x,balls[0].y,BALL_RADIUS);
  GrFloodFill(dc,balls[0].x,balls[0].y,TRUE);
  dc->color=BLACK;
  GrCircle(dc,balls[0].x,balls[0].y,BALL_RADIUS);

  for (i=1;i<BALLS_NUM;i++) {
    dc->color=LTPURPLE;
    GrCircle(dc,balls[i].x,balls[i].y,BALL_RADIUS);
    GrFloodFill(dc,balls[i].x,balls[i].y,TRUE);
    if (cx-BASE_SIZE-BALL_RADIUS<=balls[i].x<=cx+BASE_SIZE+BALL_RADIUS &&
          cy-BASE_SIZE-BALL_RADIUS<=balls[i].y<=cy+BASE_SIZE+BALL_RADIUS)
      snd_on=TRUE;
    dc->color=BLACK;
    GrCircle(dc,balls[i].x,balls[i].y,BALL_RADIUS);
  }
  if (snd_on)
    Snd(74);
  else
    Snd;
}

U0 MyDerivative(CMathODE *ode,F64 t,COrder2D3 *,COrder2D3 *)
{
  I64 i,j;
  F64 d,dd;
  CD3 p,p2;
  CTask *task=ode->win_task;

  D3SubEqu(D3Equ(&p2,
        ms.pos.x-task->pix_left-task->scroll_x,
        ms.pos.y-task->pix_top-task->scroll_y,0),
        &balls[0].state->x);
  D3AddEqu(&balls[0].DstateDt->DxDt,D3MulEqu(&p2,STRETCH));

  D3Equ(&p2,task->pix_width>>1,task->pix_height>>1,0);
  for (i=1;i<BALLS_NUM;i++) {
    D3Sub(&p,&p2,&balls[i].state->x);
    if (d=D3Norm(&p)) {
//Gravity would be /(d*d*d), but that's too exponential.
      D3MulEqu(&p,GRAVITY/d);
      D3AddEqu(&balls[i].DstateDt->DxDt,&p);
    }
  }

  for (i=0;i<BALLS_NUM;i++)
    for (j=i+1;j<BALLS_NUM;j++) {
      D3Sub(&p,&balls[j].state->x,&balls[i].state->x);
      dd=D3NormSqr(&p);
      if (dd<=(2*BALL_RADIUS)*(2*BALL_RADIUS)) {
        if (t-collision_t>0.05) {
          Noise(50,102,105);
          collision_t=t;
        }
        d=Sqrt(dd)+0.0001;
        dd=10.0*Sqr(Sqr((2*BALL_RADIUS)*(2*BALL_RADIUS)-dd));
        D3MulEqu(&p,dd/d);
        D3AddEqu(&balls[j].DstateDt->DxDt,&p);
        D3SubEqu(&balls[i].DstateDt->DxDt,&p);
      }
    }

  d=balls[0].state->x;
  if (d-BALL_RADIUS<0)
    balls[0].DstateDt->DxDt+=Sqr(Sqr(Sqr(d-BALL_RADIUS)));
  if (d+BALL_RADIUS>task->pix_width)
    balls[0].DstateDt->DxDt-=Sqr(Sqr(Sqr((d+BALL_RADIUS)-task->pix_width)));

  d=balls[0].state->y;
  if (d-BALL_RADIUS<0)
    balls[0].DstateDt->DyDt+=Sqr(Sqr(Sqr(d-BALL_RADIUS)));
  if (d+BALL_RADIUS>task->pix_height)
    balls[0].DstateDt->DyDt-=Sqr(Sqr(Sqr((d+BALL_RADIUS)-task->pix_height)));
}

U0 Whap()
{
  I64 i;
  CMathODE *ode=ODENew(0,1e-2,ODEF_HAS_MASSES);

  SettingsPush; //See SettingsPush
  AutoComplete;
  WinBorder;
  WinMax;

  MenuPush(
        "File {"
        "  Abort(,CH_SHIFT_ESC);"
        "  Exit(,CH_ESC);"
        "}"
        );
  ode->derive=&MyDerivative;
  ode->drag_v2=0.002;
  ode->drag_v3=0.00001;
  ode->acceleration_limit=5e3;
  MemSet(balls,0,BALLS_NUM*sizeof(CMass));
  D3Equ(&balls[0].x,100,100,0);
  for (i=1;i<BALLS_NUM;i++)
    D3Equ(&balls[i].x,
          RandI16%500+Fs->pix_width>>1,RandI16%500+Fs->pix_height>>1,0);
  balls[0].x=ms.pos.x-Fs->pix_left-Fs->scroll_x;
  balls[0].y=ms.pos.y-Fs->pix_top-Fs->scroll_y;
  for (i=0;i<BALLS_NUM;i++) {
    balls[i].mass=1.0;
    balls[i].drag_profile_factor=1.0;
    QueIns(&balls[i],ode->last_mass);
  }
  balls[2].x=balls[1].x+15;
  balls[2].y=balls[1].y;
  balls[3].x=balls[1].x;
  balls[3].y=balls[1].y+15;
  MemSet(springs,0,SPRINGS_NUM*sizeof(CSpring));
  springs[0].end1=&balls[1];
  springs[0].end2=&balls[2];
  springs[0].rest_len=15;
  springs[0].const=10000;
  QueIns(&springs[0],ode->last_spring);
  springs[1].end1=&balls[1];
  springs[1].end2=&balls[3];
  springs[1].rest_len=15;
  springs[1].const=10000;
  QueIns(&springs[1],ode->last_spring);
  springs[2].end1=&balls[2];
  springs[2].end2=&balls[3];
  springs[2].rest_len=sqrt2*15;
  springs[2].const=10000;
  QueIns(&springs[2],ode->last_spring);

  collision_t=0;
  QueIns(ode,Fs->last_ode);

  DocCursor;
  DocClear;
  Fs->draw_it=&DrawIt;
  GetChar;
  SettingsPop;
  QueRem(ode);
  ODEDel(ode);
  MenuPop;
}

Whap;