/*
 * src/screensaver.c, part of Complete Goban (game program)
 * Copyright (C) 1998 Scott Draves
 * See "configure.h.in" for more copyright information.
 */


#include <wms.h>
#include <rnd.h>
#include <but.h>
#include <plain.h>
#include <ctext.h>
#include <text.h>
#include <term.h>
#include <abut_msg.h>
#include <fsel.h>
#include <clp.h>
#include <str.h>
#include <text.h>
#include <textin.h>
#include <timer.h>
#include "cgoban.h"
#include "goban.h"
#include "msg.h"
#include "goGame.h"
#include "goPic.h"
#include "cgbuts.h"
#include "sgf.h"
#include "sgfIn.h"
#include "sgfOut.h"
#include "sgfPlay.h"
#include "editBoard.h"
#include <dirent.h>
#include <math.h>
#ifdef  _LOCAL_H_
   Levelization Error.
#endif
#include "screensaver.h"
#include "local.h"


static bool  fwdOk(void *packet)  {
  return TRUE;
}


#if 0
#define none2 NULL
#define none1 NULL
#define nonev1 NULL
#else
GobanOut
none2(void *packet, int loc)
{
  return 0;
}

GobanOut
none1(void *packet)
{
  return gobanOut_draw;
}


void
nonev1(void *packet)
{
}
#endif

static const GobanActions  local_actions = {
  none2, none1, none1, none1, none1,
  none1, none1, none1, none1, none1,
  none1, none1,
  NULL,
  nonev1,
  fwdOk, fwdOk, fwdOk, fwdOk};


static void
set_time(struct timeval *s, double secs) {
  s->tv_usec = (int)(secs * 1e6) % (1000*1000);
  s->tv_sec = secs;
}
  

static double
get_time(struct timeval *s) {
  return s->tv_sec + s->tv_usec * 1e-6;
}
  
static const char *
gameTitle(Sgf *mc) {
  SgfElem  *me = sgf_findFirstType(mc, sgfType_title);
  if (me) {
    return str_chars(me->sVal);
  } else if ((me = sgf_findFirstType(mc, sgfType_playerName))) {
    char buf[1000];
    SgfElem *you = sgfElem_findFirstType(me, sgfType_playerName);
    const char *n1 = str_chars(me->sVal);
    const char *n2 = you ? str_chars(you->sVal) : "?";
    if (me->gVal != goStone_white) {
      const char *t = n1;
      n1 = n2;
      n2 = t;
    }
    sprintf(buf, "%s - %s", n1, n2);
    return strdup(buf); /* leaks */
  }
  return msg_noTitle;
}


void
screensaver_setSgf(Screensaver *l, Sgf *mc) {
  GoRules  rules;
  int  size, hcap;
  float  komi;
  GoTime  time;
  SgfElem  *me;
  l->moves = mc;
  l->move = 0;
  me = sgf_findType(mc, sgfType_rules);
  if (me)  {
    rules = (GoRules)me->iVal;
  } else
    rules = goRules_japanese;
  me = sgf_findType(mc, sgfType_size);
  if (me)  {
    size = me->iVal;
  } else  {
    size = 19;
  }
  me = sgf_findType(mc, sgfType_handicap);
  if (me)  {
    hcap = me->iVal;
  } else  {
    hcap = 0;
  }
  me = sgf_findType(mc, sgfType_komi);
  if (me)  {
    komi = (float)me->iVal / 2.0;
  } else  {
    komi = 0.0;
  }
  me = sgf_findFirstType(mc, sgfType_playerName);
  while (me)  {
    me = sgfElem_findFirstType(me, sgfType_playerName);
  }


  me = sgf_findFirstType(mc, sgfType_time);
  if (me)  {
    goTime_parseDescribeChars(&time, str_chars(me->sVal));
  } else  {
    time.type = goTime_none;
  }
  if (l->game)
    goGame_destroy(l->game);

  // should be passive ? XXX
  l->game = goGame_create(size, rules, hcap, komi, &time, TRUE);

  sgf_play(mc, l->game, NULL, -1, NULL);

  if (l->goban)
    butText_set(l->goban->labelText, gameTitle(mc));

  goGame_moveTo(l->game, 0);
}

char *
screensaver_randomSgf(char *dir)
{
  char *namelist[8096];
  struct dirent *dirp;
  DIR *dp;
  int i, n = 0;
  char *sgf;

  if((dp=opendir(dir)) == NULL ) {
    fprintf(stderr, "could not read %s\n", dir);
    exit(1);
  }
  while ((dirp = readdir(dp)) != NULL) {
    if (dirp->d_name[0] == '.')continue;
    namelist[n++]=strdup(dirp->d_name);
  }
  {
    char buf[1000];
    sprintf(buf, "%s/%s", dir, namelist[random() % n]);
    sgf = strdup(buf);
  }
  for (i = 0; i < n; i++)
    free(namelist[i]);

  closedir(dp);

  return sgf;
}

Sgf *
set_game(Screensaver *l)
{
  Sgf *mc;
  const char *err;
  while (1) {
    char *fname = screensaver_randomSgf(l->game_dir);
    mc = sgf_createFile(l->cg, fname, &err, NULL);
    if (mc) {
      /* printf("playing %s\n", fname); */
      return mc;
    }
    fprintf(stderr, "error in %s\n", err);
  }
}

static ButOut
next_move(ButTimer *timer)
{
  Screensaver  *l = butTimer_packet(timer);

  {
    double p = get_time(&timer->period);
    p *= l->acceleration;
    if (p < l->mintime) p = l->mintime;
    set_time(&timer->period, p);
  }

  if (l->game->moveNum == l->game->maxMoves) {
    Sgf  *mc;
    sleep(l->gametime);
    mc = set_game(l);
    screensaver_setSgf(l, mc);
    set_time(&l->timer->period, l->stonetime);
    butTimer_reset(l->timer);
  } else {
    goGame_moveTo(l->game, l->game->moveNum + 1);
  }

  goban_update(l->goban);
  
  return 0;
}

Screensaver *
screensaver_create(Cgoban *cg)  {
  Sgf  *mc;
  Screensaver  *l;
  const char *title;

  srandom(time(0));

  cg->env->minWindows = 1;
  cg->screensaver = 1;
  cg->showCoords = 0;


  
  assert(MAGIC(cg));
  l = wms_malloc(sizeof(Screensaver));
  MAGIC_SET(l);
  l->cg = cg;
  l->game = NULL;
  l->goban = NULL;
  {
      char *gd = clp_getStr(cg->clp, "game-dir");
      l->game_dir = strdup((gd && gd[0]) ? gd : PACKAGE_DATA_DIR);
  }

  mc = set_game(l);

  screensaver_setSgf(l, mc);

  title = gameTitle(mc);

  l->goban = goban_create(cg, &local_actions, l, l->game, "local",
			  title);
  l->goban->iDec1 = grid_create(&cg->cgbuts, NULL, NULL, l->goban->iWin, 2,
				BUT_DRAWABLE, 0);
  grid_setStone(l->goban->iDec1, goStone_white, FALSE);
  l->goban->iDec2 = grid_create(&cg->cgbuts, NULL, NULL, l->goban->iWin, 2,
				BUT_DRAWABLE, 0);
  grid_setStone(l->goban->iDec2, goStone_black, FALSE);

  {
    double x = clp_getDouble(cg->clp, "gametime");
    if (x < 0.0) {
      fprintf(stderr, "gametime cannot be negative, %g is.\n", x);
      x = 0.0;
    }
    x *= 1e-3;
    l->gametime = x;
  }


  {
    double x = clp_getDouble(cg->clp, "acceleration");
    /* command line option is normally 0 - 100.  150 is max possible,
       negative values are fine */
    x = 1.0 - pow(10.0, -(1.0 + (100.0-x) * 0.02));
    if (x < 0.0) x = 0.0;
    l->acceleration = x;
  }

  {
    double x = clp_getDouble(cg->clp, "minstonetime");
    if (x <= 1) {
      fprintf(stderr, "minstonetime is too small (%g).\n", x);
      x = 1;
    }
    x *= 1e-3;
    l->mintime = x;
  }

  {
    struct timeval period;
    double secs = clp_getDouble(cg->clp, "stonetime");

    if (secs <= 0.0) {
      fprintf(stderr, "stonetime must be positive, %g is not.\n", secs);
      secs = 1e-4;
    } else
      secs *= 1e-3;

    l->stonetime = secs;
    
    set_time(&period, secs);

    l->timer = butTimer_create(l, NULL, period, period, 0, next_move);
  }
  
  return(l);
}
