//Copyright, 2002-2003, Astra Network Inc.  All Rights Reserved

//This source code has been published by Astra Network Inc. However, any
//use, reproduction, modification, distribution or transfer of this
//software, or any software which includes or is based upon any of this
//code, is only permitted if expressly authorized by a written license
//agreement from Astra. Contact your Astra representative directly for
//more information.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

#include "checkpoint.h"

#define PIPENAME "/tmp/simple"

#define CP_CLEAR        0
#define CP_RECV_DATA    1
#define CP_GOOD_DATA    2

#define ERR_OPEN_FAILED 100
#define ERR_READ_FAILED    101
#define ERR_BAD_DATA       102

#define NODE_EXIT         0
#define NODE_INIT         1
#define NODE_GETWORK      2


int
normalInit (HC_Sequence_t *sequence, void *userdata)
{
  int *fd = (int *) userdata;

  *fd = open (PIPENAME, O_RDONLY);
  if (*fd < 0)
    {
      HC_NormalFail (sequence, ERR_OPEN_FAILED);
    }
  return (HC_NormalSuccess (sequence, NODE_GETWORK));
}

int
normalGetWork (HC_Sequence_t *sequence, void *userdata)
{
  int *fd = (int *) userdata;
  int rc, i = 0;
  char buf[27];


  while( i < 25 )
    {
      HC_Checkpoint (sequence, CP_CLEAR);	// clear checkpoints

      memset (buf, 0, sizeof (buf));

      rc = read (*fd, buf, 26);
      if (rc != 26)
        {
          return (HC_NormalFail (sequence, ERR_READ_FAILED));
        }

      HC_Checkpoint (sequence, CP_RECV_DATA);
      if (buf[0] != 'A')
        {
          return (HC_NormalFail (sequence, buf[0]));
        }
      printf("%d successful read(s)\n", ++i );
    }
  return (HC_NormalSuccess (sequence, NODE_EXIT));
}


int
rollbackInit (HC_Sequence_t *sequence, void *userdata, 
              unsigned long checkpoint)
{
  printf("Open failed! Perhaps the server died?\n");
  sleep (1);			// open failed - wait and try again
  return (HC_RollBackSuccess (sequence, NODE_INIT));
}

int
rollbackGetWork (HC_Sequence_t *sequence, void *userdata, 
                 unsigned long checkpoint)
{
  int *fd = (int *) userdata;

  switch (checkpoint)
    {
    case CP_RECV_DATA:
      printf("Some bad data\n" );
    case CP_CLEAR:
      // we don't do any thing different whether the read failed or the
      // data is bad.
      break;
    default:
      HC_Panic (sequence, "Invalid checkpoint %ul!\n", checkpoint);
      break;
    }
  // We say that we succeed so we try again. The policy will try 5 times,
  // then go to the previous node and reopen the file.
  return (HC_RollBackSuccess (sequence, NODE_GETWORK));
}

int
policyInit (HC_Sequence_t *sequence, void *userdata,
	    HC_PolicyEvent_t event, long policy_data)
{
  switch (event)
    {
    case HC_NORMALFAIL:
      // Rollback forever
      return (HC_RollBackCurrent (sequence));
    default:
      return (HC_DefaultPolicy (sequence, userdata, event, policy_data));
    }
}

int
policyGetWork (HC_Sequence_t *sequence, void *userdata,
	       HC_PolicyEvent_t event, long policy_data)
{
  int *fd = (int *)userdata;

  switch (event)
    {
    case HC_NORMALFAIL:
      // override default in case of failed read - there is no point in
      // trying this 5 times.
      if (policy_data == ERR_READ_FAILED)
        {
          close( *fd );
          return (HC_RollBackPrev (sequence));
        }
      printf("policy_data = %c\n", policy_data );
    default:
      return (HC_DefaultPolicy (sequence, userdata, event, policy_data));
    }
}

int
main (int argc, char *argv[])
{
  HC_NodelistNode_t nodes[] = {
    {NODE_INIT,    HC_NORMALFUNC, normalInit},
    {NODE_GETWORK, HC_NORMALFUNC, normalGetWork},
    {NODE_INIT,    HC_ROLLBACKFUNC, rollbackInit},
    {NODE_GETWORK, HC_ROLLBACKFUNC, rollbackGetWork},
    {NODE_INIT,    HC_POLICYFUNC, policyInit},
    {NODE_GETWORK, HC_POLICYFUNC, policyGetWork},
    {0,            HC_LISTEND, NULL}
  };

  HC_Sequence_t *sequence;
  int fd;
  int rc;

  sequence = HC_NewSequence (nodes);

  rc = HC_CallSequence (sequence, &fd, NODE_INIT);

  fprintf (stderr, "Sequence was: %s\n", HC_Strerror (rc));

  HC_DeleteSequence( sequence );

  return (EXIT_SUCCESS);
}
