#include <stdio.h>

#include "commlib-common.h"
#include "commlib-head.h"
#include "commlib-parent.h"

#define MAX_LEAVES 256
#define MAX_FILES 2

/* GLOBAL DATA */
char g_addrmap_used[MAX_LEAVES];   /* which mappings have been initialized */
char g_addrmap_leafpc[MAX_LEAVES]; /* mapping from addrs to FSes */
char g_addrmap_pcleaf[MAX_LEAVES]; /* mapping from FSes to addrs */


int main(int argc, char **argv) {
  int i;
  int addr = 0;
  int last_addr = 0;
  char highest = 0; /* number of highest FS */
  pcdata pkt;
  pcdata response;

  /*** INITIALIZE ***/
  *((char*)0x003f) |= 0x4; /* disable COP system */
  commlib_init();
  DisableInterrupts; /* if we don't do this, we can't use serial port
		      * (right?)*/

  head_read_pkt_from_pc(&pkt); /* get initial ACK */
  head_debug("head app got initial ACK; starting...");

  /* say how many files we're going to need
   * and send request to open applicable files */
  head_file_select(MAX_FILES);
  head_write_pkt_to_pc_args(PC_EVENT_OPEN_FILE,0,NULL);

  /* initialize address maps */
  for(i=0; i<MAX_LEAVES; ++i) {
    g_addrmap_used[i]   = 0;
    g_addrmap_leafpc[i] = 0;
    g_addrmap_pcleaf[i] = 0;
  }

  /*** ENTER MAIN LOOP ***/
  while(1) { /* main loop */

    /* get block from child */
    parent_read_block_from_child((char *)&pkt,&addr);
    
    /* establish mapping if it's not already established */
    if(!g_addrmap_used[addr]) {
      /* this is the first time this address has been used --
       * set up the mapping */
      g_addrmap_used[addr]      = 1;
      g_addrmap_leafpc[addr]    = highest;
      g_addrmap_pcleaf[highest] = addr;
      ++highest;
    }

    /* make sure PC is communicating with correct file */
    if(addr != last_addr) {
      head_file_select(g_addrmap_leafpc[addr]);
      last_addr = addr;
    }

    /* read and get response */
    switch(pkt.EventID) {

      /* special case: just send an ACK immediately for fopens,
       * since the protocol specifies that only one fopen ever get sent
       * to the pc */
    case PC_EVENT_OPEN_FILE:
      pkt.EventID = PC_EVENT_ACK;
      pkt.DataLength = 0;
      parent_write_block_to_child((char *)&pkt,3,addr);
      break;

      /* request-response messages */
    case PC_EVENT_PLAY_REQUEST:
    case PC_EVENT_RECEIVE_DECODED_BYTES:
    case PC_EVENT_DEBUG_MESSAGE:
    case PC_EVENT_MP3_FILE_SEEK:
    case PC_EVENT_OUT_FILE_SEEK:
    case PC_EVENT_SHUTDOWN:
    case PC_EVENT_FILE_STATS_QUERY:
    case PC_EVENT_RESEND:
    case PC_EVENT_FILE_SELECT:
      
      head_write_pkt_to_pc(&pkt);
      i = head_read_pkt_from_pc(&response);
      parent_write_block_to_child((char *)&response,
				  3+response.DataLength,addr);
      break;
      
      /* request-response-response messages */
    case PC_EVENT_READ_MP3_BYTES:

      head_write_pkt_to_pc(&pkt);

      do {
	i = head_read_pkt_from_pc(&response);
	if(response.EventID == PC_EVENT_RESEND)
	  head_debug("got 'impossible' resend event after read_mp3_bytes");
	parent_write_block_to_child((char *)&response,
				    3+response.DataLength,addr);

	parent_read_block_from_child((char *)&pkt,&addr);
	if(pkt.EventID == PC_EVENT_RESEND)
	  head_debug("head: sending resend event from leaf...");
	else if(pkt.EventID == PC_EVENT_UNKNOWN)
	  head_debug("head: sending 'unknown' event from leaf...");
	head_write_pkt_to_pc(&pkt);
      } while(pkt.EventID == PC_EVENT_RESEND);
      
      
      break;
      
    default:
      head_debug("Warning: Head got unexpected packet from board:");

      head_write_pkt_to_pc(&pkt);
      head_read_pkt_from_pc(&response);
      if(response.EventID != PC_EVENT_ACK) {
	head_debug("Got unexpected response from PC");
      }
      break;

    }
#if 0
    head_write_pkt_to_pc(&pkt);
    head_read_pkt_from_pc(&pkt);

    while(1) {
      if(pkt.EventID == PC_EVENT_ACK) break;
      head_write_pkt_to_pc(&pkt);
      if(pkt.EventID == PC_EVENT_ACK) break;      
      head_read_pkt_from_pc(&pkt);      
    }
#endif
  } /* main loop */

  /* we should never be here, but CHC11 wants a return value anyway */
  return 0;
} /* main */

