/*

  t-fsm.c

  Author: Antti Huima <huima@ssh.fi>

  Copyright (c) 1999 SSH Communications Security, Finland
  All rights reserved.

  Created Thu Aug 26 15:26:11 1999.

  */

#include "sshincludes.h"
#include "sshdebug.h"
#include "sshfsm.h"
#include "ssheloop.h"
#include "sshstream.h"
#include "sshfdstream.h"
#include "sshbuffer.h"
#include "sshfsmstreams.h"

#include <ctype.h>

#define SSH_DEBUG_MODULE "t-fsm"

typedef struct {
  SshStream stdio_stream;
  SshUInt32 flags;
  SshFSMCondition input_avail, input_consumed;
  SshFSMCondition output_avail, output_consumed;
  SshFSMCondition stub_done;
  SshBuffer *inbuf, *outbuf;
  SshFSMThread stub, processer;
} GlobalData;

typedef struct {
  int d;
} ProcesserData;

SSH_FSM_STEP(process)
{
  int k;
  unsigned char *ptr;
  int i;
  SSH_FSM_DATA(GlobalData *, ProcesserData *);

  k = ssh_buffer_len(gdata->inbuf);

  if (gdata->flags & SSH_STREAMSTUB_EOF_RECEIVED)
    {
      SSH_DEBUG(6, ("EOF got, finishing the processor thread."));
      SSH_FSM_THROW(gdata->stub, SSH_STREAMSTUB_FINISH);
      return SSH_FSM_FINISH;
    }

  if (k == 0)
    SSH_FSM_CONDITION_WAIT(gdata->input_avail);

  ptr = ssh_buffer_ptr(gdata->inbuf);
  for (i = 0; i < k; i++)
    {
      ptr[i] = toupper(ptr[i]);
    }
  ssh_buffer_append(gdata->outbuf, ptr, k);
  ssh_buffer_consume(gdata->inbuf, k);
  SSH_FSM_CONDITION_SIGNAL(gdata->input_consumed);
  SSH_FSM_CONDITION_SIGNAL(gdata->output_avail);
  return SSH_FSM_CONTINUE;
}

SSH_FSM_STEP(watch)
{
  SSH_FSM_GDATA(GlobalData *);
  SSH_FSM_SET_NEXT("kill");
  SSH_FSM_CONDITION_WAIT(gdata->stub_done);
}

SSH_FSM_STEP(kill_fsm)
{
  ssh_fsm_destroy(ssh_fsm_get_fsm(thread));
  return SSH_FSM_FINISH;
}

SshFSMStateMapItem states_array [] =
{
#include "sshfsmstreams_states.h"
  { "process", "Process data", process },
  { "watch", "Terminate FSM when possible", watch },
  { "kill", "Terminate FSM", kill_fsm }
};

int main(int argc, char **argv)
{
  SshFSM fsm;
  GlobalData *g;

#if 0
  /* XXX: use these; or get rid of them. They do not work under
     windows. */
  int fd1, fd2;

  fd1 = open("./Makefile", O_RDONLY, 0666);
  fd2 = open("./temp-out", O_WRONLY|O_CREAT|O_TRUNC, 0666);
#endif

  ssh_event_loop_initialize();

  ssh_debug_set_level_string("*=8");

  fsm = ssh_fsm_allocate(sizeof(GlobalData),
                         states_array,
                         SSH_FSM_NUM_STATES(states_array),
                         NULL);

  g = ssh_fsm_get_gdata_fsm(fsm);

  g->flags = 0;
  g->input_avail = ssh_fsm_condition_create(fsm);
  g->input_consumed = ssh_fsm_condition_create(fsm);
  g->output_avail = ssh_fsm_condition_create(fsm);
  g->output_consumed = ssh_fsm_condition_create(fsm);
  g->stub_done = ssh_fsm_condition_create(fsm);
  g->inbuf = ssh_buffer_allocate();
  g->outbuf = ssh_buffer_allocate();
  g->stdio_stream = ssh_stream_fd_stdio();

  g->processer =
    ssh_fsm_spawn(fsm, sizeof(ProcesserData), "process", NULL, NULL);

  ssh_fsm_spawn(fsm, 0, "watch", NULL, NULL);

  ssh_fsm_set_thread_name(g->processer, "processer");

  g->stub = ssh_streamstub_spawn(fsm,
                                 g->stdio_stream,
                                 g->inbuf,
                                 g->outbuf,
                                 5000,
                                 g->input_avail,
                                 g->input_consumed,
                                 g->output_consumed,
                                 g->output_avail,
                                 g->stub_done,
                                 &(g->flags));

  ssh_event_loop_run();

  exit(0);
}
