/* a interface to ios for O'CAML */
#include <string.h>
#include <stdio.h>
#include <caml/mlvalues.h>
#include <caml/callback.h>
#include <caml/memory.h>
#include <caml/alloc.h>

#include "client.h"

#define CIOS_SQUARE(r, c) (((r) + 1) * 10 + (c) + 1)
#define CIOS_ROW(s) ((s) / 10 - 1)
#define CIOS_COL(s) ((s) % 10 - 1)
#define CIOS2MY_SQUARE(s) (CIOS_ROW(s) * 8 + CIOS_COL(s))
#define MY2CIOS_SQUARE(s) CIOS_SQUARE((s) >> 3, (s) & 7)

value
cios_connect(value host, value port)
{
  CAMLparam2(host, port);
  ios_connect(String_val(host), Int_val(port));
  CAMLreturn Val_unit;
}

value
cios_disconnect(value dummy)
{
  CAMLparam1(dummy);
  ios_disconnect();
  CAMLreturn Val_unit;
}


value
cios_logon(login, passw, echo, input)
     value login, passw, echo, input;
{
  CAMLparam4(login, passw, echo, input);
  
  if (strlen(String_val(login)) > 8)
    invalid_argument("cios_logon: name too long");
  if (strlen(String_val(passw)) > 8)
    invalid_argument("cios_logon: password too long");

  ios_logon(String_val(login), String_val(passw),
	    Bool_val(echo), Bool_val(input));
  CAMLreturn Val_unit;
}

value
cios_get(value dummy)
{
  CAMLparam1(dummy);
  
  ios_type type;

  v_ios.type = IOS_NONE;
  ios_get();
  type = v_ios.type;
  v_ios.type = IOS_NONE;
  
  switch (type) {
  case IOS_NONE:
    CAMLreturn Val_int(0);	/* None_get */
  case IOS_MATCH:		/* Match_get a_match */
    {
      ios_match *m = &v_ios.u.MATCH;
      CAMLlocal2(match_get, a_match);
      
      match_get = alloc(1, 0);	/* Match_get */
      Store_field(match_get, 0, Val_int(0)); /* dummy value */
      
      a_match = alloc(16, 0);	/* a_match */
      Store_field(a_match, 0, Val_bool(m->stored));
      Store_field(a_match, 1, Val_bool(m->rated));
      Store_field(a_match, 2, Val_int(m->my_time));
      Store_field(a_match, 3, Val_int(m->op_time));
      Store_field(a_match, 4, Val_int(m->my_inc));
      Store_field(a_match, 5, Val_int(m->op_inc));
      Store_field(a_match, 6, Val_int(m->my_def));
      Store_field(a_match, 7, Val_int(m->op_def));
      Store_field(a_match, 8, Val_int(m->my_color == MY_BLACK ? 0 : 1));
      Store_field(a_match, 9, Val_bool(m->rnd_color));
      Store_field(a_match, 10, copy_string(m->op_name));
      Store_field(a_match, 11, Val_int(m->max_win));
      Store_field(a_match, 12, Val_int(m->min_win));
      Store_field(a_match, 13, Val_int(m->min_loss));
      Store_field(a_match, 14, Val_int(m->max_loss));
      Store_field(a_match, 15, Val_bool(m->confirmation));

      Store_field(match_get, 0, a_match);
      CAMLreturn match_get;
    }

  case IOS_BOARD:
  case IOS_LOOK:
    {
      int square;
      ios_board *b = &v_ios.u.BOARD;
      
      CAMLlocal5(retval, someblack, somewhite, brda, brd);
      
      retval = alloc(1, type == IOS_BOARD ? 1 : 4);	/* Board | Look */
      Store_field(retval, 0, Val_false); /* dummy value */
      
      someblack = alloc(1, 0);	/* Some */
      Store_field(someblack, 0, Val_int(0)); /* Black */
      somewhite = alloc(1, 0);	/* Some */
      Store_field(somewhite, 0, Val_int(1)); /* White */
      
      /* allocate the board array */
      brda = alloc(64, 0);	/* board array */
      for (square = 0; square < 64; square++) {
	switch (b->board[ MY2CIOS_SQUARE(square) ]) {
	case MY_BLACK:
	  Store_field(brda, square, someblack); break; /* Some Black */
	case MY_WHITE:
	  Store_field(brda, square, somewhite); break; /* Some White */
	default:
	  Store_field(brda, square, Val_int(0)); /* None */
	}
      }

      brd = alloc(14, 0);	/* board */

      Store_field(brd, 0, copy_string(b->who));
      Store_field(brd, 1, Val_int(b->no));
      Store_field(brd, 2, brda);
      Store_field(brd, 3, Val_int(b->time_w));
      Store_field(brd, 4, Val_int(b->time_b));
      Store_field(brd, 5, Val_int(b->def_w));
      Store_field(brd, 6, Val_int(b->def_b));
      Store_field(brd, 7, Val_int(b->inc_w));
      Store_field(brd, 8, Val_int(b->inc_b));
      Store_field(brd, 9, Val_int(b->turn_color == MY_BLACK ? 0 : 1));
      Store_field(brd, 10, Val_bool(b->turn_my));
      if (b->last_move == MY_PASS) 
	Store_field(brd, 11, Val_int(0)); /* Pass */
      else {
	CAMLlocal1(mv);
	mv = alloc(1, 0);	/* Square of int */
	Store_field(mv, 0, Val_int(CIOS2MY_SQUARE(b->last_move)));
	Store_field(brd, 11, mv);
      }
      Store_field(brd, 12, Val_int(b->last_move_no));
      Store_field(brd, 13, copy_double(b->last_value));

      Store_field(retval, 0, brd);
      CAMLreturn retval;
    }
  
  case IOS_MOVES:		/* Moves of Board.move list */
    {
      ios_moves *m = &v_ios.u.MOVES;
      int i;

      CAMLlocal4(head, newnode, square, retval);
      
      head = Val_int(0);	/* empty list */
      for (i = 0; i < m->no; i++) {
	newnode = alloc(2, 0);	/* allocate a cons cell */
	if (m->moves[i] == MY_PASS)
	  Store_field(newnode, 0, Int_val(0)); /* Pass */
	else {
	  square = alloc(1, 0);	/* Square of int */
	  Store_field(square, 0, Val_int(CIOS2MY_SQUARE(m->moves[i])));
	  Store_field(newnode, 0, square);
	}
	/* push the new cons cell on the list */
	Store_field(newnode, 1, head);
	head = newnode;
      }
      /* now head contains the list of moves */
      retval = alloc(1, 2);	/* Moves of Board.move list */
      Store_field(retval, 0, head);
      CAMLreturn retval;
    }

  case IOS_GAME_END:
    CAMLreturn Val_int(1);	/* Game_end */
  case IOS_GAME_CREATE:
    {
      ios_create *c = &v_ios.u.CREATE;

      CAMLlocal2(create, retval);
      
      create = alloc(5, 0);	/* create */
      Store_field(create, 0, copy_string(c->black));
      Store_field(create, 1, copy_string(c->white));
      Store_field(create, 2, Val_bool(c->b_trust));
      Store_field(create, 3, Val_bool(c->w_trust));
      Store_field(create, 4, Val_bool(c->rated));

      retval = alloc(1, 3);	/* Game_create of create */
      Store_field(retval, 0, create);
      CAMLreturn retval;
    }
      
  case IOS_WHO:
    {
      int i;
      ios_who *w = &v_ios.u.WHO;

      CAMLlocal4(head, whoelem, newnode, retval);

      head = Val_int(0);	/* empty list */
      for (i = 0; i < w->no; i++) {
	/* create a who_elem */
	whoelem = alloc(8, 0);	/* who_elem */
	Store_field(whoelem, 0, copy_string(w->list[i].name));
	Store_field(whoelem, 1, Val_int(w->list[i].blitz));
	Store_field(whoelem, 2, Val_int(w->list[i].standard));
	Store_field(whoelem, 3, Val_bool(w->list[i].registered));
	Store_field(whoelem, 4, Val_bool(w->list[i].playing));
	Store_field(whoelem, 5, Val_bool(w->list[i].open));
	Store_field(whoelem, 6, Val_bool(w->list[i].rated));
	Store_field(whoelem, 7, Val_bool(w->list[i].trust));

	newnode = alloc(2, 0);	/* cons cell */
	Store_field(newnode, 0, whoelem);
	Store_field(newnode, 1, head);
	head = newnode;
      }
      /* now head contains the list of who_elems */
      retval = alloc(1, 5);	/* Who_get of who_elem list */
      Store_field(retval, 0, head);
      CAMLreturn retval;
    }
  default:
    failwith("cios_get: unknown v_ios.type");
  }
}

value
cios_put(value settings)
{
  CAMLparam1(settings);
  
  if (Is_long(settings)) 	/* Who_put */
    v_ios.type = IOS_WHO;
  else {
    switch (Tag_val(settings)) {
    case 0:			/* Match_put of a_match  */
      {
	ios_match *m = &v_ios.u.MATCH;
	value match = Field(settings, 0);

	if (strlen(String_val(m->op_name)) > 8)
	  invalid_argument("cios_put: op_name too long");

	m->stored = Bool_val(Field(match, 0));
	m->rated = Bool_val(Field(match, 1));
	m->my_time = Int_val(Field(match, 2));
	m->op_time = Int_val(Field(match, 3));
	m->my_inc = Int_val(Field(match, 4));
	m->op_inc = Int_val(Field(match, 5));
	m->my_def = Int_val(Field(match, 6));
	m->op_def = Int_val(Field(match, 7));
	m->my_color = Int_val(Field(match, 8)) == 0 ? MY_BLACK : MY_WHITE;
	m->rnd_color = Bool_val(Field(match, 9));
	strcpy(m->op_name, String_val(Field(match, 10)) );
	m->max_win = Int_val(Field(match, 11));
	m->min_win = Int_val(Field(match, 12));
	m->min_loss = Int_val(Field(match, 13));
	m->max_loss = Int_val(Field(match, 14));
	m->confirmation = Bool_val(Field(match, 15));
	v_ios.type = IOS_MATCH;

	break;
      }
    case 1:			/* Move of Board.move * float * float */
      {
	ios_move *m = &v_ios.u.MOVE;
	v_ios.type = IOS_MOVE;
	if (Is_long(Field(settings, 0)))
	  m->yx = MY_PASS;
	else
	  m->yx = MY2CIOS_SQUARE(Int_val(Field(Field(settings, 0), 0)));
	m->time = Double_val(Field(settings, 1));
	m->value = Double_val(Field(settings, 2));
	break;
      }
    
    case 2:			/* Accept of string */
      if (strlen(String_val(Field(settings, 0))) > 8)
	invalid_argument("cios_put: Accept name too long");
      v_ios.type = IOS_ACCEPT;
      strcpy(v_ios.u.ACCEPT.name, String_val(Field(settings, 0)));

      break;

    case 3:			/* Decline of string * string */
      {
	char *name = String_val(Field(settings, 0));
	char *reason = String_val(Field(settings, 1));
	if (strlen(name) > 8)
	  invalid_argument("cios_put: Decline name too long");
	if (strlen(reason) >= 512)
	  invalid_argument("cios_put: Decline reason too long");
	strcpy(v_ios.u.DECLINE.name, name);
	strcpy(v_ios.u.DECLINE.reason, reason);
	v_ios.type = IOS_DECLINE;
      }
    } /* switch */
  }
  
  ios_put();
  CAMLreturn Val_unit;
}

value
cios_put_str(value str)
{
  CAMLparam1(str);
  ios_put_str(String_val(str));
  CAMLreturn Val_unit;
}

/* challenge : int -> int -> int -> Board.piece option -> int -> int -> bool ->
   bool -> unit */
value
cios_challenge(time, inc, def, color, min_rating, max_rating, must_rated,
	       best)
     value time, inc, def, color, min_rating, max_rating, must_rated, best;
{
  CAMLparam5(time, inc, def, color, min_rating);
  CAMLxparam3(max_rating, must_rated, best);
  
  int clr = Is_long(color) ? 0 : (Field(color, 0) == 0 ? MY_BLACK : MY_WHITE);
  int rnd_color = Is_long(color);

  ios_challenge(Int_val(time), Int_val(inc), Int_val(def), clr, rnd_color,
		Int_val(min_rating), Int_val(max_rating),
		Bool_val(must_rated), Bool_val(best));
  CAMLreturn Val_unit;
}

value
cios_challenge_byte(value *args, int n)
{
  cios_challenge(args[0], args[1], args[2], args[3], args[4], args[5],
		 args[6], args[7]);
  return Val_unit;
}
