/*
  パケット操作
  Satofumi KAMIMURA
  $Id: packetHandleHost.c 329 2008-11-03 00:02:55Z satofumi $
*/

#include "packetHandleHost.h"
#ifdef __cplusplus
#include "runCtrl.h"
#else
#include "connect_device.h"
#define Connection int
#endif


enum {
  SEND_PACKET_SIZE = 512,
  TIMEOUT_SHIFT = 3,
};


// !!! 複数のクライアントを扱うようになったら、そのときに変更する
static int RetryTimes = 3;
void set_runRetryTimes(int times) {
  RetryTimes = times;
}

static int RecvTimeout = -1;
void set_runRecvTimeout(int timeout) {
  RecvTimeout = timeout;
}



static void sendRetryPacket(qrk::Connection* con,
			    const char* send_packet, int send_size) {
#ifdef __cplusplus
  con->send(send_packet, send_size);
#else
  device_send(*con, send_packet, send_size);
#endif
}


int waitPacketResponse(qrk::Connection* con, runCtrl_t* tbl,
		       char* send_packet, int send_size,
		       int unique_id) {
  enum {
    WaitStartByte,
    WaitHeaderRecv,
    WaitPacketRecv,
    RecvCompleted,
    SendRetry,
  };
  int timeout;
  int state = SendRetry;
  int retry_times = 0;
  char recv_buffer[SEND_PACKET_SIZE];
  int filled = 0;
  int require_size = NODE_ACCESS_PACKET_HEADER_SIZE;
  int packet_length = 0;
  int type = PACKET_UNKNOWN;
  int n;

  if (RecvTimeout < 0) {
    timeout = send_size << TIMEOUT_SHIFT;
  } else {
    timeout = RecvTimeout;
  }

#ifdef __cplusplus
  if (!con) {
    throw VXV::RunCtrl_Exception("ConnectionDevice is not connected");
  }
#else
  if (con < 0) {
    return -1;
  }
#endif

  do {
    // リトライ
    if (state == SendRetry) {
      state = WaitStartByte;
      sendRetryPacket(con, send_packet, send_size);

      require_size = NODE_ACCESS_PACKET_HEADER_SIZE;
      recv_buffer[0] = 0x00;
      filled = removeInvalidPacketHeader((unsigned char*)recv_buffer, filled);
      ++retry_times;

      // リトライに失敗
      if (retry_times > RetryTimes) {
#ifdef __cplusplus
	return ConnectionDevice::RetryFail;
#else
	return -1;
#endif
      }
    }

    // 受信処理
    filled = removeInvalidPacketHeader((unsigned char*)recv_buffer, filled);
#ifdef __cplusplus
    n = con->receive(&recv_buffer[filled], require_size, timeout);
#else
    n = device_recv(*con, &recv_buffer[filled], require_size, timeout);
#endif
    if (n <= 0) {
      state = SendRetry;
      continue;
    }
    filled += n;
    require_size -= n;
    filled = removeInvalidPacketHeader((unsigned char*)recv_buffer, filled);

    if (state == WaitStartByte) {
      // スタートバイト待ち
      if ((filled >= 3) && (checkFirstTag((unsigned char*)recv_buffer) >= 0)) {
	state = WaitHeaderRecv;
      }
    }
    if (require_size > 0) {
      continue;
    }
    if (state == WaitHeaderRecv) {
      // ヘッダのチェックサム確認
      if (checkHeaderFormat((unsigned char*)recv_buffer) < 0) {
	state = SendRetry;
      } else {
	// ヘッダからパケット情報を取得
	long recv_unique_id = getPacketUniqueId((unsigned char*)recv_buffer);
	// ユニークID を確認
	if (recv_unique_id != unique_id) {
	  state = SendRetry;
	}
	type = getPacketType((unsigned char*)recv_buffer);
	state = WaitPacketRecv;
	packet_length = getPacketLength((unsigned char*)recv_buffer);
	require_size += packet_length - NODE_ACCESS_PACKET_HEADER_SIZE;
      }

    } else if (state == WaitPacketRecv) {
      // パケットのチェックサムを確認
      if (checkPacketFormat((unsigned char*)recv_buffer, packet_length) < 0) {
	state = SendRetry;
      } else {
	state = RecvCompleted;
      }
    }
  } while (state != RecvCompleted);

  // 応答への処理
  if (type == PACKET_READ_RESPONSE) {
    // 読み出し応答への処理
    writeFromPacketData((unsigned char*)recv_buffer, (unsigned char *)tbl);
  }
  return 0;
}
