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

#include "clock.h"

#include <string.h>
#include <stdlib.h>

#include "screen.h"

/* depending of the mouse position, we have to draw != mouse pointer shape */
void draw_mouse(CLOCK *c, SCREEN *s, int x, int y)
{
  /* let's have some gotos :) */
  if (mouse_on_button(s, x, y)!=-1)
    goto draw_m_on;

  if (mouse_on_arrow(s, x, y)!=-1)
    goto draw_m_on;

  if (mouse_on_turn(s, x, y)!=-1)
    goto draw_m_on;

  if (mouse_on_quit(s, x, y)!=-1)
    goto draw_m_on;

  draw_mouse_off(s);
  return;

draw_m_on:
  draw_mouse_on(s);
}

void draw_all(CLOCK *c, SCREEN *s)
{
  int i, j;

  /* draw the background */
  draw_back(s, c->current_face);

  /* draw the clocks */
  for (i=0; i<3; i++)
    for (j=0; j<3; j++)
      draw_clock(s, j, i, c->current_face, c->face[c->current_face][j][i]);

  /* draw the buttons */
  for (i=0; i<4; i++)
    draw_button(s, i, c->buttons[c->current_face][i], c->current_face);
}

void init_clock(CLOCK *c, SCREEN *s)
{
  memset(c, 0, sizeof(CLOCK));

  c->buttons[1][0]=
  c->buttons[1][1]=
  c->buttons[1][2]=
  c->buttons[1][3]=1;
  c->mode=NORMAL;
}

void push_button(CLOCK *c, int button, SCREEN *s)
{
  c->buttons[c->current_face^1][button^1] ^= 1;
  draw_button(s, button, c->buttons[c->current_face][button] ^= 1, c->current_face);
}

/* dir = 1 or -1 */
static void turn_clock(CLOCK *c, int corner, int dir, int face, SCREEN *s)
{
  /*
    here the clocks are :
           0    4    1

           5    8    6

           2    7    3
   */
  int i, j;
  /* get the coordinates of the clock from its number, as seen by the previous picture */
  int clock[][2]={{0, 0}, {2, 0}, {0, 2}, {2, 2}, {1, 0}, {0, 1}, {2, 1}, {1, 2}, {1, 1}};

  i=clock[corner][0];
  j=clock[corner][1];

  c->face[face][i][j]+=dir;

  if (c->face[face][i][j]<0)
    c->face[face][i][j]=11;
  if (c->face[face][i][j]==12)
    c->face[face][i][j]=0;

  if (face==c->current_face)
    draw_clock(s, i, j, face, c->face[face][i][j]);
}

static void turn_face(CLOCK *c, int corner, int dir, int face, SCREEN *s)
{
  /*
    here the clocks are :
           0     4     1

             [0]   [1]

           5     8     6

             [2]   [3]

           2     7     3
   */
  int i;
  /* a clock has 2 neighbourgs (in fact 3, but the center always if button pushed, so
   * we don't count it as a neighbourg)
   */
  int neighbourg[][2]={{4, 5}, {4, 6}, {5, 7}, {6, 7}};
  /* the far neighbourgs are the clocks that are not direct neighbourgs */
  int far_neighbourg[][5]={{1, 2, 3, 6, 7}, {0, 2, 3, 5, 7}, {0, 1, 3, 4, 6}, {0, 1, 2, 4, 5}};
  /* far_if tells what button has to be pushed for a far neighbourg to turn
   * some far neighbourfs turn if only one button is pushed, some other may turn
   * if 1 button or another one or both of them are pushed, so we do as if all
   * can have 2 button to activate them (but for the far neighbourgs that have only
   * one button possible, we put it twice, little hack)
   * By the way, we can just say that clock number x turns if and only if button [0] or [1]
   * is pushed, have I deducted from some analyzing (I was going to put each far neighbourg
   * for each corner but I saw it was alaways the same button for each clock, independantly
   * of the reference clock).
   */
  int far_if[][2]={{0, 0}, {1, 1}, {2, 2}, {3, 3}, {0, 1}, {0, 2}, {1, 3}, {2, 3}};

  dir*=2;
  dir--;

  /* turn the clock we know will turn */
  turn_clock(c, corner, dir, face, s);

  /* we gotta check if the corner's clock do turn or not */
  for (i=0; i<3; i++) {
    int ck=far_neighbourg[corner][i];

    if (c->buttons[face][corner]!=c->buttons[face][ck])
      continue;

    turn_clock(c, ck, dir, face, s);
  }

  /* check the close neighbourgs */
  if (c->buttons[face][corner]==0)
    return;

  /* if we are here, we have to continue */
  /* turn the neighbourgs */
  turn_clock(c, neighbourg[corner][0], dir, face, s);
  turn_clock(c, neighbourg[corner][1], dir, face, s);
  /* if the neighbourgs turn, the center does turn too */
  turn_clock(c, 8, dir, face, s);

  /* now, check the 2 other clocks */
  for (i=3; i<5; i++) {
    /* this code is done like the following for readability and not speed consideration
     * (anyway, this part of the program does not eat lotsa cpu, so we can be readable)
     */
    int ck=far_neighbourg[corner][i];
    int button1, button2;

    button1=far_if[ck][0];
    button2=far_if[ck][1];

    if (!c->buttons[face][button1] && !c->buttons[face][button2])
      continue;

    turn_clock(c, ck, dir, face, s);
  }
}

#if 0
/* this version was incorrect, I let it there for educationnal purpose */
static void turn_face(CLOCK *c, int corner, int dir, int face, SCREEN *s)
{
  /*
    here the clocks are :
           0     4     1

             [0]   [1]

           5     8     6

             [2]   [3]

           2     7     3
   */
  int i;
  /* a clock has 2 neighbourgs (in fact 3, but the center always if button pushed, so
   * we don't count it as a neighbourg)
   */
  int neighbourg[][2]={{4, 5}, {4, 6}, {5, 7}, {6, 7}};
  /* the far neighbourgs are the clocks that are not direct neighbourgs */
  int far_neighbourg[][5]={{1, 2, 3, 6, 7}, {0, 2, 3, 5, 7}, {0, 1, 3, 4, 6}, {0, 1, 2, 4, 5}};
  /* far_if tells what button has to be pushed for a far neighbourg to turn
   * some far neighbourfs turn if only one button is pushed, some other may turn
   * if 1 button or another one or both of them are pushed, so we do as if all
   * can have 2 button to activate them (but for the far neighbourgs that have only
   * one button possible, we put it twice, little hack)
   * By the way, we can just say that clock number x turns if and only if button [0] or [1]
   * is pushed, have I deducted from some analyzing (I was going to put each far neighbourg
   * for each corner but I saw it was alaways the same button for each clock, independantly
   * of the reference clock).
   */
  int far_if[][2]={{0, 0}, {1, 1}, {2, 2}, {3, 3}, {0, 1}, {0, 2}, {1, 3}, {2, 3}};
  dir*=2;
  dir--;

  /* turn the clock we know will turn */
  turn_clock(c, corner, dir, face, s);

  /* check the close neighbourgs */
  if (c->buttons[face][corner]==0)
    return;

  /* if we are here, we have to continue */
  /* turn the neighbourgs */
  turn_clock(c, neighbourg[corner][0], dir, face, s);
  turn_clock(c, neighbourg[corner][1], dir, face, s);
  /* if the neighbourgs turn, the center does turn too */
  turn_clock(c, 8, dir, face, s);

  /* now, check the 5 other clocks */
  for (i=0; i<5; i++) {
    /* this code is done like the following for readability and not speed consideration
     * (anyway, this part of the program does not eat lotsa cpu, so we can be readable)
     */
    int ck=far_neighbourg[corner][i];
    int button1, button2;

    button1=far_if[ck][0];
    button2=far_if[ck][1];

    if (!c->buttons[face][button1] && !c->buttons[face][button2])
      continue;

    turn_clock(c, ck, dir, face, s);
  }
}
#endif

void turn_corner(CLOCK *c, int corner, int dir, SCREEN *s)
{
  int other_side[]={1, 0, 3, 2};

  turn_face(c, corner, dir, c->current_face, s);
  turn_face(c, other_side[corner], dir^1, c->current_face^1, s);

  /* the following code was incorrect, due to the incorrectness of turn_face, but I let it there, for educationnal purpose */
#if 0
  /* a little hack here, because corner clocks turn together, so imagine button 0 and 3 are pressed,
   * so in the other side, they won't. In face 1, if I turn clock 0, clock 3 will turn too, but in face
   * 2, because button nut pressed, clock 2 won't, but it should, so problem. Solution : this little hack.
   * current face is absolutly correct, so we put the other side clocks to their right value,
   * simply by getting the value of their corresponding ones in the current face, thus rendering turn_face
   * wrong, in a sense, making this code a bit obfuscate, which is not good practice. But that's the world
   * of coding, sometimes, data get incoherent...
   */
  c->face[c->current_face^1][0][0] = 12 - c->face[c->current_face][2][0];
  c->face[c->current_face^1][0][2] = 12 - c->face[c->current_face][2][2];
  c->face[c->current_face^1][2][0] = 12 - c->face[c->current_face][0][0];
  c->face[c->current_face^1][2][2] = 12 - c->face[c->current_face][0][2];
#endif
}

void return_clock(CLOCK *c, SCREEN *s)
{
  c->current_face ^= 1;
  draw_all(c, s);
}

/* return a random between 0 and max-1 */
int myrand(int max)
{
  return (float)max*rand()/(RAND_MAX+1.0);
}

void randomize_clock(CLOCK *c)
{
  int i, j, k;

  for (k=0; k<2; k++)
    for (i=0; i<3; i++)
      for (j=0; j<3; j++)
	c->face[k][i][j]=myrand(12);

  /* we've got to normalize the corner's clocks (don't forget the modulo stuff !!!) */
  c->face[1][0][2]=(12-c->face[0][2][2])%12;
  c->face[1][2][2]=(12-c->face[0][0][2])%12;
  c->face[1][0][0]=(12-c->face[0][2][0])%12;
  c->face[1][2][0]=(12-c->face[0][0][0])%12;
}
