#include <aes.h>
#include <vdi.h>
#include <gemdefs.h>
#include <osbind.h>
#include <stdlib.h>
#include <errno.h>
#include <scancode.h>

int play_sam( void *adr, void *end, long hertz, long hold, int dma );

typedef struct view
{
  int	handle, s1;
  int	lbc,			/* left-border-count fr Fenster.	*/
	tlc,			/* top-line-count fr Fenster.		*/
	bc,			/* border-count fr Fenster.		*/
	lc,			/* line-count fr Fenster.		*/
	xfac, yfac,		/* Scroll- und Rastereinheiten.		*/
	x, y, w, h,		/* Arbeitsbereich des Fensters.		*/
	hslide, vslide,		/* Puffer fr Sliderpositionen.		*/
	(*key)( struct view *tv, int code, int ks );
  void	(*draw)( struct view *tv, int *clip ),
	(*free)( struct view *tv ),
	(*sclick)( struct view *tv, int mx, int my, int flag );
  char	path[128];		/* Datei-Pfad (Fenstertitel).		*/
  struct view *next;		/* Zeiger auf nchste Fensterstruktur.	*/
  struct view *prev;		/* Zeiger auf vorige Fensterstruktur.	*/
  BASPAG *actpd;		/* Zeiger auf Prozess-Descriptor.	*/

  int   dma, data;
  long	dw, dh, hertz;
  char	*start, *end,
	buf[];			/* Sample-Buffer.			*/
}
VIEW;

#include "1stview.h"
#include "util.h"

static int  dmaflag = 0;
static char samstring[] = "---       Hertz ---",
	    hertzstring[] = "       ",
	    wstring[] = "  Fenster       ^E",
	    ostring[] = "  Original      ^S",
	    normstring[] = "  Norm ^N ",
	    dmastring[] = "  DMA ^M ";

#pragma warn -rpt
static OBJECT popup[] =
{
  0,  1, 10, G_BOX,    NONE,   SHADOWED, 0xFF1100L,		0, 0, 19,7,
  2, -1, -1, G_STRING, NONE,   DISABLED, samstring,		0, 0, 19,1,
  3, -1, -1, G_STRING, NONE,   NORMAL,   wstring,		0, 1, 19,1,
  4, -1, -1, G_BUTTON, NONE,   NORMAL,   "- ^\004",		0, 2, 6, 1,
  5, -1, -1, G_BUTTON, NONE,   DISABLED, hertzstring,		6, 2, 7, 1,
  6, -1, -1, G_BUTTON, NONE,   NORMAL,   "+ ^\003",		13,2, 6, 1,
  7, -1, -1, G_STRING, NONE,   NORMAL,   ostring,		0, 3, 19,1,
  8, -1, -1, G_STRING, NONE,   DISABLED, "----- P L A Y -----", 0, 4, 19,1,
  9, -1, -1, G_BUTTON, NONE,   NORMAL,   normstring,		0, 5, 10,1,
 10, -1, -1, G_BUTTON, NONE,   DISABLED, dmastring,		10,5, 9, 1,
  0, -1, -1, G_STRING, LASTOB, NORMAL,   "      STOP      ^T ",	0, 6, 19,1
};
#pragma warn +rpt

static void check_dma( void )
{
#ifdef __TOS__
  void *oldstack;
  long *cookiejar;

  oldstack = (void *)Super( 0 );
  cookiejar = *(long **)0x5A0;
  if (cookiejar)
  {
    while (*cookiejar)
    {
      if (*cookiejar == '_SND')
      {
	if (cookiejar[1] & 2L) popup[9].ob_state = NORMAL;
	break;
      }
      cookiejar += 2;
  } }
  Super( oldstack );
#endif
}

static void change_hz( VIEW *tv, int flag )
{
#ifdef __TOS__
  void *oldstack;

  oldstack = (void *)Super( 0 );
  if (tv->dma)
  {
    flag += tv->data;
    if (flag < 0x80) flag = 0x80; if (flag > 0x83) flag = 0x83;
    *(int *)0xFFFF8920L = flag;
  }
  else
  {
    if ((flag = tv->data - flag) <= 0) flag = 1;
    if (flag > 255) flag = 255;
    *(char *)0xFFFFFA1FL = flag;
  }
  Super( oldstack ); tv->data = flag;
#endif
}

static void new_item( VIEW *tv, int new )
{
  if (new == 2)
  {
    if (tv->dw == tv->w && tv->dh == tv->h) return;
    tv->dw = tv->w; tv->dh = tv->h;
  }
  else
  {
    if (tv->dw == tv->end - tv->start && tv->dh == 256) return;
    tv->dw = tv->end - tv->start; tv->dh = 256;
  }
  tv->bc = (int)((tv->dw + 31) >> 5); tv->lc = (int)((tv->dh + 7) >> 3);
  new_redraw();
}

static int *pline( int *pxy )
{
  VDI( 6, (int)(pxy - ptsin) >> 1, 0, handle ); return ptsin;
}

static void draw_sample( VIEW *tv, int *clip )
{
  int  *pxy;
  char *p, *q;
  long len;
  int  dx, dy;

  vr_recfl( handle, clip ); vs_clip( handle, 1, clip ); pxy = ptsin;
  q = p = tv->start; len = tv->end - p; dy = tv->y - (tv->tlc << 3);
  if (tv->dw == len)
  {
    p += ((long)tv->lbc << 5) + clip[0] - tv->x;
    q = p; q += clip[2] - clip[0]; if (++q > tv->end) q = tv->end;
    dx = clip[0] - 1; if (--p < tv->start) { ++p; ++dx; }
    while (p < q)
    {
      *pxy++ = dx++;
      *pxy++ = dy + (int)(((127 - *p++) * tv->dh) >> 8);
      if (pxy == ptsin + 128)
      {
	pxy = pline( pxy ); *(long *)pxy = *(long *)(pxy + 126); pxy += 2;
  } } }
  else
  {
    dx = tv->x - (tv->lbc << 5);
    p += ((clip[0] - 1 - dx) * len) / tv->dw;
    q += ((clip[2] + 1 - dx) * len) / tv->dw;
    if (p < tv->start) p = tv->start; if (q > tv->end) q = tv->end;
    while (p < q)
    {
      *pxy++ = dx + (int)(((p - tv->start) * tv->dw) / len);
      *pxy++ = dy + (int)(((127 - *p++) * tv->dh) >> 8);
      if (pxy == ptsin + 128)
      {
	pxy = pline( pxy ); *(long *)pxy = *(long *)(pxy + 126); pxy += 2;
  } } }
  pline( pxy ); vs_clip( handle, 0, clip );
}

static void free_sample( VIEW *tv )
{
#ifdef __TOS__
  play_sam( tv->start, tv->start, tv->hertz, 0, tv->dma );
#endif
}

static void do_play( VIEW *tv )
{
  tv->dma = dmaflag;
#ifdef __TOS__
  tv->data = play_sam( tv->start, tv->end, tv->hertz, 1, tv->dma );
#else
  tv->data = (int)(614400L / tv->hertz);
#endif
}

#pragma warn -par
static int key_sample( VIEW *tv, int code, int ks )
{
  switch (code)
  {
    case CNTRL_E:  new_item( tv, 2 ); return 0;
    case CNTRL_S:  new_item( tv, 6 ); return 0;
    case CNTRL_M:  if (popup[9].ob_state == NORMAL)
		     { free_sample( tv ); dmaflag = 1; do_play( tv ); }
		   return 0;
    case CNTRL_N:  free_sample( tv ); dmaflag = 0; do_play( tv ); return 0;
    case CNTRL_T:  free_sample( tv ); return 0;
    case CNTRL_CL: change_hz( tv, -1 ); return 0;
    case CNTRL_CR: change_hz( tv, 1 ); return 0;
  }
  return 1;
}
#pragma warn +par

static void sclick_sample( VIEW *tv, int mx, int my, int flag )
{
  long value, count;
  char *string;

  if (flag) return;
  value = tv->hertz; count = 6; string = samstring + 9;
  do *--string = '0' + value % 10;
  while (--count && (value /= 10) != 0);
  while (--count > 0) *--string = ' ';
  if (tv->dma == 0) value = 614400L / tv->data;
  else switch (tv->data)
       {
	 case 0x80: value =  6258L; break;
	 case 0x81: value = 12517L; break;
	 case 0x82: value = 25033L; break;
	 default:   value = 50066L;
       }
  ltoa( value, hertzstring, 10 );
  *wstring = tv->dw == tv->w && tv->dh == tv->h ? 8 : ' ';
  *ostring = tv->dw == tv->end - tv->start && tv->dh == 256 ? 8 : ' ';
  *normstring = tv->dma ? ' ' : 8; *dmastring = tv->dma ? 8 : ' ';
  switch (popup_menu( popup, 4, mx, my, objc_draw ))
  {
    case  2: new_item( tv, 2 ); break;
    case  3: change_hz( tv, -1 ); break;
    case  5: change_hz( tv, 1 ); break;
    case  6: new_item( tv, 6 ); break;
    case  8: free_sample( tv ); dmaflag = 0; do_play( tv ); break;
    case  9: free_sample( tv ); dmaflag = 1; do_play( tv ); break;
    case 10: free_sample( tv );
} }

VIEW *load_sample( int fh, long len )
{
  VIEW *rv;
  char *stop, *start;

  if ((rv = Malloc( sizeof(VIEW) + len )) == 0)
    { Fclose( fh ); form_error( EINVMEM ); return 0; }
  rv->hertz = par.hertz; start = rv->buf; rv->end = stop = start + len;
  Fread( fh, len, start ); Fclose( fh );
  if (popup->ob_next == 0)
    { --popup->ob_next; fix_tree( popup, 10 ); check_dma(); }
#ifdef __TOS__
  if (*(long *)start == 0xAABBCCDDL)		/* SoundMerlin Format */
#else
  if (*(long *)start == 0xDDCCBBAAL)
#endif
  {
    rv->hertz = 10U * ((unsigned *)start)[10]; stop = start += 50;
  }
#ifdef __TOS__
  else if (*(long *)start == 0x0000AB12L)	/* SoundMachine Format */
#else
  else if (*(long *)start == 0x12AB0000L)
#endif
  {
    rv->hertz = 13000; stop = start += 34;
  }
#ifdef __TOS__
  else if ((*(long *)start & 0xFFFFFF00L) == 'JON\0')		/* ??? */
#else
  else if ((*(long *)start & 0x00FFFFFFL) ==
	   ((long)'N' << 16) + ((long)'O' << 8) + 'J')
#endif
  {
    rv->hertz = 1000U * ((unsigned char *)start)[3]; start += 4;
  }
  while (stop > start) *--stop -= 128;
  rv->start = start; rv->xfac = 32; rv->yfac = 8;
  rv->w = 512; rv->h = 256; rv->dh = 256; rv->lc = 32;
  rv->dw = rv->end - start; rv->bc = (int)((rv->dw + 31) >> 5);
  rv->draw = draw_sample; rv->free = free_sample;
  rv->key = key_sample; rv->sclick = sclick_sample;
  do_play( rv ); return rv;
}