#if defined(PLATFORM_HC11) || defined(PLATFORM_DEBUG)
#ifndef PLATFORM_SLG
#include <hidef.h>
#include <stddef.h>
#include <stdtypes.h>
#include <Terminal.h>
#include <startup.h>


#endif /* ifdef PLATFORM_SLG */
#endif /* ifdef PLATFORM_HC11 */
#include <stdio.h>
#include <assert.h>

#include "commlib-leaf.h"
#include "commlib-head.h"
#include "commlib-child.h"


#ifdef PLATFORM_HC11
void leaf_send_packet(const pcdata *pkt) {
  child_write_block_to_parent((const char *)pkt,pkt->DataLength + 3*sizeof(char));
} /* leaf_send_packet */

void leaf_send_packet_args(const unsigned char EventID,
			   const unsigned char DataLength,
			   const unsigned char *Data) {
  unsigned char i;
  unsigned char checksum;

  pcdata pkt;

  /* XXX this is *so* unnecessarily inefficient --
   * we'd be a lot better off directly implementing the call to
   * child_write_block_to_parent()... */

  /* write EventID */
  pkt.EventID = EventID;
  checksum = EventID;

  /* write DataLength */
  pkt.DataLength = DataLength;
  checksum += DataLength;

  /* write payload */
  for(i=0; i<DataLength; ++i) {
    pkt.Data[i] = Data[i];
    checksum += Data[i];
  }

  /* write checksum */
  pkt.Data[DataLength] = checksum;

  /* send the packet */
  child_write_block_to_parent((const unsigned char *)&pkt,DataLength + 3*sizeof(char));
} /* leaf_send_packet */

unsigned char leaf_recv_packet(pcdata *pkt) {
  unsigned char i;
  unsigned char checksum;
  unsigned char DataLength;
  int received;

  received = child_req_block_from_parent((unsigned char *)pkt);

  /* get checksum from EventID */
  checksum = pkt->EventID;

  /* incorporate DataLength into checksum */
  DataLength = pkt->DataLength;
  checksum += DataLength;

  /* read payload */
  for(i=0; i<DataLength; ++i) {
    checksum += pkt->Data[i];
  }

  return (checksum - pkt->Data[i]);
} /* leaf_recv_packet */

unsigned char leaf_recv_packet_args(unsigned char *EventID,
			 unsigned char *DataLength,
			 unsigned char *Data) {
  unsigned char i;
  unsigned char datalen;
  unsigned char checksum;

  int received;
  pcdata pkt;

  received = child_req_block_from_parent((unsigned char *)&pkt);

  /* get checksum from EventID */
  *EventID = pkt.EventID;
  checksum = *EventID;

  /* incorporate DataLength into checksum */
  datalen = pkt.DataLength;
  *DataLength = datalen;
  checksum += datalen;

  /* read payload */
  for(i=0; i<datalen; ++i) {
    Data[i] = pkt.Data[i];
    checksum += Data[i];
  }

  return (checksum - pkt.Data[i]);
} /* leaf_recv_packet_args */

void leaf_debug(char *message) {
  int i;
  unsigned char len=0;
  unsigned char checksum;
  pcdata pkt; /* stores the ack */
  
  /* get strlen(message) */
  for(i=0; i<256 && message[i] != '\0'; ++i)
    ++len;

  /* write EventID */
  pkt.EventID = PC_EVENT_DEBUG_MESSAGE;
  checksum = PC_EVENT_DEBUG_MESSAGE;

  /* write DataLength */
  pkt.DataLength = len;
  checksum += len;

  /* write payload */
  for(i=0; i<len; ++i) {
    pkt.Data[i] = message[i];
    checksum += message[i];
  }

  /* write checksum */
  pkt.Data[i] = checksum;

  /* send packet, get ack */
  do {
    leaf_send_packet(&pkt);
    leaf_recv_packet(&pkt);
  } while(pkt.EventID == PC_EVENT_RESEND);
} /* leaf_debug */


void *leaf_fopen(const char *filename, const char *mode) {
  pcdata response;

  do {
    leaf_send_packet_args(PC_EVENT_OPEN_FILE,0,NULL);
    leaf_recv_packet(&response);
  } while (response.EventID == PC_EVENT_RESEND);
  if(response.EventID != PC_EVENT_ACK) {
    char msg[] = "leaf_fopen received bad response  ";
    msg[strlen(msg)-1] = response.EventID;
    leaf_debug(msg);
  }

  return (FILE *)filename;  /* one of {WAV_FILE, MP3_FILE} */
} /* leaf_fopen */



int leaf_fclose(void *stream) {
  /* XXX  um, do nothing? */
  leaf_debug("leaf_fclose() called");
  return 0;
} /* leaf_fclose */

size_t leaf_fread(unsigned char *ptr,
		  unsigned int size,
		  unsigned int  nitems,
		  void *stream) {
  unsigned int start,end;
  unsigned long totalsize;
  signed long size_sent; /* have to send 32-bit two's-complement, not uchar */
  pcdata pkt;
  unsigned char checksum;
  size_t total=0;

  totalsize = size * nitems;
  /* end is exclusive upper index bound: send interval [start,end) */
  for(start=0; start<totalsize; start+=255) {
    end = start + 255;
    if(end > totalsize) end = totalsize;
    size_sent = end - start;

      do {
	do {
	  leaf_send_packet_args(PC_EVENT_READ_MP3_BYTES,
				sizeof(signed long),
				(char*)&size_sent);
	  checksum = leaf_recv_packet_args(&(pkt.EventID),
					   &(pkt.DataLength),
					   &(ptr[start]));
	} while(pkt.EventID == PC_EVENT_RESEND);
	
	if(checksum==0) {
	  leaf_send_packet_args(PC_EVENT_ACK,0,NULL);
	} else {
	  leaf_send_packet_args(PC_EVENT_RESEND,0,NULL);
	}
      } while(checksum != 0);
      total += pkt.DataLength;
  }

  return total;
} /* leaf_fread */

size_t leaf_fwrite(const char *ptr, size_t size, size_t nitems, void *stream) {
  unsigned int start,end;
  unsigned char totalsize,size_sent;
  pcdata response;

  totalsize = size * nitems;
  for(start=0; start<totalsize; start+=255) {
    end = start + 255;
    if(end > totalsize) end = totalsize;
    size_sent = end - start;

    
    do {
      leaf_send_packet_args(PC_EVENT_RECEIVE_DECODED_BYTES,
			    size_sent, &(ptr[start]));
      leaf_recv_packet(&response);
    } while(response.EventID==PC_EVENT_RESEND);
    
    if(response.EventID != PC_EVENT_ACK) {
      char msg[] = "leaf_fwrite received bad response  ";
      msg[strlen(msg)-1] = response.EventID;
      leaf_debug(msg);
    }

  }

  return (totalsize);
} /* leaf_fwrite */


/* leaf_fseek is used only for fseeking output */
int leaf_fseek(void *stream, long offset, int whence) {
  pcdata response;
  int which;
  
  if(whence != SEEK_SET) {
    leaf_debug("leaf_fseek got non-SEEK_SET whence");
  }
  
  if( stream==MP3_FILE )
    which = PC_EVENT_MP3_FILE_SEEK;
  else if( stream==WAV_FILE )
    which = PC_EVENT_OUT_FILE_SEEK;
  else
    which = PC_EVENT_OUT_FILE_SEEK; /* temp */

  do {
    leaf_send_packet_args(which,
			  sizeof(offset),(char*)&offset);
    leaf_recv_packet(&response);
  } while(response.EventID==PC_EVENT_RESEND);
  if(response.EventID != PC_EVENT_ACK) {
    char msg[] = "leaf_fseek received bad response  ";
    msg[strlen(msg)-1] = response.EventID;
    leaf_debug(msg);
  }
  
  return 0;
} /* leaf_fseek */

void leaf_dct_send(unsigned char *data, unsigned int n) {
  int i;

  /* begin send */
  SEND(ADDR_C_COUT,COMM_L_START_IMDCT);
  WAIT(ADDR_C_CIN,COMM_P_START_TRANSFER);
  *ADDR_C_DDR = DATA_OUTPUT_MASK;

  /* send initial address (0) */
  SEND(ADDR_C_DATA,0);
  SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);
  WAIT(ADDR_C_CIN,COMM_P_BYTE_THERE);
  
  SEND(ADDR_C_COUT,COMM_C_NULL);
  WAIT(ADDR_C_CIN,COMM_P_NULL);

  /* send data size */
  SEND(ADDR_C_DATA,(n & 255));
  SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);
  WAIT(ADDR_C_CIN,COMM_P_BYTE_THERE);
  
  SEND(ADDR_C_COUT,COMM_C_NULL);
  WAIT(ADDR_C_CIN,COMM_P_NULL);

  SEND(ADDR_C_DATA,n / 256);
  SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);
  WAIT(ADDR_C_CIN,COMM_P_BYTE_THERE);
  
  SEND(ADDR_C_COUT,COMM_C_NULL);
  WAIT(ADDR_C_CIN,COMM_P_NULL);
  
  /* send the rest of the data */
  for(i=0; i<n; ++i) {

    SEND(ADDR_C_DATA,data[i]);
    SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);
    WAIT(ADDR_C_CIN,COMM_P_BYTE_THERE);

    SEND(ADDR_C_COUT,COMM_C_NULL);
    WAIT(ADDR_C_CIN,COMM_P_NULL);
  }
  SEND(ADDR_C_COUT,COMM_C_DONE_TRANSFER);
  *ADDR_C_DDR = DATA_RELEASE; /* do this while waiting for parent */
  WAIT(ADDR_C_CIN,COMM_P_DONE_TRANSFER);

  /* clean up */
  SEND(ADDR_C_COUT,COMM_C_NULL);
  WAIT(ADDR_C_CIN,COMM_P_NULL);
} /* leaf_dct_send */

int leaf_dct_recv(unsigned char *dest) {
  int bytes_received = 0;
  int my_address;
  int data_size, temp; /* these aren't used for anything useful, actually */

  /* begin send */
  WAIT(ADDR_C_CIN,COMM_L_DONE_IMDCT);

  *ADDR_C_DDR = DATA_INPUT_MASK;
  SEND(ADDR_C_COUT,COMM_C_START_TRANSFER); /* tell parent ready to receive */

  /* get address */
  WAIT(ADDR_C_CIN,COMM_P_BYTE_THERE);
  RECV(ADDR_C_DATA,&my_address);
  SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);

  WAIT(ADDR_C_CIN,COMM_P_NULL);
  SEND(ADDR_C_COUT,COMM_C_NULL);

  /*assert(my_address == 0);*/

  /* get data size (unused) */
  WAIT(ADDR_C_CIN,COMM_P_BYTE_THERE);
  RECV(ADDR_C_DATA,&temp);
  SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);

  WAIT(ADDR_C_CIN,COMM_P_NULL);
  SEND(ADDR_C_COUT,COMM_C_NULL);
  WAIT_NOT(ADDR_C_CIN,COMM_P_NULL);

  RECV(ADDR_C_DATA,&data_size);
  SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);

  WAIT(ADDR_C_CIN,COMM_P_NULL);
  SEND(ADDR_C_COUT,COMM_C_NULL);

  data_size = temp | (data_size * 256);

  /* get the rest of the data */
  WAIT_NOT(ADDR_C_CIN,COMM_P_NULL);
  while(*ADDR_C_CIN != COMM_P_DONE_TRANSFER) {
    /*assert(*ADDR_C_CIN == COMM_P_BYTE_THERE);*/

    RECV(ADDR_C_DATA,dest);
    SEND(ADDR_C_COUT,COMM_C_BYTE_THERE);

    WAIT(ADDR_C_CIN,COMM_P_NULL);
    SEND(ADDR_C_COUT,COMM_C_NULL);

    ++bytes_received;
    ++dest;

    WAIT_NOT(ADDR_C_CIN,COMM_P_NULL);
  }
  *ADDR_C_DDR = DATA_RELEASE;

  /* clean up */
  SEND(ADDR_C_COUT,COMM_C_DONE_TRANSFER);
  WAIT(ADDR_C_CIN,COMM_P_NULL);
  SEND(ADDR_C_COUT,COMM_C_NULL);

  return bytes_received;
} /* leaf_dct_recv */

#endif /* ifdef PLATFORM_HC11 */




#ifdef PLATFORM_DEBUG

#include "mp3.h"



#pragma DATA_SEG DEFAULT
unsigned char *mp3_ptr = MP3_DATA;


FILE *debug_fopen(const char *filename, const char *mode)
{
  return (FILE*)filename;
}



int debug_fclose(void *stream)
{
  return 0;
}

size_t debug_fread(unsigned char *ptr, size_t size, size_t nitems, void *stream)
{
  size_t nread;
  /* only used for mp3 data */


  if( (mp3_ptr + (size*nitems)) > (MP3_DATA + MP3_SIZE) )
    /* try to read past EOF */
    nread = ((MP3_DATA + MP3_SIZE)-mp3_ptr) * size;
  else nread = nitems * size;

  memcpy( ptr, mp3_ptr, nread );
  mp3_ptr += nread;

  return nread;
}

size_t debug_fwrite(const char *ptr, size_t size, size_t nitems, void *stream)
{
  return nitems;
}

int debug_fseek(void *stream, long offset, int whence)
{
 
  switch( whence ) {
  case SEEK_CUR:
    if( stream == MP3_FILE )
      mp3_ptr += offset;
    break;

  case SEEK_SET: 
    if( stream == MP3_FILE )
      mp3_ptr = MP3_DATA + offset;
    break;

  default:
    return -1;
  }

  return 0;
}

#endif
