/*
 * client.c:
 *
 * Copyright (c) 2000 James McKenzie <james@fishsoup.dhs.org>.
 * All rights reserved.
 *
 * $Log: hifivol.c,v $
 * Revision 1.1  2002/03/23 17:12:59  root
 * #
 *
 * Revision 1.2  2000/02/21 04:13:34  root
 * #
 *
 * Revision 1.1  2000/02/21 03:34:21  root
 * #
 *
 *
 */

static char rcsid[] =
    "$Id: hifivol.c,v 1.1 2002/03/23 17:12:59 root Exp root $";

#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#include <unistd.h>

#include "hifi.h"
#include "hifimsg.h"

char msg[] = "HD";

  int funit=-1;
  int fclass=-1;

void *ml;

typedef struct {
  char *str;
  int val;
  char *fmt;
} ListEnt;

#define DO_CLASS(a) { #a , HIFI_CLASS_ ## a ,"" }
ListEnt class_list[] = {
  DO_CLASS(GENERIC), DO_CLASS(TUNER), DO_CLASS(CD), DO_CLASS(MPEG),
  DO_CLASS(SYSTEM), DO_CLASS(DISPLAY), DO_CLASS(CMD), DO_CLASS(WAVE),
  DO_CLASS(CLOCK), DO_CLASS(OGG), DO_CLASS(ALL), 
  {"broadcast", HIFI_CLASS_ALL}, {NULL, 0}
};


#define DO_CMD(a,b) { #a , HIFI_CMD_ ## a,b }
#define DO_ST(a,b) { #a , HIFI_ST_ ## a,b }
ListEnt msg_list[] = {
  DO_CMD(NOOP, ""),
  DO_CMD(VOL, "Vol L:%b/100 R:%b/100"),
  DO_CMD(SL_VOL, "Vol L:%b/100 R:%b/100"),
  DO_ST(VOL, "Vol L:%b/100 R:%b/100"),
  DO_CMD(BAL, "Balance %m"),
  DO_ST(BAL, "Balance %m"),
  DO_CMD(STOP, ""),
  DO_CMD(RANDOM, ""),
  DO_CMD(PLAY, ""),
  DO_CMD(RECORD, ""),
  DO_CMD(PAUSE, ""),
  DO_CMD(EJECT, ""),

  DO_CMD(CD_PLAY, "%b.%b %b:%n"),
  DO_ST(CD_STATE, "%b.%b %b:%n %q"),
  DO_ST(CD_TKSTATE, "Track %b/%b length %w secs"),

  DO_CMD(MPEG_PLAY, "%e"),
  DO_CMD(MPEG_SEEK, "%w secs"),
  DO_CMD(MPEG_SEARCH, "%s"),
  DO_ST(MPEG_STATE, "%p %b:%n / %w secs"),
  DO_ST(MPEG_LIST, "List %b/%b %e %s"),
  DO_ST(MPEG_GPNAME, "%e %s"),
  DO_ST(MPEG_BUFFER, "%b%%"),

  DO_CMD(SYSTEM_PCM_VOL, "Vol L:%b/100 R:%b/100"),
  DO_ST(SYSTEM_PCM_VOL, "Vol L:%b/100 R:%b/100"),
  DO_CMD(SYSTEM_SPDIF, "%b"),
  DO_ST(SYSTEM_SPDIF, "%b"),
  DO_ST(SYSTEM_STATE, "%s"),
  DO_CMD(SYSTEM_SLEEP, "%w mins"),
  DO_CMD(SYSTEM_OFF,""),

  DO_CMD(TUNER_TUNE, "%t %d"),
  DO_CMD(TUNER_SEARCH_UP, ""),
  DO_CMD(TUNER_SEARCH_DOWN, ""),
  DO_CMD(TUNER_MONO, ""),
  DO_CMD(TUNER_STEREO, ""),
  DO_ST(TUNER_STATE, "%t %d Hz %u %v"),


  DO_ST(WAVE_STATE, "%y %b:%n/%w File %b"),
  DO_ST(WAVE_LIST, "%b/%b %s"),
  DO_CMD(WAVE_SOURCE, "%x"),
  DO_ST(WAVE_SOURCE, "%x"),
  DO_CMD(WAVE_PLAY, "%b"),
  DO_CMD(WAVE_SEEK, "%w secs"),

  DO_ST(CLOCK_TIME, "%s"),

  DO_ST(OGG_STATE,"%z stream %b %w/%b/%b %n:%n:%n buffer %b"),
  DO_CMD(OGG_PLAY,"stream %b %w/%b/%b %b:%b:%b"),
  DO_CMD(OGG_PLAY_S,"%i secs"),
  DO_CMD(OGG_PLAY_MS,"%i secs"),
  DO_CMD(OGG_PLAY_HMS,"%i secs"),
  DO_CMD(OGG_SEEK,"%i secs"),
  {NULL, 0, NULL}
};

ListEnt mpegstates[] = {
  {"Stopped", HIFI_MPEG_State_Stopped, ""},
  {"Paused", HIFI_MPEG_State_Paused, ""},
  {"Playing", HIFI_MPEG_State_Playing, ""},
  {"Error", HIFI_MPEG_State_Error, ""},
  {"Unknown", HIFI_MPEG_State_Unknown, ""},
  {NULL, 0, NULL}
};

ListEnt cdstates[] = {
  {"Playing", HIFI_CD_State_Playing, ""},
  {"Paused", HIFI_CD_State_Paused, ""},
  {"Stopped", HIFI_CD_State_Stopped, ""},
  {"Unknown", HIFI_CD_State_Unknown, ""},
  {NULL, 0, NULL}
};

ListEnt wavestates[] = {
  {"Stopped", HIFI_WAVE_State_Stopped, ""},
  {"Paused", HIFI_WAVE_State_Paused, ""},
  {"Playing", HIFI_WAVE_State_Playing, ""},
  {"Recording", HIFI_WAVE_State_Recording, ""},
  {"Error", HIFI_WAVE_State_Error, ""},
  {"Unknown", HIFI_WAVE_State_Unknown, ""},
  {NULL, 0, NULL}
};

ListEnt oggstates[] = {
  {"Stopped", HIFI_OGG_State_Stopped, ""},
  {"Playing", HIFI_OGG_State_Playing, ""},
  {"Error", HIFI_OGG_State_Error, ""},
  {"Unknown", HIFI_OGG_State_Unknown, ""},
  {NULL, 0, NULL}
};

ListEnt tunerbands[] = {
  {"LW", HIFI_TUNER_Band_LW, ""},
  {"MW", HIFI_TUNER_Band_MW, ""},
  {"SW", HIFI_TUNER_Band_SW, ""},
  {"FM", HIFI_TUNER_Band_FM, ""},
  {NULL, 0, NULL}
};

int a_to_number(char *s)
{
  int i;
  if ((s[0] == '0') && ((s[1] | 32) == 'x')) {
    sscanf(s + 2, "%x", &i);
    return i;
  }
  return atoi(s);
}

char *number_to_dec(int n)
{
  char buf[100];
  sprintf(buf, "%d", n);
  return strdup(buf);
}

char *number_to_hex(int n)
{
  char buf[100];
  sprintf(buf, "0x%x", n);
  return strdup(buf);
}

ListEnt *lookup_str(char *s, ListEnt * list)
{

  while (list->str) {
    if (!strcasecmp(s, list->str)) {
      return list;
    }
    list++;
  }
  return NULL;
}

ListEnt *lookup_val(int val, ListEnt * list)
{

  while (list->str) {
    if (list->val == val) {
      return list;
    }
    list++;
  }
  return NULL;
}

int str_to_val(char *s, ListEnt * list)
{
  ListEnt *le = lookup_str(s, list);

  if (!le)
    return a_to_number(s);

  return le->val;
}


char *val_to_str(int val, ListEnt * list)
{
  ListEnt *le = lookup_val(val, list);

  if (!le)
    return number_to_hex(val);

  return strdup(le->str);
}


void skip(char **s,char *stop)
{
while ((**s) && (!index(stop,**s))) (*s)++;
}
void skipi(char **s,char *over)
{
while ((**s) && (index(over,**s))) (*s)++;
}
void copyuntil(char **s,char *into,char *stop)
{
while ((**s) && (!index(stop,**s))) *(into++)=*((*s)++);

*into=0;
}

void copywhile(char **s,char *into,char *over)
{
while ((**s) && (index(over,**s))) *(into++)=*((*s)++);
*into=0;
}


int pull_number(char **s)
{
char buf[1024];

skip(s,"0123456789");
copywhile(s,buf,"0123456789");

return(atoi(buf));
}

Byte *compose(Byte * data, char *fmt, char *s)
{
  char buf[1024], *ptr = buf;
  Byte b;
  Word w;
  DWord d;
  SDWord sd;
  int i;

  if (!fmt) return data;

  if (!s) return data;


    while (*fmt) {
      if (*fmt == '%') {
        fmt++;
        switch (*fmt) {
        case 'b':
	  *(data++)=pull_number(&s);
          break;
        case 'n':
	  *(data++)=pull_number(&s);
          break;
        case 'w':
	  w=pull_number(&s);
          data=hifipkt_eword(data, w);
          break;
        case 'd':
	  d=pull_number(&s);
          data=hifipkt_edword(data, d);
          break;
        case 'i':
	  sd=pull_number(&s);
          data=hifipkt_esdword(data, sd);
          break;
        case 'm':
	  i=pull_number(&s);
	  b=(Byte) (i+128);	
          *(data++)=b;;
          break;
        case 'e':
	  {
 	  char foo[3];
	  int j;
	  skip(&s,"0123456789abcdefABCDEF");
	
  	   foo[2]=0;
	  for (j=0;j<8;++j) {
	     foo[0]=*(s++);
	     foo[1]=*(s++);
		b=strtol(foo,NULL,16);
	     *(data++)=b;
	   }
		}
          break;
        case 's':
	  skipi(&s," ");
          while (*s)
            *(data++) = *(s++);
	  *(data++)=0;
          break;
        case 'p':
	  {
	  char foo[100];
	  skipi(&s," ");
	  copyuntil(&s,foo," ");
          *(data++)=str_to_val(foo,mpegstates);
          }
          break;
        case 'q':
	  {
	  char foo[100];
	  skipi(&s," ");
	  copyuntil(&s,foo," ");
          *(data++)=str_to_val(foo,cdstates);
          }
          break;
        case 't':
	  {
	  char foo[100];
	  skipi(&s," ");
	  copyuntil(&s,foo," ");
          *(data++)=str_to_val(foo,tunerbands);
          }
          break;
        case 'y':
	  {
	  char foo[100];
	  skipi(&s," ");
	  copyuntil(&s,foo," ");
          *(data++)=str_to_val(foo,wavestates);
          }
          break;
        case 'x':
 	  *data=0;
	  if (strstr(s,"CD"))  *data|=HIFI_WAVE_Source_CD;
	  if (strstr(s,"Cd"))  *data|=HIFI_WAVE_Source_CD;
	  if (strstr(s,"cd"))  *data|=HIFI_WAVE_Source_CD;
	  if (strstr(s,"TUNER"))  *data|=HIFI_WAVE_Source_Tuner;
	  if (strstr(s,"Tuner"))  *data|=HIFI_WAVE_Source_Tuner;
	  if (strstr(s,"tuner"))  *data|=HIFI_WAVE_Source_Tuner;
	  if (strstr(s,"SPDIF"))  *data|=HIFI_WAVE_Source_SPDIF;
	  if (strstr(s,"Spdif"))  *data|=HIFI_WAVE_Source_SPDIF;
	  if (strstr(s,"spdif"))  *data|=HIFI_WAVE_Source_SPDIF;
          data++;
          break;
        default:
	  break;
        }
      }
      fmt++;
    }

  return data;
}


char *decompose(char *fmt, Byte * data, int len)
{
  char buf[1024], *ptr = buf;
  Byte b;
  Word w;
  DWord d;
  SDWord sd;
  int i;

  if (!len)
    return strdup("");
  if (!fmt) {
    for (i = 0; i < len; ++i) {
      ptr += sprintf(ptr, "0x%02x ", data[i]);
    }

  } else {

    while (*fmt) {
      if (*fmt == '%') {
        fmt++;
        switch (*fmt) {
        case '%':
          *(ptr++) = '%';
          break;
        case 'b':
          b = *(data++);
          ptr += sprintf(ptr, "%d", b);
          break;
        case 'n':
          b = *(data++);
          ptr += sprintf(ptr, "%02d", b);
          break;
        case 'w':
          data=hifipkt_dword(data, &w);
          ptr += sprintf(ptr, "%d", w);
          break;
        case 'i':
          data=hifipkt_dsdword(data, &sd);
          ptr += sprintf(ptr, "%d", sd);
	  break;
        case 'd':
          data=hifipkt_ddword(data, &d);
          ptr += sprintf(ptr, "%d", d);
          break;
        case 'm':
          b = *(data++);
          ptr += sprintf(ptr, "%d", ((int) b) - 128);
          break;
        case 'e':
          ptr += sprintf(ptr, "%02x%02x%02x%02x%02x%02x%02x%02x",
                         data[0], data[1], data[2], data[3],
                         data[4], data[5], data[6], data[7]);
          data += 8;
          break;
        case 's':
          while (*data)
            *(ptr++) = *(data++);
          break;
        case 'p':
          {
            char *st = val_to_str(*(data++), mpegstates);
            ptr += sprintf(ptr, "%s", st);
            free(st);
          }
          break;
        case 'q':
          {
            char *st = val_to_str(*(data++), cdstates);
            ptr += sprintf(ptr, "%s", st);
            free(st);
          }
          break;
        case 't':
          {
            char *st = val_to_str(*(data++), tunerbands);
            ptr += sprintf(ptr, "%s", st);
            free(st);
          }
          break;
        case 'u':
          if (data[0]) {
            ptr += sprintf(ptr, "Locked");
          } else {
            ptr += sprintf(ptr, "Unlocked");
          }
          data++;
          break;
        case 'v':
          if (data[0]) {
            ptr += sprintf(ptr, "Stereo");
          } else {
            ptr += sprintf(ptr, "Mono");
          }
          data++;
          break;
        case 'y':
          {
            char *st = val_to_str(*(data++), wavestates);
            ptr += sprintf(ptr, "%s", st);
            free(st);
          }
          break;
        case 'x':
          if (data[0] & HIFI_WAVE_Source_CD)
            ptr += sprintf(ptr, "CD ");
          if (data[0] & HIFI_WAVE_Source_Tuner)
            ptr += sprintf(ptr, "Tuner ");
          if (data[0] & HIFI_WAVE_Source_SPDIF)
            ptr += sprintf(ptr, "SPDIF ");
          data++;
          break;
	case 'z':
	    {
            char *st = val_to_str(*(data++), oggstates);
            ptr += sprintf(ptr, "%s", st);
            free(st);
	    }
	    break;
        default:
          *(ptr++) = '%';
          *(ptr++) = *fmt;
        }
      } else {
        *(ptr++) = *fmt;
      }
      fmt++;
    }
  }
  *ptr = 0;

  return strdup(buf);
}


Byte a_to_class(char *s)
{
  return str_to_val(s, class_list);
}

char *class_to_a(Byte c)
{
  ListEnt *le = lookup_val(c, class_list);
  if (le)
    return strdup(le->str);
  return number_to_hex(c);
}


char *unit_to_a(Byte c)
{
  switch (c) {
  case 255:
    return strdup("broadcast");
    break;
  }

  return number_to_dec(c);
}

Byte a_to_unit(char *s)
{
  char *ret;

  if (!strcasecmp(s, "broadcast"))
    return 255;
  if (!strcasecmp(s, "all"))
    return 255;

  return a_to_number(s);
}

char *cmd_to_a(Word cmd)
{
  char *ret, *ptr;
  ListEnt *le = lookup_val(cmd, msg_list);
  if (le)
    return strdup(le->str);

  ptr = class_to_a(HIFI_STCMD_CLASS(cmd));
  ret = malloc(strlen(ptr) + 20);

  if (HIFI_IS_ST(cmd)) {
    strcpy(ret, "ST(");
  } else {
    strcpy(ret, "CMD(");
  }

  strcat(ret, ptr);
  free(ptr);

  sprintf(ret + strlen(ret), ",0x%02x)", cmd & 0x7f);

  return ret;
}

Word a_to_cmd(char *a)
{
  return str_to_val(a, msg_list);
}

void timeout(int signo)
{
  //This alerts the hub to our presence
  hifi_send(ml, msg, sizeof(msg));
}


void callback(Byte sclass, Byte sunit, Byte dclass, Byte dunit, Word cmd,
              Byte * data, Word l)
{
  char *f;
  ListEnt *le;




	  if ((fclass<0)  || (sclass==fclass) || (dclass==fclass)) {
	  if ((funit<0)  || (sunit==funit) || (dunit==funit)) {
  char *s = class_to_a(sclass);
  char *d = class_to_a(dclass);
  char *c = cmd_to_a(cmd);
  char *su = unit_to_a(sunit);
  char *du = unit_to_a(dunit);

  printf("\t%s.%s->%s.%s %s (%d bytes)\n", s, su, d, du, c, l);

  free(du);
  free(su);
  free(c);
  free(d);
  free(s);

  le = lookup_val(cmd, msg_list);

  f = decompose(le ? le->fmt : NULL, data, l);

  printf("\t\t");
  puts(f);

}
}

}


void send_cmd(int argc, char *argv[], int cmdunit)
{
  char buf[2048], *ptr;
  Byte *pkt, *pptr, *cptr;
  Byte dclass, dunit;
  Word cmd;
  ListEnt *le;

/*Destination*/
  strcpy(buf, *(argv++)), argc--;
  ptr = index(buf, '.');

  if (ptr) {
    *ptr = 0;
    dclass = a_to_class(buf);
    dunit = a_to_unit(ptr + 1);
  } else {
    dclass = a_to_class(buf);
    dunit = 255;
  }


  ptr = *(argv++), argc--;
  if (!strncasecmp(ptr, "hifi_", 5))
    ptr += 5;
  if (!strncasecmp(ptr, "cmd_", 4))
    ptr += 4;

  le = lookup_str(ptr, msg_list);
  cmd = a_to_cmd(ptr);


  buf[0] = 0;
  while (argc--) {
    strcat(buf, *(argv++));
    strcat(buf, " ");
  }

  pptr = pkt = hifi_getbuf();
  pptr = hifipkt_new_rel(pptr, HIFI_CLASS_CMD, cmdunit, dclass, dunit);
  pptr = hifipkt_ecmd(cptr = pptr, cmd);
  pptr = compose(pptr, le ? le->fmt : NULL, buf);
  hifipkt_elen(cptr, HIFI_PTR_DIFF(pptr, cptr));
  pptr = hifipkt_eeof(pptr);

  hifi_new_packet (ml,pkt, HIFI_PTR_DIFF(pptr, pkt),callback);

  hifi_send(ml, pkt, HIFI_PTR_DIFF(pptr, pkt));


  hifi_freebuf(pkt);
  hifimsg_close(ml);

  exit(0);
}

int main(int argc, char *argv[])
{
  char buf[1024],*ptr;
  int len;
  struct timeval tv;
  struct itimerval itime;
  int cmdunit = getpid() % 255;


#if 0
  if (argc == 2) {
    fprintf(stderr, "Usage:\n");
    fprintf(stderr, " %s                               dump packets\n",
            argv[0]);
    fprintf(stderr, " %s destination command [args]    send a command\n\n",
            argv[0]);
    fprintf(stderr, "destination is something like mpeg.32\n");
    fprintf(stderr, "  or broadcast.all\n");
    fprintf(stderr,
            "command is a command name (you may omit HIFI_CMD_)\n");
    exit(1);
  }
#endif

  ml = hifi_open(HIFI_CLASS_CMD, cmdunit);



  if (argc > 2)
    send_cmd(argc - 1, argv + 1, cmdunit);

  if (argc==2) {

/*Destination*/
  strcpy(buf, argv[1]);
  ptr = index(buf, '.');

  if (ptr) {
    *ptr = 0;
    fclass = a_to_class(buf);
    funit = a_to_unit(ptr + 1);
  } else {
    fclass = a_to_class(buf);
  }
  }


  signal(SIGALRM, timeout);

  itime.it_value.tv_sec = 2;
  itime.it_value.tv_usec = 0;

  itime.it_interval.tv_sec = 2;
  itime.it_interval.tv_usec = 0;
  setitimer(ITIMER_REAL, &itime, NULL);


  while (1) {
    Word proto;
    len = hifi_recv(ml, buf, sizeof(buf));
    gettimeofday(&tv, NULL);

    if (len) {

      hifipkt_dword(buf, &proto);
      if (proto == HIFI_MAGIC) {

        {
          char *s;
          char *d;
          char *su;
          char *du;
          Word w;

          d = class_to_a(buf[2]);
          du = unit_to_a(buf[3]);
          s = class_to_a(buf[4]);
          su = unit_to_a(buf[5]);
          hifipkt_dword(&buf[6], &w);


	  if ((fclass<0)  || (buf[2]==fclass) || (buf[4]==fclass)) {
	  if ((funit<0)  || (buf[3]==funit) || (buf[5]==funit)) {


          printf("%d.%06d: %s.%s->%s.%s %s %s Seq %d, %d bytes\n",
                 (int) tv.tv_sec, (int) tv.tv_usec, s, su, d, du,
                 w & HIFI_SEQUENCE_REL ? "Reliable" : "Unreliable",
                 w & HIFI_SEQUENCE_ACK ? "Ack" : "",
                 w & HIFI_SEQUENCE_MASK, len);

	}
	}

          free(su);
          free(s);
          free(du);
          free(d);


        }

        hifi_new_packet(ml, buf, len, callback);

        //hifimsg_dumpbytes (buf, len);
        //printf ("\n");
      } else {
        extern void hifimsg_dumpbytes(void *, int);
        printf("%d.%06d: Unknown protocol 0x%04x\n",
               (int) tv.tv_sec, (int) tv.tv_usec, proto);

        hifimsg_dumpbytes(buf, len);
        printf("\n\n");
      }
    } else {
      perror("recv");
    }

  }

}
