/*
 *  stream_sharedmem.c
 *
 *	Copyright (C) Alban Bedel - 04/2003
 *
 *  This file is part of MPlayer, a free movie player.
 *	
 *  MPlayer is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  MPlayer is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 *
 */


#include "config.h"

#ifdef HAVE_SHAREDMEMOUT

#include <unistd.h>

#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include "mp_msg.h"
#include "stream.h"
#include "libmpdemux/demuxer.h"
#include "help_mp.h"
#include "m_option.h"
#include "m_struct.h"
#include "bswap.h"


typedef struct {
  int shmid;
  char* shmptr;
  int smemmaxsize;
  smem_t smem;
  } smems_t;

static struct stream_priv_s {
  char *host;
  int smemcount;
  int smemrecno;
  int smemid;
  int smemmaxsize;
  void *data;
  void *readptr;
  int readlen;
  int *smemsize;
  int shmid;
  char* shmptr;
  smems_t *smems;
  smem_t smem;
} stream_priv_dflts = {
  NULL,
  0,
  0,
  0,
  0,
  NULL,
  NULL,
  0,
  NULL,
  0,
  NULL,
  NULL
};


#define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
/// URL definition
static m_option_t stream_opts_fields[] = {
  {"hostname", ST_OFF(host), CONF_TYPE_STRING, 0, 0 ,0, NULL},
  { NULL, NULL, 0, 0, 0, 0,  NULL }
};
static struct m_struct_st stream_opts = {
  "shmem",
  sizeof(struct stream_priv_s),
  &stream_priv_dflts,
  stream_opts_fields
};


static int fill_buffer(stream_t *s, char* buffer, int max_len){
  struct stream_priv_s* p = (struct stream_priv_s*)s->priv;
  void *ptr;
  smem_t *sptr;
  int len;

  if(!p->shmptr) {
    return -1;
  }
  
  if (p->readlen==0)
    {
    p->smemrecno++;
    if (p->smemrecno>=p->smemcount) p->smemrecno=0;
    p->shmid=p->smems[p->smemrecno].shmid;
    p->smemmaxsize=p->smems[p->smemrecno].smemmaxsize;
    p->shmptr=p->smems[p->smemrecno].shmptr;

    sptr=p->shmptr;
    p->smemsize=sptr->size;
    memcpy(p->data,sptr->data,p->smemsize);
    p->readptr=p->data;
    p->readlen=p->smemsize;    
    }
  if (p->readlen>max_len) len=max_len; else len=p->readlen;    
  if(len > 0)
    {
    memcpy(buffer,p->readptr,len);
    p->readptr+=len;
    p->readlen-=len;
    }
  return len;
}

static int control(stream_t *stream,int cmd,void* arg) 
{
struct stream_priv_s* p = (struct stream_priv_s*)stream->priv;
switch(cmd) 
  {
    case STREAM_CTRL_GET_WIDTH: {
      *((unsigned int *)arg) = p->smem.width;
      return 1; }
    case STREAM_CTRL_GET_HEIGHT: {
      *((unsigned int *)arg) = p->smem.height;
      return 1; }
    case STREAM_CTRL_GET_FORMAT: {
      *((unsigned int *)arg) = p->smem.format;
      return 1; }
  }
return STREAM_UNSUPORTED;
}


static int seek(stream_t *s,off_t newpos) {
  struct stream_priv_s* p = (struct stream_priv_s*)s->priv;
  p->readlen=p->smemsize;
  return 0;
}

static int net_stream_reset(struct stream_st *s) {
  struct stream_priv_s* p = (struct stream_priv_s*)s->priv;
  p->readlen=p->smemsize;
  return 0;
}
 
static void close_smem(int smemcount, smems_t *smems) {
  int i;
  for(i=0;i<smemcount;i++)
    if(smems[i].shmid) {
      shmdt(smems[i].shmptr);
      smems[i].shmid=0;
      smems[i].smemmaxsize=0;
      smems[i].shmptr=NULL;      
      }
  return;
}


static void close_s(struct stream_st *s) {
  struct stream_priv_s* p = (struct stream_priv_s*)s->priv;

  close_smem(p->smemcount,p->smems);
  free(p->smems);
  free(p->data);
  m_struct_free(&stream_opts,p);
}

static int open_smem(int smemid, int *shmid, int *smemmaxsize, char **shmptr, smem_t *smem, int num) {
  smem_t *ptr;
  *shmid=shmget(smemid, sizeof(smem_t), 0666);
  if (*shmid < 0) {
    mp_msg(MSGT_OPEN,MSGL_ERR, "Can't open shared memory (%d)\n",smemid);
    return 0;
  }
  *shmptr=shmat(*shmid,0,0);
  if(*shmptr==(char*)-1) {
    mp_msg(MSGT_OPEN,MSGL_ERR, "Can't mount shared memory (%d)\n",smemid);
    return 0;
  }
  ptr=*shmptr;
  if (ptr->mplayerid[0]!='M' || ptr->mplayerid[1]!='P') {
    shmdt(*shmptr);
    mp_msg(MSGT_OPEN,MSGL_ERR, "Illegal shared memory type (%d)\n",smemid);
    return 0;
  }
  if (ptr->type!=0) {
    shmdt(*shmptr);
    mp_msg(MSGT_OPEN,MSGL_ERR, "Illegal shared memory type (%d)\n",smemid);
    return 0;
  }
  if (num==0)
    {
    smem->size=ptr->size;
    smem->type=ptr->type;
    smem->demuxer_type=ptr->demuxer_type;
    smem->format=ptr->format;
    smem->width=ptr->width;
    smem->height=ptr->height;
    }
    else
    {
    if (smem->size<ptr->size || smem->demuxer_type!=ptr->demuxer_type || smem->format!=ptr->format ||
	smem->width!=ptr->width || smem->height!=ptr->height) {
      shmdt(*shmptr);
      mp_msg(MSGT_OPEN,MSGL_ERR, "Illegal shared memory type (%d)\n",smemid);
      return 0;
      }
    }
  *smemmaxsize=ptr->size;
  shmdt(*shmptr);
  *shmid=shmget(smemid, sizeof(smem_t)+*smemmaxsize, 0666);
  if (*shmid < 0) {
    mp_msg(MSGT_OPEN,MSGL_ERR, "Can't open shared memory (%d)\n",smemid);
    return 0;
  }
  *shmptr=shmat(*shmid,0,0);
  if(*shmptr==(char*)-1) {
    mp_msg(MSGT_OPEN,MSGL_ERR, "Can't mount shared memory (%d)\n",smemid);
    return 0;
  }
  return 1;
}

static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
  int f;
  struct stream_priv_s* p = (struct stream_priv_s*)opts;
  int smemids[8];
  int i;

  if(mode != STREAM_READ)
    return STREAM_UNSUPORTED;

  if(!p->host) {
    mp_msg(MSGT_OPEN,MSGL_ERR, "We need an shared memory id (ex: shmem://1000 or shmem://1000,1001,1002,1003)\n");
    m_struct_free(&stream_opts,opts);
    return STREAM_ERROR;
  }

  memset(smemids,0,sizeof(smemids));
  p->smemcount=0;
  sscanf(p->host,"%d,%d,%d,%d,%d,%d,%d,%d",&smemids[0],&smemids[1],&smemids[2],&smemids[3],&smemids[4],&smemids[5],&smemids[6],&smemids[7]);
  for(i=0;i<8;i++)
    {
    if (smemids[i]>0) p->smemcount++;
    }
  if (p->smemcount==0)
    {
    mp_msg(MSGT_OPEN,MSGL_ERR, "We need an shared memory id (ex: shmem://1000 or shmem://1000,1001,1002,1003)\n");
    m_struct_free(&stream_opts,opts);
    return STREAM_ERROR;
    }  
  p->smems=malloc(sizeof(smems_t)*p->smemcount);
  memset(p->smems,0,sizeof(smems_t)*p->smemcount);
  p->smemmaxsize=0;
  for(i=0;i<p->smemcount;i++)
    {
    if (!open_smem(smemids[i],&(p->smems[i].shmid),&(p->smems[i].smemmaxsize),&(p->smems[i].shmptr),&(p->smem),i))
      {
      close_smem(p->smemcount,p->smems);
      free(p->smems);
      m_struct_free(&stream_opts,opts);
      return STREAM_ERROR;
      }
    if(p->smems[i].smemmaxsize>p->smemmaxsize) p->smemmaxsize=p->smems[i].smemmaxsize;
//    printf("[%d] %d = %d %d %p \n",i,smemids[i],p->smems[i].shmid,p->smems[i].smemmaxsize,p->smems[i].shmptr);
    }
  mp_msg(MSGT_OPEN,MSGL_INFO, "Shared memory stream info: format=0x%x width=%d height=%d\n",p->smem.format,p->smem.width,p->smem.height);
  p->smemrecno=0;
  p->shmid=p->smems[p->smemrecno].shmid;
  p->smemmaxsize=p->smems[p->smemrecno].smemmaxsize;
  p->shmptr=p->smems[p->smemrecno].shmptr;

  p->data=malloc(p->smemmaxsize);
  *file_format = DEMUXER_TYPE_RAWVIDEO;
  stream->flags = 0;
  stream->sector_size = 2048;
  stream->start_pos = 0;
  stream->end_pos = 0;

  stream->fill_buffer = fill_buffer;
  stream->control = control;
  if(stream->flags & STREAM_SEEK)
    stream->seek = seek;
  stream->close = close_s;
  stream->priv=opts;
  return STREAM_OK;
}



stream_info_t stream_info_sharedmem = {
  "Shared memory stream",
  "sharedmem",
  "Otvos Attila",
  "",
  open_s,
  { "smem",NULL },
  &stream_opts,
  1 // Url is an option string
};

#endif
