#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "mpg123.h"
#include "common.h"

static int get_fileinfo(struct reader *,char *buf);


static int default_init(struct reader *rds)
{
  char buf[128];

  rds->filepos = 0;
  rds->filelen = get_fileinfo(rds,buf);
  
  if(rds->filelen > 0) {
    if(!strncmp(buf,"TAG",3)) {
      rds->flags |= READER_ID3TAG;
      memcpy(rds->id3buf,buf,128);
    }
  }
  return 0;
}

void stream_close(struct reader *rds)
{
    if (rds->flags & READER_FD_OPENED)
        rds->filept->close(rds->filept);

    rds->filept=NULL;
    rds->flags&=~READER_FD_OPENED;
}

/**************************************** 
 * HACK,HACK,HACK: step back <num> frames 
 * can only work if the 'stream' isn't a real stream but a file
 */
static int stream_back_bytes(struct reader *rds,int bytes)
{
  if(rds->filept->seek(rds->filept,-bytes,SEEK_CUR) < 0)
    return -1;
  return 0;
}

static int stream_back_frame(struct reader *rds,struct frame *fr,int num)
{
	int32_t bytes;
	unsigned char buf[4];
	u_int32_t newhead;

	if(!firsthead)
		return 0;

	bytes = (fr->framesize+8)*(num+2);

	/* Skipping back/forth requires a bit more work in buffered mode. 
	 * See mapped_back_frame(). 
	 */
/*
		bytes += (int32_t)(compute_buffer_offset(fr)*compute_bpf(fr));
*/	
	if(rds->filept->seek(rds->filept,-bytes,SEEK_CUR) < 0)
		return -1;

	if(rds->filept->read(rds->filept,buf,4) != 4)
		return -1;

	newhead = (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + buf[3];
	
	while( (newhead & HDRCMPMASK) != (firsthead & HDRCMPMASK) ) {
		if(rds->filept->read(rds->filept,buf,1) != 1)
			return -1;
		newhead <<= 8;
		newhead |= buf[0];
		newhead &= 0xffffffff;
	}

	if( rds->filept->seek(rds->filept,-4,SEEK_CUR) < 0)
		return -1;
	
	read_frame(fr);
	read_frame(fr);

	if(fr->lay == 3) {
		set_pointer(512);
	}

	
	return 0;
}

static int stream_head_read(struct reader *rds,u_int32_t *newhead)
{
  unsigned char hbuf[4];

  if(rds->filept->read(rds->filept,hbuf,4) != 4)
    return FALSE;
  
  *newhead = ((u_int32_t) hbuf[0] << 24) |
    ((u_int32_t) hbuf[1] << 16) |
    ((u_int32_t) hbuf[2] << 8)  |
    (u_int32_t) hbuf[3];
  
  return TRUE;
}

static int stream_head_shift(struct reader *rds,u_int32_t *head)
{
  unsigned char hbuf;

  if(rds->filept->read(rds->filept,&hbuf,1) != 1)
    return 0;
  *head <<= 8;
  *head |= hbuf;
  *head &= 0xffffffff;
  return 1;
}

static int stream_skip_bytes(struct reader *rds,int len)
{
  	return rds->filept->seek(rds->filept,len,SEEK_CUR);
}

static int stream_read_frame_body(struct reader *rds,unsigned char *buf,
				  int size)
{
  int32_t l;

  if( (l=rds->filept->read(rds->filept,buf,size)) != size)
  {
    if(l <= 0)
      return 0;
    memset(buf+l,0,size-l);
  }

  return 1;
}

static int32_t stream_tell(struct reader *rds)
{
  return rds->filept->tell(rds->filept);
}

static void stream_rewind(struct reader *rds)
{
  rds->filept->seek(rds->filept,0,SEEK_SET);
}

/*
 * returns length of a file (if filept points to a file)
 * reads the last 128 bytes information into buffer
 */
static int get_fileinfo(struct reader *rds,char *buf)
{
	int len;

	len=rds->filept->size(rds->filept);

	if (!rds->filept->seekishardwork) {
        if(rds->filept->seek(rds->filept,-128,SEEK_END) < 0)
                return -1;
        if(rds->filept->read(rds->filept,(unsigned char *)buf,128) != 128) {
                return -1;
        }
        if(!strncmp(buf,"TAG",3)) {
                len -= 128;
        }
	}

        if(rds->filept->seek(rds->filept,0,SEEK_SET) < 0)
                return -1;
        if(len <= 0)
                return -1;
	return len;
}


/*****************************************************************
 * read frame helper
 */

struct reader *rd;
struct reader readers[] = {
#ifdef READ_SYSTEM
 { system_init,
   NULL,	/* filled in by system_init() */
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL } ,
#endif
 { default_init,
   stream_close,
   stream_head_read,
   stream_head_shift,
   stream_skip_bytes,
   stream_read_frame_body,
   stream_back_bytes,
   stream_back_frame,
   stream_tell,
   stream_rewind } ,
 { NULL, }
};


/* open the device to read the bit stream from it */

void open_stream(Files f,Eweid e)
{
    int i;
    int filept_opened = 1;

    CFH filept;

    filept=files_open(f,e,O_RDONLY);
	
	if (!filept) {
		//FIXME:
		return;
	}

    rd = NULL;

    for(i=0;;i++) {
      readers[i].filelen = -1;
      readers[i].filept  = filept;
      readers[i].flags = 0;
      if(filept_opened)
        readers[i].flags |= READER_FD_OPENED;
      if(!readers[i].init) {
	fprintf(stderr,"Fatal error!\n");
	exit(1);
      }
      if(readers[i].init(readers+i) >= 0) {
        rd = &readers[i];
        break;
      }


    }

    if(rd && rd->flags & READER_ID3TAG) {
      print_id3_tag(rd->id3buf);
    }
}


