/*
 * Rubik's clock.
 *
 * This file is public domain, I just hate copy[left/right]s.
 *-------------
 * the solver stuff
 */

#include "solver.h"

#include <sys/time.h>
#include <unistd.h>
#include <math.h>
#include <setjmp.h>

#include "clock.h"
#include "screen.h"
#include "event.h"

/* the only global var (yes, it's static, but it's the only var declared outside a function, that's what I call global) */
static jmp_buf env;

typedef struct {
  float a1, b1, c1, d1;
  float a2, b2, c2, d2;
  float x, y;
} BEZIER;

void init_bezier(BEZIER *b, float x1, float y1, float x2, float y2)
{
  float a=myrand(360);
  float cosa=cos(M_PI*2*a/360);
  float sina=sin(M_PI*2*a/360);
  float bi=myrand(360);
  float cosb=cos(M_PI*2*bi/360);
  float sinb=sin(M_PI*2*bi/360);
  float dx1 = 300*cosa;
  float dy1 = 300*sina;
  float dx2 = 300*cosb;
  float dy2 = 300*sinb;

  /*
    the formulas for Bezier stuff :

    x(t) = a1 t3 + b1 t2 + c1 t + d1
    y(t) = a2 t3 + b2 t2 + c2 t + d2

    x'(t) = 3 a1 t2 + 2 b1 t + c1
    y'(t) = 3 a2 t2 + 2 b2 t + c2

    x(0) = d1 = x1
    x(1) = a1 + b1 + c1 + d1 = x2
    x'(0) = c1 = dx1
    x'(1) = 3 a1 + 2 b1 + c1 = -dx2

    a1 - c1 - 2 d1 = -dx2 - 2 x2
    a1 = dx1 + 2 x1 - dx2 - 2 x2

    b1 = x2 - a1 - c1 - d1
   */
  b->a1 = dx1 + 2 * x1 - dx2 - 2 * x2;
  b->c1 = dx1;
  b->d1 = x1;
  b->b1 = x2 - b->a1 - b->c1 - b->d1;

  b->a2 = dy1 + 2 * y1 - dy2 - 2 * y2;
  b->c2 = dy1;
  b->d2 = y1;
  b->b2 = y2 - b->a2 - b->c2 - b->d2;
}

void calc_bezier(BEZIER *b, float i)
{
  /* we calc x(t), y(t) for t=i */
  b->x = b->a1 * i * i * i + b->b1 * i * i + b->c1 * i + b->d1;
  b->y = b->a2 * i * i * i + b->b2 * i * i + b->c2 * i + b->d2;
}

static void action_wait(CLOCK *c, SCREEN *s, unsigned long tout)
{
  EVENT local_event;
  struct timeval tv;
  unsigned long tinit, tafter;

  local_event.timer=tout;

  while (1) {
    /* watch the timer handling, because one could go out before time over */
    /* with this design, it's extremly very easy to add sound. */
    gettimeofday(&tv, 0);
    tinit=tv.tv_sec*1000+tv.tv_usec/1000;

    wait_next_event(&local_event, s);
    handle_event(&local_event, c, s);
    if (c->mode != SOLVER)
      /* coooool ! i've got my longjmp ! crazy ! */
      longjmp(env, 1);

    gettimeofday(&tv, 0);
    tafter=tv.tv_sec*1000+tv.tv_usec/1000;

    if (tafter>=tinit+local_event.timer)
      return;

    local_event.timer-=tafter-tinit; 
  }
}

/* have to take it out, because their initialisation is not what I first though it was, too
 * bad for the local static stuff...
 */
/* two other globals, bad, bad, bad... I hate globals */
static int prevx=0, prevy=0;

void action(CLOCK *c, SCREEN *s, EVENT *ev)
{
  BEZIER b;
#if 0
  /* a C program without static local vars is not a real C program.
   * If you don't know how static local vars behave, you won't understand this function,
   * so take a C book and learn (or send or mail to me, sed@free.fr).
   */
  /* yes, but here it's not good at all, so let me remove this code */
  static int prevx=0, prevy=0;
#endif
  int newx, newy;
  float i;

  /* a new flag, the generator (so the user won't be able to interact with the solver) */
  ev->generator=MACHINE;

  /* first, move the mouse to the right place */
  get_mouse_position(s, ev, &newx, &newy);

  if (newx==prevx && newy==prevy)
    goto not_move; /* I love this ! :) */

  init_bezier(&b, prevx, prevy, newx, newy);
  for (i=0; i<=1; i+=0.02) {
    calc_bezier(&b, i);
    draw_mouse(c, s, b.x, b.y);
    set_mouse_position(s, b.x, b.y);
    action_wait(c, s, 20);
  }

  /* here is all the magic of local static vars */
  prevx=newx;
  prevy=newy;

not_move: /* Really, gotos kick ass ! */
  /* second, handle the event */
  handle_event(ev, c, s);
  //XFlush(s->d);
  action_wait(c, s, 200);
  //usleep(200*1000);
}

void set_side(CLOCK *c, SCREEN *s, int side)
{
  EVENT ev;

  if (side==c->current_face)
    return;

  ev.type=TURN;

  action(c, s, &ev);
}

void set_button(CLOCK *c, SCREEN *s, int button, int val)
{
  EVENT ev;

  if (val==c->buttons[c->current_face][button])
    return;

  ev.type=BUTTON;
  ev.which_button=button;

  action(c, s, &ev);
}

/* return 1 if clocks != 0 if == */
int dif_clock(CLOCK *c, int c1x, int c1y, int c2x, int c2y)
{
  if (c->face[c->current_face][c1x][c1y] != c->face[c->current_face][c2x][c2y])
    return 1;

  return 0;
}

/* return 1 if clock not right val 0 if yes */
int dif_abs_clock(CLOCK *c, int c1x, int c1y, int val)
{
  if (c->face[c->current_face][c1x][c1y] != val)
    return 1;

  return 0;
}

void set_clock(CLOCK *c, SCREEN *s, int cx, int cy, int tox, int toy, int button)
{
#define current_hour	c->face[c->current_face][cx][cy]
#define wanted_hour	c->face[c->current_face][tox][toy]
  EVENT ev;
  int corn[]={4, 6, 2, 0};

  ev.type=CORNER;
  ev.which_corner=corn[button];

  /* if we differ from more than 6 hour in this direction, take the other one */
  if ((current_hour - wanted_hour+12)%12 > 6)
    ev.which_corner++;

  while (current_hour != wanted_hour)
    action(c, s, &ev);
}

void set_abs_clock(CLOCK *c, SCREEN *s, int cx, int cy, int val, int button)
{
#undef current_hour
#undef wanted_hour
#define current_hour	c->face[c->current_face][cx][cy]
#define wanted_hour	val
  EVENT ev;
  int corn[]={4, 6, 2, 0};

  ev.type=CORNER;
  ev.which_corner=corn[button];

  /* if we differ from more than 6 hour in this direction, take the other one */
  if ((current_hour - wanted_hour+12)%12 > 6)
    ev.which_corner++;

  while (current_hour != wanted_hour)
    action(c, s, &ev);
}

/* all is there, very simple to understand, nope ?
 * set_side will choose the side (0 or 1)
 * set_button will put a button in the wanted state.
 *    buttons are numbered like this :
 *        0  1
 *        2  3
 *    values are 0 for down and 1 for up
 * set_clock will give to a clock the value of another one, by turning the given corner clock.
 *    so args (2, 3) gives the position of the clock we want to set the value
 *    args (4, 5) gives the position to the clock which value we want the above one to get
 *    arg 6 gives the corner number.
 *    corner clocks are numbered like buttons.
 *    clocks are like this :
 *    (0, 0)   (1, 0)  (2, 0)
 *
 *    (0, 1)   (1, 1)  (2, 1)
 *
 *    (0, 2)   (1, 2)  (2, 2)
 *    where the first number is arg 2 (or 4) and the second is arg 3 (or 5)
 *    (All the strategy is to set the buttons and choose the correct corner to turn for
 *    when I set a clock to a value, it finally set all the clocks to the wanted result (and by
 *    the way you can see lotsa (1,1) for args (2,3), but in fact I set other clocks at the same
 *    time, so maybe I should call set_clock to another name ?) hum... I think you won't understand
 *    the strategy by simply reading this code, that's all what this comment means, but am I right ?
 *    So, well, that is why I explain by comments what I do in the code.)
 * set_abs_clock is like set_clock except we don't put the value relatively to another clock,
 *    but an absolute one.
 */
void do_solve(CLOCK *c, SCREEN *s)
{
  /* ok, let's go to side 1 */
  set_side(c, s, 0);

  /* first step, set clock (1, 1) to value of (1, 2) if not already done */
  if (dif_clock(c, 1, 1, 1, 2)) {
    set_button(c, s, 0, 1);
    set_button(c, s, 1, 0);
    set_button(c, s, 2, 0);
    set_button(c, s, 3, 0);
    set_clock(c, s, 1, 1, 1, 2, 0);
  }

  /* ok, now, set those two clocks to the value of (2, 1) */
  if (dif_clock(c, 1, 1, 2, 1)) {
    set_button(c, s, 1, 0);
    set_button(c, s, 3, 0);
    set_button(c, s, 2, 1);
    set_clock(c, s, 1, 1, 2, 1, 2);
  }

  /* we have 3 clocks at same value, set them now (together, by selecting the buttons in a state
   * that allows that and turning the correct corner clock) to the value of (0, 1) */
  if (dif_clock(c, 1, 1, 0, 1)) {
    set_button(c, s, 0, 0);
    set_button(c, s, 2, 0);
    set_button(c, s, 3, 1);
    set_clock(c, s, 1, 1, 0, 1, 3);
  }

  /* we have 4 clocks, set them to the value of (1, 0) */
  if (dif_clock(c, 1, 1, 1, 0)) {
    set_button(c, s, 0, 0);
    set_button(c, s, 1, 0);
    set_button(c, s, 2, 1);
    set_button(c, s, 3, 1);
    set_clock(c, s, 1, 1, 1, 0, 2);
  }

  /* now, because corner clocks will be set by the other side (the corresponding corner clocks of both sides change
   * whatever button are pushed or not, so we will handle them to the other side), let's put our five not corner
   * clocks to 0 (or 12, depending how you read your clock)
   */
  if (dif_abs_clock(c, 1, 1, 0)) {
    set_button(c, s, 0, 1);
    set_button(c, s, 1, 1);
    set_button(c, s, 2, 1);
    set_button(c, s, 3, 1);
    set_abs_clock(c, s, 1, 1, 0, 1);
  }

  /* now, side 2 */
  set_side(c, s, 1);

  /* ok, let's do exaclty the same as for side 1 (and because we turn a corner only if button is up,
   * we will not change other side not corner clocks) */
  if (dif_clock(c, 1, 1, 1, 2)) {
    set_button(c, s, 0, 1);
    set_button(c, s, 1, 0);
    set_button(c, s, 2, 0);
    set_button(c, s, 3, 0);
    set_clock(c, s, 1, 1, 1, 2, 0);
  }

  if (dif_clock(c, 1, 1, 2, 1)) {
    set_button(c, s, 1, 0);
    set_button(c, s, 3, 0);
    set_button(c, s, 2, 1);
    set_clock(c, s, 1, 1, 2, 1, 2);
  }

  if (dif_clock(c, 1, 1, 0, 1)) {
    set_button(c, s, 0, 0);
    set_button(c, s, 2, 0);
    set_button(c, s, 3, 1);
    set_clock(c, s, 1, 1, 0, 1, 3);
  }

  if (dif_clock(c, 1, 1, 1, 0)) {
    set_button(c, s, 0, 0);
    set_button(c, s, 1, 0);
    set_button(c, s, 2, 1);
    set_button(c, s, 3, 1);
    set_clock(c, s, 1, 1, 1, 0, 2);
  }

  /* ok, so now, if we did as for side 1, the corner clocks would not be at 0, so let's handle them */
  /* what has to be done is to let each corner clock to the same value of not corner clocks.
   * We could just set them with button down, but it will change the value of other side's not corner clocks,
   * which we don't want.
   * So the principle is to let the other clocks come to the value of a corner clock (we turn all the
   * clocks except the corner one), and then do the same with the next corner clock, and that two more
   * times, so all the clocks are to the same value, then put all this to 0.
   */
  if (dif_clock(c, 1, 1, 0, 0)) {
    set_button(c, s, 0, 0);
    set_button(c, s, 1, 1);
    set_button(c, s, 2, 1);
    set_button(c, s, 3, 1);
    set_clock(c, s, 1, 1, 0, 0, 1);
  }

  if (dif_clock(c, 1, 1, 2, 0)) {
    set_button(c, s, 0, 1);
    set_button(c, s, 1, 0);
    set_button(c, s, 2, 1);
    set_button(c, s, 3, 1);
    set_clock(c, s, 1, 1, 2, 0, 3);
  }

  if (dif_clock(c, 1, 1, 2, 2)) {
    set_button(c, s, 0, 1);
    set_button(c, s, 1, 1);
    set_button(c, s, 2, 1);
    set_button(c, s, 3, 0);
    set_clock(c, s, 1, 1, 2, 2, 2);
  }

  if (dif_clock(c, 1, 1, 0, 2)) {
    set_button(c, s, 0, 1);
    set_button(c, s, 1, 1);
    set_button(c, s, 2, 0);
    set_button(c, s, 3, 1);
    set_clock(c, s, 1, 1, 0, 2, 0);
  }

  if (dif_abs_clock(c, 1, 1, 0)) {
    set_button(c, s, 0, 1);
    set_button(c, s, 1, 1);
    set_button(c, s, 2, 1);
    set_button(c, s, 3, 1);
    set_abs_clock(c, s, 1, 1, 0, 1);
  }

  /* ok, it's done. Hope my comments are clear enough for you to understand. Now you should be
   * able to solve it fine.
   * A one more step would be to have a strategy that deals with less moves than this one.
   * I'm sure we can make better. Does anyone has a solution ?
   * One more question would be to know if a computer program can create its own strategies to
   * solve this program, but this is AI and well, I'm not a specialist of this field of computer
   * science. If some of you are, and have some time, maybe they could try to do something ? or
   * explain me some learning technics so I could try to code something, it would be great and fun.
   * Enjoy.
   */
}

#if 0
/* this version is not incorrect, it just do too much of set_button */
void do_solve(CLOCK *c, SCREEN *s)
{
  /* solving side 1 */
  set_side(c, s, 0);

  /* for set_button : 1 = up, 0 = down */
  /* as a step 0, let's be sure button 1, 2 and 3 are down */
  set_button(c, s, 1, 0);
  set_button(c, s, 2, 0);
  set_button(c, s, 3, 0);
  /* first step, set clock (1, 1) to value of (1, 2) */
  set_button(c, s, 0, 1);
  set_clock(c, s, 1, 1, 1, 2, 0);

  /* ok, now, set those two clocks to the value of (2, 1) */
  set_button(c, s, 2, 1);
  set_clock(c, s, 1, 1, 2, 1, 0);

  /* we have 3 clocks at same value, set them now (together, by selecting the buttons in a state
   * that allows that and turning the correct corner clock) to the value of (0, 1) */
  set_button(c, s, 0, 0);
  set_button(c, s, 2, 0);
  set_button(c, s, 3, 1);
  set_clock(c, s, 1, 1, 0, 1, 3);

  /* we have 4 clocks, set them to the value of (1, 0) */
  set_button(c, s, 2, 1);
  set_clock(c, s, 1, 1, 1, 0, 3);

  /* now, because corner clocks will be set by the other side (the corresponding corner clocks of both sides change
   * whatever button are pushed or not, so we will handle them to the other side), let's put our five not corner
   * clocks to 0 (or 12, depending how you read your clock)
   */
  set_button(c, s, 1, 1);
  set_button(c, s, 0, 1);
  set_abs_clock(c, s, 1, 1, 0, 0);

  /* now, side 2 */
  set_side(c, s, 1);

  /* ok, let's do exaclty the same as for side 1 (and because we turn a corner only if button is up,
   * we will not change other side not corner clocks) */
  set_button(c, s, 0, 1);
  set_clock(c, s, 1, 1, 1, 2, 0);

  set_button(c, s, 2, 1);
  set_clock(c, s, 1, 1, 2, 1, 0);

  set_button(c, s, 0, 0);
  set_button(c, s, 2, 0);
  set_button(c, s, 3, 1);
  set_clock(c, s, 1, 1, 0, 1, 3);

  set_button(c, s, 2, 1);
  set_clock(c, s, 1, 1, 1, 0, 3);

  /* ok, so now, if we did as for side 1, the corner clocks would not be at 0, so let's handle them */
  /* what has to be done is to let each corner clock to the same value of not corner clocks.
   * We could just set them with button down, but it will change the value of other side's not corner clocks,
   * which we don't want.
   * So the principle is to let the other clocks come to the value of a corner clock (we turn all the
   * clocks except the corner one), and then do the same with the next corner clock, and that two more
   * times, so all the clocks are to the same value, then put all this to 0.
   */
  set_button(c, s, 0, 1);
  set_clock(c, s, 1, 1, 2, 0, 3);

  set_button(c, s, 1, 1);
  set_button(c, s, 0, 0);
  set_clock(c, s, 1, 1, 0, 0, 3);

  set_button(c, s, 0, 1);
  set_button(c, s, 2, 0);
  set_clock(c, s, 1, 1, 0, 2, 3);

  set_button(c, s, 2, 1);
  set_button(c, s, 3, 0);
  set_clock(c, s, 1, 1, 2, 2, 0);

  set_button(c, s, 3, 1);
  set_abs_clock(c, s, 1, 1, 0, 0);

  /* ok, it's done. Hope my comments are clear enough for you to understand. Now you should be
   * able to solve it fine.
   * A one more step would be to have a strategy that deals with less moves than this one.
   * I'm sure we can make better. Does anyone has a solution ?
   * One more question would be to know if a computer program can create its own strategies to
   * solve this program, but this is AI and well, I'm not a specialist of this field of computer
   * science. If some of you are, and have some time, maybe they could try to do something ? or
   * explain me some learning technics so I could try to code something, it would be great and fun.
   * Enjoy.
   */
}
#endif

/* as we can go out from the solver at any time, we must use setjmp, longjmp, because the code must
 * remain clear
 */
void solve(CLOCK *c, SCREEN *s)
{
  prevx=prevy=s->sizex/2;
  set_mouse_position(s, s->sizex/2, s->sizex/2);

  if (setjmp(env)==0)
    do_solve(c, s);

  set_mouse_position(s, s->sizex/2, s->sizex/2);
}
