/* 
 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
 * 
 * This file is part of libdvdnav, a DVD navigation library.
 * 
 * libdvdnav 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 of the License, or
 * (at your option) any later version.
 * 
 * libdvdnav 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 * $Id: dvdnav.c,v 1.63 2004/01/31 17:12:58 jcdutton Exp $
 *
 */

/*
 * Ötvös Attila (Attila, Otvos) created it with the libdvdnav-0.1.10.
 * Useful links:
 * SourceForge.net: DVD tools for Linux
 * http://sourceforge.net/projects/dvd/
 */

#include "config.h"

#ifdef USE_MPDVDNAV		// enable dvdnav support?
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "mp_msg.h"
#include "../libmpdemux/stream.h"
#include "help_mp.h"
#include "osdep/timer.h"
#include "input/input.h"
#include "stream.h"
#include "stream_dvdnav.h"
#include "libvo/video_out.h"
#include "spudec.h"
#include "m_option.h"
#include "m_struct.h"
#include "libmpdvdnav/dvdnav_internal.h"

#define TRACE	// enable trace messages print, usage: -dvdnav-trace options
//#define LOG_DEBUG

int dvd_nav_skip_opening = 0;
int dvd_nav_still = 0;
int dvdnav_menutype = 0;
int dvdnav_continue_play = 0;
int dvdnav_go_title = 0; 
int dvdnav_go_part = 0;
int dvdnav_go_menu = 0;
int dvdnav_go_menu_force = 0;
int dvdnav_go_audio = 0;
int dvdnav_go_spu = 0;
int dvdnav_real_still = 1;
int dvdnav_fixed_vo = 1;
int dvdnav_trace = 0;
int dvdnav_skipintro = 0;
int dvdnav_color_spu = 1;
char* dvdmenu_lang = NULL;

extern char* dvd_device;
extern int audio_id;
extern int dvdsub_id;
extern char* audio_lang;
extern char* dvdsub_lang;

#ifdef HAVE_NEW_GUI
int	dvdnav_window_width=0;
int	dvdnav_window_height=0;
int	dvdnav_window_orig_width=0;
int	dvdnav_window_orig_height=0;
int	dvdnav_mouse_x=0;
int	dvdnav_mouse_y=0;
int	dvdnav_mouse_orig_x=0;
int	dvdnav_mouse_orig_y=0;
int	dvdnav_mouse_button=0;
int	dvdnav_mouse_set=0;
#endif

off_t	eventnum=0;

int cellxx=0;

static struct stream_priv_s {
  int track;
  char* device;
} stream_priv_dflts = {
  1,
  NULL
};

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


#ifndef _MSC_VER 
#include <sys/param.h>
#include <sys/fcntl.h>
#else
#ifndef MAXPATHLEN
#define MAXPATHLEN 255
#endif
#endif /* _MSC_VER */

int dvdnav_seek(dvdnav_priv_t * dvdnav_priv, off_t newpos,stream_t *s) {

  if (dvdnav_sector_search(dvdnav_priv->dvdnav, newpos, SEEK_SET)!=DVDNAV_STATUS_OK)
    {
    if (!newpos) mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_MPDVDNAV_ErrorSeek,dvdnav_err_to_string(dvdnav_priv->dvdnav),newpos );
    return 0;
    }
return 1;
}

static int seek(stream_t *s, off_t newpos) {
  off_t seekpos;

  mp_msg(MSGT_CPLAYER,MSGL_DBG2,"DVDNAV seek pos: %llx\n",newpos);
  dvdnav_priv_t* dvdnav_priv=s->priv;
  int domain_vts=dvdnav_priv->vts_domain;
  if(newpos!=s->pos) {
    if (!domain_vts)
      mp_msg(MSGT_CPLAYER,MSGL_V,"Cannot seek in DVDNAV streams yet!\n"); else {
    seekpos=newpos/2048;
    if (dvdnav_seek((dvdnav_priv_t*)s->priv,seekpos,s)) s->pos=newpos;}
    }
  if (newpos==0) {((dvdnav_priv_t*)(s->priv))->stillcounter=0; ((dvdnav_priv_t*)(s->priv))->waitcounter=0; ((dvdnav_priv_t*)(s->priv))->lockstillcounter=0;}
  return 1;
}

unsigned int * dvdnav_stream_get_palette(dvdnav_priv_t * dvdnav_priv)
{
  if (!dvdnav_priv) {
    mp_msg(MSGT_CPLAYER,MSGL_INFO,"%s: NULL dvdnav_priv\n",__FUNCTION__);
    return NULL;
  }
  if (!dvdnav_priv->dvdnav) {
    mp_msg(MSGT_CPLAYER,MSGL_INFO,"%s: NULL dvdnav_priv->dvdnav\n",__FUNCTION__);
    return NULL;
  }
  if (!dvdnav_priv->dvdnav->vm) {
    mp_msg(MSGT_CPLAYER,MSGL_INFO,"%s: NULL dvdnav_priv->dvdnav->vm\n",__FUNCTION__);
    return NULL;
  }
  if (!dvdnav_priv->dvdnav->vm->state.pgc) {
    mp_msg(MSGT_CPLAYER,MSGL_INFO,"%s: NULL dvdnav_priv->dvdnav->vm->state.pgc\n",__FUNCTION__);
    return NULL;
  }
return dvdnav_priv->dvdnav->vm->state.pgc->palette;
}

int dvdnav_menu_action(dvdnav_priv_t * dvdnav_priv, int action)
{
  pci_t* pnavpci;
  dsi_t* pnavdsi;
  uint32_t buttonN;
  pnavpci = dvdnav_get_current_nav_pci( dvdnav_priv->dvdnav );
  pnavdsi = dvdnav_get_current_nav_dsi( dvdnav_priv->dvdnav );
  dvdnav_status_t nav_status;
  if (!dvdnav_priv) return 0;
      switch (action) {
        case MP_CMD_DVDNAV_UP:
          nav_status = dvdnav_upper_button_select(dvdnav_priv->dvdnav, pnavpci);
          break;
        case MP_CMD_DVDNAV_DOWN:
          nav_status = dvdnav_lower_button_select(dvdnav_priv->dvdnav, pnavpci);
          break;
        case MP_CMD_DVDNAV_LEFT:
    	  nav_status = dvdnav_left_button_select(dvdnav_priv->dvdnav, pnavpci);
          break;
        case MP_CMD_DVDNAV_RIGHT:
    	  nav_status = dvdnav_right_button_select(dvdnav_priv->dvdnav, pnavpci);
          break;
        case MP_CMD_DVDNAV_MENU:
    	  dvdnav_wait_still_clear(dvdnav_priv->dvdnav);
	  dvdnav_priv->cell_timer=0;
	  dvdnav_priv->one_cell_timer=0;
	  dvdnav_priv->stillflg=0;
#ifdef USE_MPDVDNAV_TRACE
	  if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: menu action: Menu Root\n");
#endif
	  dvdnav_button_select_auto_action( dvdnav_priv->dvdnav, 0);
          nav_status = dvdnav_menu_call(dvdnav_priv->dvdnav,DVD_MENU_Root);
          break;
        case MP_CMD_DVDNAV_SELECT:
    	  dvdnav_wait_still_clear(dvdnav_priv->dvdnav);
	  dvdnav_priv->cell_timer=0;
	  dvdnav_priv->one_cell_timer=0;
	  dvdnav_priv->stillflg=0;
#ifdef USE_MPDVDNAV_TRACE
	  if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: menu action: Menu Select\n");
#endif
	  dvdnav_button_select_auto_action( dvdnav_priv->dvdnav, 0);
          nav_status = dvdnav_button_activate(dvdnav_priv->dvdnav, pnavpci);
          break;
        default:
	  return 0;
	}
if (nav_status == DVDNAV_STATUS_ERR) {mp_msg(MSGT_CPLAYER, MSGL_WARN, MSGTR_MPDVDNAV_ErrorNav,dvdnav_err_to_string(dvdnav_priv->dvdnav)); return 0;}
return 1;
}

int dvdnav_menu_force(dvdnav_priv_t * dvdnav_priv)
{
return 1;
}

void dvdnav_stream_fullstart(dvdnav_priv_t * dvdnav_priv) {
  if (dvdnav_priv && !dvdnav_priv->started) {
//    dvdnav_stream_reset(dvdnav_priv);
    dvdnav_priv->started=1;
  }
  dvdnav_event_clear(dvdnav_priv);
  dvdnav_priv->stillcounter=0;
  dvdnav_priv->waitcounter=0;
  dvdnav_priv->lockstillcounter=0;
}

int dvdnav_reallyeof(dvdnav_priv_t * dvdnav_priv) 
{
if (!dvdnav_priv) return 1;
return dvdnav_priv->stopflg;
}

void dvdnav_set_language(dvdnav_priv_t *dvdnav_priv, char *alang, char *slang, char *mlang)	/* set nav languages */
{
if (slang) { if (dvdnav_spu_language_select(dvdnav_priv->dvdnav, slang ) != DVDNAV_STATUS_OK)	/* FIXME: don't work! */
  mp_msg(MSGT_FIXME, MSGL_FIXME, "Error on setting spu languages: %s\n",dvdnav_err_to_string(dvdnav_priv->dvdnav)); }
if (alang) { if (dvdnav_audio_language_select(dvdnav_priv->dvdnav, alang ) != DVDNAV_STATUS_OK)	/* FIXME: don't work! */
  mp_msg(MSGT_FIXME, MSGL_FIXME, "Error on setting audio languages: %s\n",dvdnav_err_to_string(dvdnav_priv->dvdnav)); }
if (mlang) { if (dvdnav_menu_language_select(dvdnav_priv->dvdnav, dvdmenu_lang ) != DVDNAV_STATUS_OK)
  mp_msg(MSGT_FIXME, MSGL_FIXME, "Error on setting menu languages: %s\n",dvdnav_err_to_string(dvdnav_priv->dvdnav)); }
return;
}

int dvdnav_lang_from_sid(dvdnav_priv_t *dvdnav_priv, int id) {
  if (!dvdnav_priv) return 0;
  if (id >= dvdnav_priv->nr_of_subtitles) return 0;
  return dvdnav_priv->subtitles[id].language;
}

int dvdnav_number_of_subs(dvdnav_priv_t *dvdnav_priv) {
  if (!dvdnav_priv) return -1;
  return dvdnav_priv->nr_of_subtitles;
}

void dvdnav_event_lock(dvdnav_priv_t * dvdnav_priv, int lock_mode) {
  dvdnav_priv->event_lock=lock_mode;	/* set event lock */
}
int dvdnav_isevent_lock(dvdnav_priv_t * dvdnav_priv) {
  return dvdnav_priv->event_lock;	/* get event lock */
}


static void stream_dvdnav_close(stream_t *s) {
free_dvdnav_stream((dvdnav_priv_t*)s->priv);
}

void dvdnav_event_clear(dvdnav_priv_t * dvdnav_priv)
{
memset(&(dvdnav_priv->event),0,sizeof(stream_dvdnav_event_t));
return;
}

void dvdnav_get_highlight(dvdnav_priv_t *dvdnav_priv,dvdnav_highlight_event_t *highlight_event)
{
pci_t* pnavpci;
int button_number = -1;
int b_mode = 0;
if (!dvdnav_priv) return;
if (!dvdnav_priv->dvdnav) return;
if (!highlight_event) return;
if (NULL==(pnavpci = dvdnav_get_current_nav_pci( dvdnav_priv->dvdnav ))) return;
dvdnav_get_current_highlight(dvdnav_priv->dvdnav, &(highlight_event->buttonN));
highlight_event->display=1; /* show */
if (highlight_event->display==2) b_mode=1;	/* activate */
if (highlight_event->buttonN > 0 && pnavpci->hli.hl_gi.btn_ns > 0 && highlight_event->display) {
    for (button_number = 0; button_number < pnavpci->hli.hl_gi.btn_ns; button_number++) {
        btni_t *btni = &(pnavpci->hli.btnit[button_number]);
        if(highlight_event->buttonN==button_number+1) {
	    highlight_event->sx=min(btni->x_start,btni->x_end);
	    highlight_event->ex=max(btni->x_start,btni->x_end);
	    highlight_event->sy=min(btni->y_start,btni->y_end);
	    highlight_event->ey=max(btni->y_start,btni->y_end);
    	    if (btni->btn_coln==0) highlight_event->palette=0;
		else highlight_event->palette=pnavpci->hli.btn_colit.btn_coli[btni->btn_coln-1][b_mode];
	    }
	}
#ifdef USE_MPDVDNAV_TRACE
    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: get highlight (%i:%i-%i:%i #%i)\n",highlight_event->sx,highlight_event->sy,highlight_event->ex,highlight_event->ey,highlight_event->buttonN);
#endif
    } else {	/* hide button or none button */
#ifdef USE_MPDVDNAV_TRACE
    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: get highlight hide\n");
#endif
    highlight_event->sx=0;
    highlight_event->ex=0;
    highlight_event->sy=0;
    highlight_event->ey=0;
    highlight_event->palette=0; 
    highlight_event->buttonN=0; }
return;
}

void dvdnav_get_button_palette(dvdnav_priv_t *dvdnav_priv,uint32_t *palette)	/* get button palette */
{
pci_t* pnavpci;
int button_number;
int buttonN;
int b_mode = 0;
if (!dvdnav_priv) return;
if (!dvdnav_priv->dvdnav) return;
if (!palette) return;
if (NULL==(pnavpci = dvdnav_get_current_nav_pci( dvdnav_priv->dvdnav ))) return;
dvdnav_get_current_highlight(dvdnav_priv->dvdnav, &buttonN);	/* get current button ID */
if (pnavpci->hli.hl_gi.btn_ns) {	/* is buttons */
    for (button_number = 0; button_number < pnavpci->hli.hl_gi.btn_ns; button_number++) {
        btni_t *btni = &(pnavpci->hli.btnit[button_number]);
        if(buttonN==button_number+1) {					/* current button? */
    	    if (btni->btn_coln==0) *palette=0;
		else *palette=pnavpci->hli.btn_colit.btn_coli[btni->btn_coln-1][b_mode];
	    }
	}
    }
if (*palette==0)	/* if none current button? */
    {
    btni_t *btni = &(pnavpci->hli.btnit[0]);
    if (btni->btn_coln==0) *palette=0;
	else *palette=pnavpci->hli.btn_colit.btn_coli[btni->btn_coln-1][b_mode];
    }
return;
}

#ifdef HAVE_NEW_GUI
void dvdnav_mouse(dvdnav_priv_t * dvdnav_priv)
{
  pci_t* pnavpci;
  if (dvdnav_mouse_set!=1) return;
  dvdnav_mouse_set=2;		// Lock mouse event
  if (NULL==(pnavpci = dvdnav_get_current_nav_pci( dvdnav_priv->dvdnav ))) return;
  if (dvdnav_window_orig_width>0 && dvdnav_window_orig_height>0 && dvdnav_window_width>0 && dvdnav_window_height>0)
    {
    // Calculate original mouse position (if scaled window)
    dvdnav_mouse_orig_x=dvdnav_mouse_x*dvdnav_window_orig_width/dvdnav_window_width;
    dvdnav_mouse_orig_y=dvdnav_mouse_y*dvdnav_window_orig_height/dvdnav_window_height;
    }
    else
    {
    dvdnav_mouse_orig_x=dvdnav_mouse_x;
    dvdnav_mouse_orig_y=dvdnav_mouse_y;
    }
  if (pnavpci->hli.hl_gi.btn_ns) {	/* is buttons */
    dvdnav_mouse_select(dvdnav_priv->dvdnav, pnavpci, dvdnav_mouse_orig_x, dvdnav_mouse_orig_y); /* mouse position select */
    if (dvdnav_mouse_button) dvdnav_menu_action(dvdnav_priv, MP_CMD_DVDNAV_SELECT); }	/* mouse button event */
  dvdnav_mouse_set=0;
  dvdnav_mouse_button=0;
  return;
}
#endif

#define FIRST_AC3_AID 128
#define FIRST_DTS_AID 136
#define FIRST_MPG_AID 0
#define FIRST_PCM_AID 160

int dvdnav_stream_read(dvdnav_priv_t * dvdnav_priv, unsigned char *buf, int *len,stream_dvdnav_event_t *event) {
  int eventid = DVDNAV_NOP;

  if (!len) return -1;
  *len=-1;
  if (!dvdnav_priv) return -1;
  if (!buf) return -1;
  if (dvdnav_priv->stopflg) return -1;

  if (dvd_nav_still) {
    mp_msg(MSGT_CPLAYER,MSGL_DBG2,MSGTR_MPDVDNAV_ReadInStillActive,__FUNCTION__);
    *len=0;
    return -1;
  }
  if (dvdnav_get_next_block(dvdnav_priv->dvdnav,buf,&eventid,len)!=DVDNAV_STATUS_OK) {
    mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_MPDVDNAV_ErrorGetNextBlock,dvdnav_err_to_string(dvdnav_priv->dvdnav) );
    *len=-1;
  }
  else if (eventid!=DVDNAV_BLOCK_OK) {
    switch (eventid) {
#if 0
	case DVDNAV_NOP : {
	    event->eventflag.nop=1; 
	    event->eventflag.isevent=1;
	    break; }
#endif
	case DVDNAV_STILL_FRAME: {
    	    event->eventflag.still_frame=1;
	    event->eventflag.isevent=1;
	    dvdnav_priv->stillflg=1;
	    dvdnav_priv->stillok=0;
    	    dvdnav_still_event_t *still_event = (dvdnav_still_event_t*)(buf);
    	    event->still_length=still_event->length;
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: still frame (%x)\n",event->still_length);
#endif
	    if (event->still_length==0xff)
	      {
	      if (!dvdnav_real_still || dvdnav_priv->stillcounter<DVDNAV_MAX_STILL_FRAME || dvdnav_priv->event_lock) 
	        dvdnav_still_back(dvdnav_priv->dvdnav); else dvdnav_priv->stillok=1;
	      } else {
	      unsigned int end_time=still_event->length*1000000;
	      if (!dvdnav_priv->cell_timer) {dvdnav_priv->cell_timer = GetTimer(); dvdnav_priv->one_cell_timer = dvdnav_priv->cell_timer;}
	      unsigned int now = GetTimer();
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) {
	      float pt=now-dvdnav_priv->cell_timer;
	      pt=pt/1000000;
	      float et=end_time;
	      et=et/1000000;
	      mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: remains time %8.3f sec\n",et-pt); }
#endif
	      if (now-dvdnav_priv->cell_timer<end_time) {	// cell play time end?
	        dvdnav_button_select_auto_action( dvdnav_priv->dvdnav, 1);
    	        if (!dvdnav_real_still || dvdnav_priv->stillcounter<DVDNAV_MAX_STILL_FRAME || dvdnav_priv->event_lock) 
		    dvdnav_still_back(dvdnav_priv->dvdnav); else dvdnav_priv->stillok=1; // don't let dvdnav stall on this image
	        } else {
//	        dvdnav_priv->back_from_timer=1;
//	        dvdnav_wait_still_clear(dvdnav_priv->dvdnav);
    	        dvdnav_still_skip(dvdnav_priv->dvdnav);
	        dvdnav_priv->cell_timer=0;
	        dvdnav_priv->one_cell_timer=0;}
	      }
    	    break; }
	case DVDNAV_SPU_STREAM_CHANGE : {
	    event->eventflag.spu_stream_change=1;
	    event->eventflag.isevent=1;
	    dvdnav_spu_stream_change_event_t *spu_stream_change_event = (dvdnav_spu_stream_change_event_t*)(buf);
	    event->spu_physical_wide=spu_stream_change_event->physical_wide;
	    event->spu_physical_letterbox=spu_stream_change_event->physical_letterbox;
	    event->spu_physical_pan_scan=spu_stream_change_event->physical_pan_scan;
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: spu stream change\n");
#endif
	    break; }
        case DVDNAV_AUDIO_STREAM_CHANGE : {
	    event->eventflag.audio_stream_change=1; 
	    event->eventflag.isevent=1;
	    dvdnav_audio_stream_change_event_t *audio_stream_change_event = (dvdnav_audio_stream_change_event_t*)(buf);
	    event->audio_physical=audio_stream_change_event->physical;
	    event->audio_logical=audio_stream_change_event->logical;
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: audio stream change\n");
#endif
	    break; }
	case DVDNAV_VTS_CHANGE : {
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: vts change\n");
#endif
//printf("vts_change\n");
	    event->eventflag.vts_change=1; 
	    event->eventflag.isevent=1;
	    dvdnav_vts_change_event_t *vts_change_event = (dvdnav_vts_change_event_t*)(buf);
	    event->vts_old_vtsN=vts_change_event->old_vtsN;
	    event->vts_old_domain=vts_change_event->old_domain;
	    event->vts_new_vtsN=vts_change_event->new_vtsN;
	    event->vts_new_domain=vts_change_event->new_domain;
	    if (!dvdnav_priv->vts_domain && !dvdnav_priv->event_lock ) {
		dvdnav_priv->cell_timer=GetTimer(); dvdnav_priv->one_cell_timer=dvdnav_priv->cell_timer; 
		    event->cell_really_change=1; dvdnav_priv->stillcounter=0; dvdnav_priv->waitcounter=0; dvdnav_priv->lockstillcounter=0;
		} else {
		dvdnav_priv->cell_timer=0; dvdnav_priv->one_cell_timer=dvdnav_priv->cell_timer; 
		    event->cell_really_change=1; dvdnav_priv->stillcounter=0; dvdnav_priv->waitcounter=0; dvdnav_priv->lockstillcounter=0;
		}
	    break; }
        case DVDNAV_CELL_CHANGE : {
	    event->eventflag.cell_change=1; 
	    event->eventflag.isevent=1;
	    dvdnav_cell_change_event_t *cell_change_event = (dvdnav_cell_change_event_t*)(buf);
	    event->cell_cellN=cell_change_event->cellN;
	    event->cell_pgN=cell_change_event->pgN;
	    event->cell_cell_length=cell_change_event->cell_length;
	    event->cell_pg_length=cell_change_event->pg_length;
	    event->cell_pgc_length=cell_change_event->pgc_length;
	    event->cell_cell_start=cell_change_event->cell_start;
	    event->cell_pg_start=cell_change_event->pg_start;
	    int pgcN = 0;
	    dvdnav_get_pgc(dvdnav_priv->dvdnav, &pgcN);
// get dvdnav audio settings
	    uint8_t alang=dvdnav_get_active_audio_stream(dvdnav_priv->dvdnav);
	    dvdnav_priv->alang=128+dvdnav_get_active_audio_stream(dvdnav_priv->dvdnav);
// get dvdnav spu settings
	    dvdnav_priv->slang=dvdnav_get_active_spu_stream(dvdnav_priv->dvdnav);		
// get dvdnav aspect settings
	    dvdnav_priv->aspect=dvdnav_get_video_aspect(dvdnav_priv->dvdnav);
// setting dvdnav info
	    dvdnav_priv->vts_domain=dvdnav_is_domain_vts(dvdnav_priv->dvdnav);
	    dvdnav_get_position(dvdnav_priv->dvdnav, &(dvdnav_priv->tpos), &(dvdnav_priv->tlen));
	    dvdnav_get_number_of_titles(dvdnav_priv->dvdnav, &(dvdnav_priv->titles_nr));
	    dvdnav_priv->part=0;
	    dvdnav_priv->title=0;
	    dvdnav_priv->stillok=0;

//printf("cell change: cell: %i pgN: %i pgcN: %i \n",event->cell_cellN,event->cell_pgN,pgcN);
	    if (!(dvdnav_priv->vts_domain)) {
	      dvdnav_priv->chapters_nr=0; 
	      if (!dvdnav_priv->cell_timer && !dvdnav_priv->event_lock &&
	        (dvdnav_priv->cell_cellN!=event->cell_cellN || dvdnav_priv->cell_pgN!=event->cell_pgN || dvdnav_priv->cell_pgcN!=pgcN)	// really cell change?
	        ) {dvdnav_priv->cell_timer=GetTimer(); dvdnav_priv->one_cell_timer=dvdnav_priv->cell_timer; 
		    event->cell_really_change=1; dvdnav_priv->stillcounter=0; dvdnav_priv->waitcounter=0; dvdnav_priv->lockstillcounter=0;}
	      if (dvdnav_priv->event_lock) {dvdnav_priv->cell_timer=0; dvdnav_priv->one_cell_timer=0;}
	      dvdnav_priv->menu_sid=dvdnav_get_active_spu_stream(dvdnav_priv->dvdnav);
	      } else {
	      if (!dvdnav_priv->stillflg) {dvdnav_priv->cell_timer=0; dvdnav_priv->one_cell_timer=0; dvdnav_priv->stillcounter=0; dvdnav_priv->waitcounter=0; dvdnav_priv->lockstillcounter=0;}
	      if (!dvdnav_priv->cell_timer && !dvdnav_priv->event_lock &&
	        (dvdnav_priv->cell_cellN!=event->cell_cellN || dvdnav_priv->cell_pgN!=event->cell_pgN || dvdnav_priv->cell_pgcN!=pgcN)	// really cell change?
	        ) {dvdnav_priv->one_cell_timer=dvdnav_priv->cell_timer; event->cell_really_change=1; dvdnav_priv->stillcounter=0; dvdnav_priv->waitcounter=0; dvdnav_priv->lockstillcounter=0;}
	      dvdnav_current_title_info(dvdnav_priv->dvdnav, &dvdnav_priv->title, &dvdnav_priv->part);
	      dvdnav_get_number_of_parts(dvdnav_priv->dvdnav, dvdnav_priv->title, &dvdnav_priv->chapters_nr);
	      event->cell_really_change=1;

	      uint32_t audio_id;
	      audio_attr_t audio_attr;
	      dvdnav_priv->nr_of_channels=0;
	      int language;
	      int i;
	      for( i=0; i<8; i++)
	        {
		if (-1!=(audio_id=audio_id=dvdnav_get_audio_logical_stream(dvdnav_priv->dvdnav, i))) {
	          dvdnav_get_audio_attr(dvdnav_priv->dvdnav, i, &audio_attr);
		  language=0;
		  if(audio_attr.lang_type==1)
		    {
		    language=audio_attr.lang_code;
		    }
		  dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].language=language;
		  dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].id=audio_id;
		  switch (audio_attr.audio_format) {
		    case 0: // ac3
		      dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].id+=FIRST_AC3_AID;
		    break;
		    case 6: // dts
		      dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].id+=FIRST_DTS_AID;
		    break;
		    case 2: // mpeg layer 1/2/3
		    case 3: // mpeg2 ext
		      dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].id+=FIRST_MPG_AID;
		    break;
		    case 4: // lpcm
		      dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].id+=FIRST_PCM_AID;
		    break;
		    }
		  dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].type=audio_attr.audio_format;
		  dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].channels=audio_attr.channels;
		  if (alang==i) dvdnav_priv->alang=dvdnav_priv->audio_streams[dvdnav_priv->nr_of_channels].id;
		  dvdnav_priv->nr_of_channels++;
	          }
		}
	      dvdnav_priv->nr_of_subtitles=0;
	      uint32_t sub_id;
	      subp_attr_t subp_attr;
	      for( i=0; i<32; i++)
	        {
		if (-1!=(sub_id=dvdnav_get_spu_logical_stream(dvdnav_priv->dvdnav, i))) {
	          dvdnav_get_spu_attr(dvdnav_priv->dvdnav, i, &subp_attr);
		  language=0;
		  if(subp_attr.type==1)
		    {
		    language=subp_attr.lang_code;
		    }
		  dvdnav_priv->subtitles[dvdnav_priv->nr_of_subtitles].language=language;
		  dvdnav_priv->subtitles[dvdnav_priv->nr_of_subtitles].id=sub_id;
		  dvdnav_priv->nr_of_subtitles++;
	          }
		}
	      }
	    dvdnav_priv->cell_cellN=cell_change_event->cellN;
	    dvdnav_priv->cell_pgN=cell_change_event->pgN;
	    dvdnav_priv->cell_pgcN=pgcN;
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: cell change\n");
#endif
	    break; }
	case DVDNAV_NAV_PACKET : {
	    event->eventflag.nav_packet=1; 
	    event->eventflag.isevent=1;
	    pci_t* pnavpci = dvdnav_get_current_nav_pci( dvdnav_priv->dvdnav );
	    int buttonN;
	    if (pnavpci && pnavpci->hli.hl_gi.btn_ns) {
	      dvdnav_get_current_highlight(dvdnav_priv->dvdnav, &buttonN);	/* get current button ID */
	      if (buttonN==0 || buttonN > pnavpci->hli.hl_gi.btn_ns) dvdnav_button_select( dvdnav_priv->dvdnav , pnavpci, 1);
	      }
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: nav packet\n");
#endif
	    break; }
	case DVDNAV_STOP: {
    	    event->eventflag.stop=1;
	    event->eventflag.isevent=1;
	    dvdnav_priv->stopflg=1;
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: stop\n");
#endif
	    break; }
        case DVDNAV_HIGHLIGHT : {
	    event->eventflag.highlight=1; 
	    event->eventflag.isevent=1;
	    dvdnav_highlight_event_t *highlight_event = (dvdnav_highlight_event_t*)(buf);
	    event->hl_display=highlight_event->display;
	    /* libdvdnav v0.1.10: these fields are currently not set!! */
	    dvdnav_get_highlight(dvdnav_priv,highlight_event);
	    event->hl_palette=highlight_event->palette;
	    event->hl_sx=highlight_event->sx;
	    event->hl_sy=highlight_event->sy;
	    event->hl_ex=highlight_event->ex;
	    event->hl_ey=highlight_event->ey;
	    event->hl_pts=highlight_event->pts;
	    event->hl_buttonN=highlight_event->buttonN;
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: highlight (%i:%i-%i:%i #%i)\n",event->hl_sx,event->hl_sy,event->hl_ex,event->hl_ey,event->hl_buttonN);
#endif
	    break; }
	case DVDNAV_SPU_CLUT_CHANGE: {
	    event->eventflag.spu_clut_change=1;
	    event->eventflag.isevent=1;
	    memcpy(event->spu_clut,buf,16*sizeof(uint32_t));
	    dvdnav_get_button_palette(dvdnav_priv,&(event->spu_palette));
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: spu clut change\n");
#endif
    	    break; }
	case DVDNAV_HOP_CHANNEL : {
	    event->eventflag.hop_channel=1; 
	    event->eventflag.isevent=1;
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: hop channel\n");
#endif
	    break; }
	case DVDNAV_WAIT: {
#ifdef USE_MPDVDNAV_TRACE
	    if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: wait\n");
#endif
	    event->eventflag.wait=1;
	    event->eventflag.isevent=1;
	    int flag=0;
	    dvdnav_is_still_cell( dvdnav_priv->dvdnav , &flag);
	    if (!dvdnav_priv->vts_domain && dvdnav_priv->cell_timer && !dvdnav_priv->event_lock && !flag)
	      {
	      pci_t* pnavpci = dvdnav_get_current_nav_pci( dvdnav_priv->dvdnav );
	      unsigned int end_time=(pnavpci->pci_gi.e_eltm.hour*3600+pnavpci->pci_gi.e_eltm.minute*60+pnavpci->pci_gi.e_eltm.second)*1000000;
	      unsigned int now = GetTimer();
	      float t;
	      t=now-dvdnav_priv->one_cell_timer;
	      dvdnav_priv->one_cell_timer=now;
	      t=t/1000000;
	      float rt=end_time-(now-dvdnav_priv->cell_timer);
	      rt=rt/1000000;
	      float et=end_time;
	      et=et/1000000;
//printf( "dvdnav: remains time %8.3f sec (%8.3f/%8.3f sec)\n",rt,et,t);
#ifdef USE_MPDVDNAV_TRACE
	      if (dvdnav_trace) mp_msg(MSGT_CPLAYER,MSGL_INFO, "dvdnav: remains time %8.3f sec (%8.3f/%8.3f sec)\n",rt,et,t);
#endif
	      if (t<2.00 && et>4.00) {				// correct cell play time?
	        if (now-dvdnav_priv->cell_timer<end_time) {	// cell play time end?
//printf("wait back\n");
	    	  if (dvdnav_priv->waitcounter<DVDNAV_MAX_WAIT_FRAME) {dvdnav_wait_back(dvdnav_priv->dvdnav); if (dvdnav_real_still) dvdnav_priv->waitcounter++;}
		  } else {
		  dvdnav_priv->waitcounter=0;
		  dvdnav_priv->cell_timer=0;
		  dvdnav_priv->one_cell_timer=0;
		  dvdnav_priv->back_from_timer=1;
		  dvdnav_wait_still_clear(dvdnav_priv->dvdnav);
		  } 
		} else {
		dvdnav_wait_skip(dvdnav_priv->dvdnav); 
		dvdnav_priv->waitcounter=0;
		dvdnav_priv->cell_timer=0;
		dvdnav_priv->one_cell_timer=0;
		}
	      } else {
	      dvdnav_wait_skip(dvdnav_priv->dvdnav);
	      if (!flag) {dvdnav_priv->cell_timer=0; dvdnav_priv->one_cell_timer=0; dvdnav_priv->waitcounter=0;}
	      }
	    break; }
	}
    *len=0;
  }
return eventid;
}


static int fill_buffer(stream_t *s, char *but, int len)
{
  int event;
  dvdnav_priv_t* dvdnav_priv=s->priv;
  len=0;
  while (len==0) /* grab all event until DVDNAV_BLOCK_OK (len=2048), DVDNAV_STOP or DVDNAV_STILL_FRAME */
    {
    if (-1==(event=dvdnav_stream_read(dvdnav_priv,s->buffer,&len,&(dvdnav_priv->event))) || len==-1) {mp_msg(MSGT_CPLAYER,MSGL_ERR,"DVDNAV stream read error!\n"); return 0; }
    switch (event) {
	case DVDNAV_STOP: return len; break; /* return eof */
	case DVDNAV_STILL_FRAME: {
	  if (dvdnav_real_still && !dvdnav_priv->stillok) dvdnav_priv->stillcounter++;
	  if (!dvdnav_priv->event_lock) return len; else {
	  dvdnav_priv->lockstillcounter++;
	  dvdnav_priv->stillok=0;
	  if (dvdnav_priv->lockstillcounter>DVDNAV_MAX_STILL_FRAME) return len; 
	  if (dvdnav_real_still && dvdnav_priv->stillcounter>DVDNAV_MAX_STILL_FRAME) return len; } 
	  break; }/* return eof */
	case DVDNAV_WAIT: {
	  if (dvdnav_priv->waitcounter>=DVDNAV_MAX_WAIT_FRAME) return len;
	  }
	}
    /* the query of the time lenght of title stream mode: event_lock true */
    if (dvdnav_priv->event_lock && (dvdnav_priv->event.eventflag.cell_change) && (dvdnav_priv->title!=dvdnav_priv->lasttitle || 
	(dvdnav_priv->title==0 && dvdnav_priv->vts_domain!=dvdnav_priv->old_vts_domain))) { /* if end of title switch other then eof */
      /* end of play title */
      dvdnav_event_clear(dvdnav_priv); /* stream event clear */
      /* reset title play title & part */
      dvdnav_part_play(dvdnav_priv->dvdnav, dvdnav_priv->lasttitle, dvdnav_priv->lastpart);
      /* return eof */
//      printf("eof!!\n");
      return len;
      }
/*
    if (len>0)
	{
	FILE *fh;
	char fname[64];
	snprintf(fname,64,"navdump-%i.mpg",cellxx);
	fh=fopen(fname,"a+");
	fwrite(s->buffer,len,1,fh);
	fclose(fh);
	}
*/
    }
#if 1
  if (dvdnav_priv->event_lock) // lock events ??
    dvdnav_event_clear(dvdnav_priv); /* stream event clear */
#endif
  mp_msg(MSGT_STREAM,MSGL_DBG2,"DVDNAV fill_buffer len: %d\n",len);
  return len;
}

dvdnav_priv_t * new_dvdnav_stream(char * filename, uint32_t titlenum) {
  char * title_str;
  dvdnav_priv_t *dvdnav_priv;

  if (!filename) return NULL;
  if (!(dvdnav_priv=(dvdnav_priv_t*)calloc(1,sizeof(*dvdnav_priv))))  return NULL;
  if (!(dvdnav_priv->filename=strdup(filename))) {
    free(dvdnav_priv);
    return NULL; }
  if(dvdnav_open(&(dvdnav_priv->dvdnav),dvdnav_priv->filename)!=DVDNAV_STATUS_OK) {
    free(dvdnav_priv->filename);
    free(dvdnav_priv);
    return NULL; }
  if (!dvdnav_priv->dvdnav) {
    free(dvdnav_priv);
    return NULL; }
//  dvdnav_stream_ignore_timers(dvdnav_priv,dvd_nav_skip_opening);
  /* turn on dvdnav caching */
  dvdnav_set_readahead_flag(dvdnav_priv->dvdnav,0);
  /* report the title?! */
#if 0
  if (dvdnav_get_title_string(dvdnav_priv->dvdnav,&title_str)==DVDNAV_STATUS_OK) {
    mp_msg(MSGT_OPEN,MSGL_INFO,"Title: '%s'\n",title_str);
  }
#endif
  if (dvdmenu_lang)	// menu
    {
    if (dvdnav_menu_language_select(dvdnav_priv->dvdnav, dvdmenu_lang ) != DVDNAV_STATUS_OK)
      mp_msg(MSGT_FIXME, MSGL_FIXME, "Error on setting menu languages: %s\n",dvdnav_err_to_string(dvdnav_priv->dvdnav));
    }
    /* use title time length (default: part time length) */
  if (dvdnav_set_PGC_positioning_flag(dvdnav_priv->dvdnav, 1) != DVDNAV_STATUS_OK)
      mp_msg(MSGT_FIXME, MSGL_FIXME, "Error on setting pgc based: %s\n",dvdnav_err_to_string(dvdnav_priv->dvdnav));
  dvdnav_event_clear(dvdnav_priv);
  if (titlenum>0) dvdnav_go_title=titlenum;
//  if (dvdnav_skipintro) dvdnav_go_menu_force = 1;
  dvdnav_priv->event_lock=1;
  dvdnav_priv->lastaspect=-1;
  return dvdnav_priv;
}

void free_dvdnav_stream(dvdnav_priv_t * dvdnav_priv) {
  if (!dvdnav_priv) return;
  dvdnav_close(dvdnav_priv->dvdnav);
  dvdnav_priv->dvdnav=NULL;
  free(dvdnav_priv);
}


static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
  struct stream_priv_s* p = (struct stream_priv_s*)opts;
  char *filename, *name;
  int event,len,tmplen=0;
  dvdnav_priv_t *dvdnav_priv;

  mp_msg(MSGT_OPEN,MSGL_INFO,"URL: %s\n", filename);
  filename = strdup(stream->url);
  name = (filename[9] == '\0') ? NULL : filename + 9;

  uint32_t titlenum=0;
  if (name) titlenum=atoi(name);	/* eg. mplayer dvdnav://1 */
  if (dvd_device)
    {
    if(!(dvdnav_priv=new_dvdnav_stream(dvd_device,titlenum))) {
      mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CantOpenDVD,dvd_device);
      return STREAM_UNSUPORTED;}
    }
    else
    {
    if(!(dvdnav_priv=new_dvdnav_stream(DEFAULT_DVD_DEVICE,titlenum))) {
      mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CantOpenDVD,dvd_device);
      return STREAM_UNSUPORTED;}
    }
  stream->sector_size = 2048;
  stream->flags = STREAM_READ | STREAM_SEEK;
  stream->fill_buffer = fill_buffer;
  stream->seek = seek;
  stream->close = stream_dvdnav_close;
  stream->type = STREAMTYPE_DVDNAV;
  stream->priv=(void*)dvdnav_priv;

  return STREAM_OK;
}

stream_info_t stream_info_dvdnav = {
  "DVDNAV stream",
  "dvdnav",
  "Otvos Attila",
  "",
  open_s,
  { "dvdnav", NULL },
  &stream_opts,
  1 // Urls are an option string
};

#endif	// #ifdef USE_MPDVDNAV
