#include "snd.h"

int round(float x)
{
  int i;
  i=x;
  if ((x-i) > 0.5) return(i+1);
  return(i);
}

char *copy_string (char *str)
{
  char *newstr = NULL;
  if (str)
    {
      newstr = (char *)calloc(strlen(str)+1,sizeof(char));
      strcpy(newstr,str);
    }
  return(newstr);
}

int snd_strlen(char *str)
{
  /* strlen(NULL) -> seg fault! */
  if ((str) && (*str)) return(strlen(str));
  return(0);
}
      
void snd_error (char *msg)
{
  fprintf(stderr,msg);
}

char *filename_without_home_directory(char *name)
{
  /* since we don't want to mess with freeing these guys, I'll just return a pointer into the name */
  /* and for now, to heck with home */
  int i,len,last_slash;
  last_slash = 0;
  len = strlen(name);
  for (i=0;i<len-1;i++) if (name[i] == '/') last_slash = i+1;
  return((char *)(name+last_slash));
}

char *just_filename(char *name)
{
  char *nodir;
  int i,len;
  nodir = copy_string(filename_without_home_directory(name));
  len = strlen(nodir);
  for (i=0;i<len;i++) if (nodir[i] == '.') {nodir[i] = '\0'; break;}
  return(nodir);
}

#ifndef sqr
float sqr(float a) {return(a*a);}
#endif

float cube (float a) {return(a*a*a);}

static char prtbuf[512];

char *prettyf(float num, int tens)
{ /* try to prettify float display -- if tens<0, return int */
  int fullf,len,i;
  float rounder;
  char *newval,*sp,*sn,*zp;
  zp=NULL;
  if (num < 0.0) rounder = -.49; else rounder = .49;
  if (tens < 0)
    {
      fullf=num+rounder;
      sprintf(prtbuf,"%d",fullf);
      len=strlen(prtbuf);
      newval=(char *)calloc(len+1,sizeof(char));
      strcpy(newval,prtbuf);
      return(newval);
    }
  fullf = num;
  if ((num-fullf) == 0.0) 
    {
      if (num<100.0)
	sprintf(prtbuf,"%d%c0",fullf,snd_string_decimal);
      else sprintf(prtbuf,"%d",fullf);
      len=strlen(prtbuf);
      newval=(char *)calloc(len+1,sizeof(char));
      strcpy(newval,prtbuf);
      return(newval);
    }
  if (num > 1000)
    {
      sprintf(prtbuf,"%d%c%d",fullf,snd_string_decimal,(int)((num-fullf)*pow(10,tens)));
      len=strlen(prtbuf);
      newval=(char *)calloc(len+1,sizeof(char));
      strcpy(newval,prtbuf);
      return(newval);
    }
  fullf = num*pow(10,tens+1)+rounder;
  if (fullf == 0) 
    {
      /* will be freed later, so can't return a constant */
      newval=(char *)calloc(2,sizeof(char));
      newval[0] = '0';
      newval[1] = '\0';
      return(newval);
    }
  sprintf(prtbuf,"%d",fullf);
  len=strlen(prtbuf);
  newval=(char *)calloc(len+10,sizeof(char));
  sn = newval;
  sp = prtbuf;
  if ((*sp) == '-') {(*sn) = (*sp); sn++; sp++; len--;}
  if (len >= (tens+1))
    {
      for (i=0;i<len-tens-1;i++) {(*sn)=(*sp); sn++; sp++;}
      (*sn)=snd_string_decimal; sn++;
    }
  else
    {
      (*sn) = '0';
      sn++;
      (*sn) = snd_string_decimal;
      sn++;
      for (i=0;i<abs(len-tens-1);i++) 
	{
	  (*sn) = '0';
	  sn++;
	}
    }
  if (tens > 5) tens = 5;
  for (i=0;i<=tens;i++) 
    {
      if (!(*sp)) break;
      (*sn)=(*sp); 
      if ((!zp) || ((*sn) != '0')) zp = sn;
      sn++; 
      sp++;
    }
  if (zp) {sn=zp;} else {(*sn)='0';}
  sn++;
  (*sn) = '\0';
  return(newval);
}

int disk_space_p(snd_info *sp, int fd, int bytes, int other_bytes)
{
  int kfree,kneeded,kother,go_on;
  kfree = disk_kspace(fd);
  if (kfree < 0) {sprintf(prtbuf,strerror(errno)); report_in_minibuffer(sp,prtbuf); return(NO_PROBLEM);}
  kneeded = bytes >> 10;
  if (kfree < kneeded)
    {
      if (other_bytes > 0)
	{
	  kother = other_bytes >> 10;
	  if (kother > kfree)
	    {
	      sprintf(prtbuf,snd_string_no_room_but_we_try,kfree<<10);
	      report_in_minibuffer(sp,prtbuf);
	      return(HUNKER_DOWN);
	    }
	}
      sprintf(prtbuf,snd_string_no_room_go_on_p,kfree<<10);
      go_on = snd_yes_or_no_p(sp->state,prtbuf);
      if (!go_on) return(GIVE_UP);
      report_in_minibuffer(sp,snd_string_going_on);
      return(BLIND_LEAP);
    }
  return(NO_PROBLEM);
}

int snd_checked_write(snd_state *ss, int fd, unsigned char *buf, int bytes)
{
  /* clm checked_write assumes clm descriptors are around and writes errors to stderr */
  int bytes_written,kfree;
  kfree = disk_kspace(fd);
  if (kfree < 0) {sprintf(prtbuf,strerror(errno)); snd_printf(ss,prtbuf); return(-1);}
  if (kfree < (bytes>>10))
    {
      sprintf(prtbuf,snd_string_no_room,kfree<<10,bytes);
      snd_yes_or_no_p(ss,prtbuf);
      return(-1);
    }
  bytes_written = write(fd,buf,bytes);
  if (bytes_written != bytes)
    {
      sprintf(prtbuf,snd_string_write_failed,strerror(errno));
      snd_printf(ss,prtbuf);
      return(-1);
    }
  return(bytes_written);
}


/* check memory allocation -- is this actually useful?  Widgets do allocations that remain
 * unchecked.  I'll use these only in very large cases, assuming they're 100 to 1000 times
 * more likely to hit such an error.  And it's not clear we can recover cleanly anyway.
 */

static int find_any_edits (chan_info *cp, void *ptr) {return(cp->edit_ctr);}

#ifndef _DEBUG_MALLOC_INC
void *snd_calloc(snd_state *ss, size_t num, size_t size)
{
  void *ptr;
  int save,pending_edits;
  ptr = calloc(num,size);
  if (ptr == NULL) 
    {
      fprintf(stderr,"memory allocation failed!"); /* get message out before checking for edits */
      snd_printf(ss,"memory allocation failed!");
      pending_edits = map_over_chans(ss,find_any_edits,NULL);
      if (pending_edits)
	{
	  save = snd_yes_or_no_p(ss,"Out of memory! Try to save current edits?");
	  if (save) map_over_sounds(ss,save_edits,NULL);
	}
    }
  return(ptr);
}

void *snd_realloc(snd_state *ss, void *original_ptr, size_t size)
{
  int save,pending_edits;
  void *ptr;
  ptr = realloc(original_ptr,size);
  if (ptr == NULL) 
    {
      fprintf(stderr,"memory re-allocation failed!"); /* get message out before checking for edits */
      snd_printf(ss,"memory re-allocation failed!");
      pending_edits = map_over_chans(ss,find_any_edits,NULL);
      if (pending_edits)
	{
	  save = snd_yes_or_no_p(ss,"Out of memory! Try to save current edits?");
	  if (save) map_over_sounds(ss,save_edits,NULL);
	}
    }
  return(ptr);
}
#endif

void fill_number(char *fs, char *ps)
{
  int i,j;
  j=snd_strlen(fs);
  if (j>4) j=4;
  if (j<4) {ps[4] = '\0'; ps[3]='0'; ps[2]='0'; ps[1]=snd_string_decimal;}
  if ((*fs) == snd_string_decimal) {*ps++ = '0'; if (j==4) j=3;}
  for (i=0;i<j;i++) (*ps++) = (*fs++);
}

static int sect_ctr = 0;
char *shorter_tempnam(char *dir,char *prefix)
{
  /* tempnam turns out names that are inconveniently long */
  char *str;
  str = (char *)calloc(256,sizeof(char));
#ifndef VMS
  if ((dir) && (*dir))
    sprintf(str,"%s/sect-%d.snd",dir,sect_ctr++);
  else sprintf(str,"/var/tmp/sect-%d.snd",sect_ctr++);
#else
  if ((dir) && (*dir))
    sprintf(str,"%ssect-%d.snd",dir,sect_ctr++);
  else sprintf(str,"sys$scratch:sect-%d.snd",sect_ctr++);
#endif
  return(str);
}

char *snd_tempnam(char *dir,char *prefix)
{
  /* problem here is that NULL passed back from Guile becomes "" which is not NULL from tempnam's point of view */
#ifndef VMS
  if ((dir) && (*dir))
    return(tempnam(dir,prefix));
  return(tempnam(NULL,prefix));
#else
  return(copy_string(tmpnam(NULL)));
#endif
}


#if HAVE_OSS
  #include <sys/ioctl.h>
  #ifdef LINUX
    #include <sys/soundcard.h>
  #else
    #include <soundcard.h>
  #endif

void clear_soundcard_inputs(void)
{
  /* turn off inputs (they create an unbelievable amount of noise) and maximize outputs */
  int fd,amp,devmask;
  fd = open("/dev/dsp",O_WRONLY,0);
  if (fd == -1) return;
  amp = 0;
  ioctl(fd,SOUND_MIXER_READ_DEVMASK,&devmask);
  if (SOUND_MASK_MIC & devmask) ioctl(fd,MIXER_WRITE(SOUND_MIXER_MIC),&amp);
  if (SOUND_MASK_IGAIN & devmask) ioctl(fd,MIXER_WRITE(SOUND_MIXER_IGAIN),&amp);
  if (SOUND_MASK_LINE & devmask) ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE),&amp);
  amp = (99<<8) + 99;
  if (SOUND_MASK_VOLUME & devmask) ioctl(fd,MIXER_WRITE(SOUND_MIXER_VOLUME),&amp);
  if (SOUND_MASK_OGAIN & devmask) ioctl(fd,MIXER_WRITE(SOUND_MIXER_OGAIN),&amp);
  if (SOUND_MASK_PCM & devmask) ioctl(fd,MIXER_WRITE(SOUND_MIXER_PCM),&amp);
  close(fd);
}
#endif
