# Written by Arno Bakker
# see LICENSE.txt for license information
from threading import currentThread
from traceback import print_exc
import inspect
import os
import re
import sys
import urllib
import urlparse
import wx

from Tribler.Video.defs import *
from Tribler.Video.VideoServer import VideoHTTPServer,VideoRawVLCServer
from Tribler.Video.utils import win32_retrieve_video_play_command,win32_retrieve_playcmd_from_mimetype,quote_program_path,videoextdefaults

from Tribler.Core.simpledefs import *
from Tribler.Core.Utilities.unicode import unicode2str,bin2unicode

from Tribler.Video.CachingStream import SmartCachingStream
from Tribler.Video.Ogg import is_ogg,OggMagicLiveStream
from Tribler.Main.vwxGUI import forceWxThread

DEBUG = False

if sys.platform == "linux2" or sys.platform == "darwin":
    USE_VLC_RAW_INTERFACE = False
else:
    USE_VLC_RAW_INTERFACE = False # False for Next-Share
    

class VideoPlayer:
    
    __single = None
    
    def __init__(self,httpport=6880):
        if VideoPlayer.__single:
            raise RuntimeError, "VideoPlayer is singleton"
        VideoPlayer.__single = self
        self.videoframe = None
        self.extprogress = None
        self.vod_download = None
        self.playbackmode = None
        self.preferredplaybackmode = None
        self.other_downloads = None
        self.closeextplayercallback = None

        self.videohttpservport = httpport
        self.videohttpserv = None
        # Must create the instance here, such that it won't get garbage collected
        self.videorawserv = VideoRawVLCServer.getInstance()

        self.resume_by_system = 1
        self.user_download_choice = None
        
    def getInstance(*args, **kw):
        if VideoPlayer.__single is None:
            VideoPlayer(*args, **kw)
        return VideoPlayer.__single
    getInstance = staticmethod(getInstance)
    
    def hasInstance():
        return VideoPlayer.__single and VideoPlayer.__single.vlcwrap and VideoPlayer.__single.vlcwrap.initialized
    hasInstance = staticmethod(hasInstance)
        
    def register(self,utility,preferredplaybackmode=None,closeextplayercallback=None):
        
        self.utility = utility # TEMPARNO: make sure only used for language strings

        self.preferredplaybackmode = preferredplaybackmode
        self.determine_playbackmode()

        if self.playbackmode == PLAYBACKMODE_INTERNAL:
            # The python-vlc bindings. Created only once at the moment,
            # as using MediaControl.exit() more than once with the raw interface
            # blows up the GUI.
            #
            from Tribler.Video.VLCWrapper import VLCWrapper
            self.vlcwrap = VLCWrapper(self.utility.getPath())
            self.supportedvodevents = [VODEVENT_START,VODEVENT_PAUSE,VODEVENT_RESUME]
        else:
            self.vlcwrap = None
            # Can't pause when external player
            self.supportedvodevents = [VODEVENT_START]
            
        if self.playbackmode != PLAYBACKMODE_INTERNAL or not USE_VLC_RAW_INTERFACE:
            # Start HTTP server for serving video to external player
            self.videohttpserv = VideoHTTPServer.getInstance(self.videohttpservport) # create
            self.videohttpserv.background_serve()
            self.videohttpserv.register(self.videohttpserver_error_callback,self.videohttpserver_set_status_callback)
            
        if closeextplayercallback is not None:
            self.closeextplayercallback = closeextplayercallback

    def set_other_downloads(self, other_downloads):
        """A boolean indicating whether there are other downloads running at this time"""
        self.other_downloads = other_downloads

    def get_vlcwrap(self):
        return self.vlcwrap
    
    def get_supported_vod_events(self):
        return self.supportedvodevents

    def set_videoframe(self,videoframe):
        self.videoframe = videoframe


    def play_file(self,dest): 
        """ Play video file from disk """
        if DEBUG:
            print >>sys.stderr,"videoplay: Playing file from disk",dest

        (prefix,ext) = os.path.splitext(dest)
        [mimetype,cmd] = self.get_video_player(ext,dest)
        
        if DEBUG:
            print >>sys.stderr,"videoplay: play_file: cmd is",cmd
 
        self.launch_video_player(cmd)

    def play_file_via_httpserv(self,dest):
        """ Play a file via our internal HTTP server. Needed when the user
        selected embedded VLC as player and the filename contains Unicode
        characters.
        """ 
        if DEBUG:
            print >>sys.stderr,"videoplay: Playing file with Unicode filename via HTTP"

        (prefix,ext) = os.path.splitext(dest)
        videourl = self.create_url(self.videohttpserv,'/'+os.path.basename(prefix+ext))
        [mimetype,cmd] = self.get_video_player(ext,videourl)

        stream = open(dest,"rb")
        stats = os.stat(dest)
        length = stats.st_size
        streaminfo = {'mimetype':mimetype,'stream':stream,'length':length}
        self.videohttpserv.set_inputstream(streaminfo)
        
        self.launch_video_player(cmd)


 
    def play_url(self,url):
        """ Play video file from network or disk """
        if DEBUG:
            print >>sys.stderr,"videoplay: Playing file from url",url
        
        self.determine_playbackmode()
        
        t = urlparse.urlsplit(url)
        dest = t[2]
        
        # VLC will play .flv files, but doesn't like the URLs that YouTube uses,
        # so quote them
        if self.playbackmode != PLAYBACKMODE_INTERNAL:
            if sys.platform == 'win32':
                x = [t[0],t[1],t[2],t[3],t[4]]
                n = urllib.quote(x[2])
                if DEBUG:
                    print >>sys.stderr,"videoplay: play_url: OLD PATH WAS",x[2],"NEW PATH",n
                x[2] = n
                n = urllib.quote(x[3])
                if DEBUG:
                    print >>sys.stderr,"videoplay: play_url: OLD QUERY WAS",x[3],"NEW PATH",n
                x[3] = n
                url = urlparse.urlunsplit(x)
            elif url[0] != '"' and url[0] != "'":
                # to prevent shell escape problems
                # TODO: handle this case in escape_path() that now just covers spaces
                url = "'"+url+"'" 

        (prefix,ext) = os.path.splitext(dest)
        [mimetype,cmd] = self.get_video_player(ext,url)
        
        if DEBUG:
            print >>sys.stderr,"videoplay: play_url: cmd is",cmd
        
        self.launch_video_player(cmd)


    def play_stream(self,streaminfo):
        if DEBUG:
            print >>sys.stderr,"videoplay: play_stream"

        self.determine_playbackmode()

        if self.playbackmode == PLAYBACKMODE_INTERNAL:
            if USE_VLC_RAW_INTERFACE:
                # Play using direct callbacks from the VLC C-code
                self.launch_video_player(None,streaminfo=streaminfo)
            else:
                # Play via internal HTTP server
                self.videohttpserv.set_inputstream(streaminfo,'/')
                url = self.create_url(self.videohttpserv,'/')

                self.launch_video_player(url,streaminfo=streaminfo)
        else:
            # External player, play stream via internal HTTP server
            path = '/'
            self.videohttpserv.set_inputstream(streaminfo,path)
            url = self.create_url(self.videohttpserv,path)

            [mimetype,cmd] = self.get_video_player(None,url,mimetype=streaminfo['mimetype'])
            self.launch_video_player(cmd)


    def launch_video_player(self,cmd,streaminfo=None):
        if self.playbackmode == PLAYBACKMODE_INTERNAL:

            if cmd is not None:
                # Play URL from network or disk
                self.videoframe.get_videopanel().Load(cmd,streaminfo=streaminfo)
            else:
                # Play using direct callbacks from the VLC C-code
                self.videoframe.get_videopanel().Load(cmd,streaminfo=streaminfo)

            self.videoframe.show_videoframe()
            self.videoframe.get_videopanel().StartPlay()
        else:
            # Launch an external player
            # Play URL from network or disk
            self.exec_video_player(cmd)


    def stop_playback(self,reset=False):
        """ Stop playback in current video window """
        if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None:
            self.videoframe.get_videopanel().Stop()
            self.videoframe.Stop()
            if reset:
                self.videoframe.get_videopanel().Reset()
        self.set_vod_download(None)
        
    def pause_playback(self):
        """ Pause playback in current video window """
        if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None:
            self.videoframe.get_videopanel().Pause()
    
    def resume_playback(self):
        """ Resume playback in current video window """
        if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None:
            self.videoframe.get_videopanel().Resume()

    def show_loading(self):
        if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None:
            self.videoframe.get_videopanel().ShowLoading()
            self.videoframe.ShowLoading()

    def close(self):
        """ Stop playback and close current video window """
        if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None:
            self.videoframe.hide_videoframe()
        self.set_vod_download(None)

    def play(self,ds, selectedinfilename=None):
        """ Used by Tribler Main """
        self.determine_playbackmode()
        
        d = ds.get_download()
        tdef = d.get_def()
        videofiles = d.get_dest_files(exts=videoextdefaults)
        
        if len(videofiles) == 0:
            print >>sys.stderr,"videoplay: play: No video files found! Let user select"
            # Let user choose any file
            videofiles = d.get_dest_files(exts=None)
            
            
        selectedoutfilename= None
        if selectedinfilename is None:
            # User didn't select file to play, select if there is a single, or ask
            if len(videofiles) > 1:
                infilenames = []
                for infilename,diskfilename in videofiles:
                    infilenames.append(infilename)
                selectedinfilename = self.ask_user_to_select_video(infilenames)
                print >> sys.stderr , "selectedinfilename == None" , selectedinfilename , len(selectedinfilename)
                if selectedinfilename is None:
                    print >>sys.stderr,"videoplay: play: User selected no video"
                    return
                for infilename,diskfilename in videofiles:
                    if infilename == selectedinfilename:
                        selectedoutfilename = diskfilename
            else:
                selectedinfilename = videofiles[0][0]
                selectedoutfilename = videofiles[0][1]
        else:
            #print >> sys.stderr , "videoplay: play: selectedinfilename not None" , selectedinfilename , len(selectedinfilename)
            for infilename,diskfilename in videofiles:
                if infilename == selectedinfilename:
                    selectedoutfilename = diskfilename
        if self.videoframe is not None:
            self.videoframe.get_videopanel().SetLoadingText(selectedinfilename)
                
        # 23/02/10 Boudewijn: This Download does not contain the
        # selectedinfilename in the available files.  It is likely
        # that this is a multifile torrent and that another file was
        # previously selected for download.
        if selectedoutfilename is None:
            return self.play_vod(ds, selectedinfilename)

        print >> sys.stderr , "videoplay: play: PROGRESS" , ds.get_progress()
        complete = ds.get_progress() == 1.0 or ds.get_status() == DLSTATUS_SEEDING

        bitrate = tdef.get_bitrate(selectedinfilename)
        if bitrate is None and not complete:
            video_analyser_path = self.utility.config.Read('videoanalyserpath')
            if not os.access(video_analyser_path,os.F_OK):
                self.onError(self.utility.lang.get('videoanalysernotfound'),video_analyser_path,self.utility.lang.get('videoanalyserwhereset'))
                return

        # The VLC MediaControl API's playlist_add_item() doesn't accept unicode filenames.
        # So if the file to play is unicode we play it via HTTP. The alternative is to make
        # Tribler save the data in non-unicode filenames.
        #
        flag = self.playbackmode == PLAYBACKMODE_INTERNAL and not self.is_ascii_filename(selectedoutfilename)
        
        if complete:
            print >> sys.stderr, 'videoplay: play: complete'
            if flag:
                self.play_file_via_httpserv(selectedoutfilename)
            else:
                self.play_file(selectedoutfilename)
            
            self.manage_others_when_playing_from_file(d)
            # Fake it, to get DL status reporting for right Download
            self.set_vod_download(d)
        else:
            print >> sys.stderr, 'videoplay: play: not complete'
            self.play_vod(ds,selectedinfilename)


    def play_vod(self,ds,infilename):
        """ Called by GUI thread when clicking "Play ASAP" button """

        d = ds.get_download()
        tdef = d.get_def()
        # For multi-file torrent: when the user selects a different file, play that
        oldselectedfile = None
        if not tdef.get_live() and ds.is_vod() and tdef.is_multifile_torrent():
            oldselectedfiles = d.get_selected_files()
            oldselectedfile = oldselectedfiles[0] # Should be just one
        
        # 1. (Re)Start torrent in VOD mode
        switchfile = (oldselectedfile is not None and oldselectedfile != infilename) 

        print >> sys.stderr, ds.is_vod() , switchfile , tdef.get_live()
        if not ds.is_vod() or switchfile or tdef.get_live():

            
            if switchfile:
                if self.playbackmode == PLAYBACKMODE_INTERNAL:
                    self.videoframe.get_videopanel().Reset()
            
            #[proceed,othertorrentspolicy] = self.warn_user(ds,infilename)
            proceed = True
            othertorrentspolicy = OTHERTORRENTS_STOP_RESTART
            
            if not proceed:
                # User bailing out
                return

            if DEBUG:
                print >>sys.stderr,"videoplay: play_vod: Enabling VOD on torrent",`d.get_def().get_name()`

            self.manage_other_downloads(othertorrentspolicy,targetd = d)

            # Restart download
            d.set_video_event_callback(self.sesscb_vod_event_callback)
            d.set_video_events(self.get_supported_vod_events())
            if d.get_def().is_multifile_torrent():
                d.set_selected_files([infilename])
            print >>sys.stderr,"videoplay: play_vod: Restarting existing Download",`ds.get_download().get_def().get_infohash()`
            self.set_vod_download(d)
            d.restart()

    def restart_other_downloads(self, download_state_list):
        if len(download_state_list) == 0:
            return
        
        def get_vod_download_status(default):
            for download_state in download_state_list:
                if self.vod_download == download_state.get_download():
                    return download_state.get_status()
            return default

        if self.resume_by_system:
            # resume when there is no VOD download
            if self.vod_download is None:
                self.resume_by_system += 1
                if DEBUG: print >> sys.stderr, "VideoPlayer: restart_other_downloads: Resume because vod_download is None", "(%d)" % self.resume_by_system

            # resume when the VOD download is not part of download_state_list
            elif not self.vod_download in [download_state.get_download() for download_state in download_state_list]:
                self.resume_by_system += 1
                if DEBUG:
                    print >> sys.stderr, "VideoPlayer: restart_other_downloads: Resume because", `self.vod_download.get_def().get_name()`, "not in list", "(%d)" % self.resume_by_system
                    print >> sys.stderr, "VideoPlayer: list:", `[download_state.get_download().get_def().get_name() for download_state in download_state_list]`

            # resume when the VOD download has finished downloading
            elif not get_vod_download_status(DLSTATUS_ALLOCATING_DISKSPACE) in (DLSTATUS_ALLOCATING_DISKSPACE, DLSTATUS_WAITING4HASHCHECK, DLSTATUS_HASHCHECKING, DLSTATUS_DOWNLOADING):
                self.resume_by_system += 1
                if DEBUG:
                    print >> sys.stderr, "VideoPlayer: restart_other_downloads: Resume because vod_download_status is inactive", "(%d)" % self.resume_by_system
                    print >> sys.stderr, "VideoPlayer: status:", dlstatus_strings[get_vod_download_status(DLSTATUS_ALLOCATING_DISKSPACE)]

            # otherwise we do not resume
            else:
                self.resume_by_system = 1

            # because of threading issues it is possible that we have
            # false positives. therefore we will only resume torrents
            # after we checked 2 times (once every second)
            if self.resume_by_system > 2:
                self.resume_by_system = 0

                # sometimes the self.vod_download stays set to a
                # download class that is no longer downloading
                self.set_vod_download(None)

                for download_state in download_state_list:
                    download = download_state.get_download()
                    torrent_def = download.get_def()
                    infohash = torrent_def.get_infohash()
                    
                    from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
                    self.user_download_choice = UserDownloadChoice.get_singleton()
                    user_state = self.user_download_choice.get_download_state(infohash)

                    # resume a download unless the user explisitly
                    # stopped the download
                    if not user_state == "stop":
                        if DEBUG: print >> sys.stderr, "VideoPlayer: restart_other_downloads: Restarting", `download.get_def().get_name()`
                        download.set_mode(DLMODE_NORMAL)
                        download.restart()

    def manage_other_downloads(self,othertorrentspolicy, targetd = None):
        self.resume_by_system = 1
        if DEBUG: print >> sys.stderr, "VideoPlayer: manage_other_downloads"

        policy_stop = othertorrentspolicy == OTHERTORRENTS_STOP or \
                      othertorrentspolicy == OTHERTORRENTS_STOP_RESTART

        for download in self.utility.session.get_downloads():
            if download.get_def().get_live():
                # Filter out live torrents, they are always
                # removed. They stay in myPreferenceDB so can be
                # restarted.
                if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Remove live", `download.get_def().get_name()`
                self.utility.session.remove_download(download)

            elif download == targetd:
                if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Leave", `download.get_def().get_name()`
                download.stop()
                
            elif policy_stop:
                if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Stop", `download.get_def().get_name()`
                download.stop()

            else:
                if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Ignore", `download.get_def().get_name()`

    def manage_others_when_playing_from_file(self,targetd):
        """ When playing from file, make sure all other Downloads are no
        longer in VOD mode, so they won't interrupt the playback.
        """
        activetorrents = self.utility.session.get_downloads()
        for d in activetorrents:
            if d.get_mode() == DLMODE_VOD:
                if d.get_def().get_live():
                    #print >>sys.stderr,"videoplay: manage_when_file_play: Removing live",`d.get_def().get_name()`
                    self.utility.session.remove_download(d)
                else:
                    #print >>sys.stderr,"videoplay: manage_when_file_play: Restarting in NORMAL mode",`d.get_def().get_name()`
                    d.stop()
                    d.set_mode(DLMODE_NORMAL)
                    d.restart()




    def start_and_play(self,tdef,dscfg, selectedinfilename = None):
        """ Called by GUI thread when Tribler started with live or video torrent on cmdline """

        # ARNO50: > Preview1: TODO: make sure this works better when Download already existed.
        

        if selectedinfilename == None:
            if not tdef.get_live():
                videofiles = tdef.get_files(exts=videoextdefaults)
                if len(videofiles) == 1:
                    selectedinfilename = videofiles[0]
                elif len(videofiles) > 1:
                    selectedinfilename = self.ask_user_to_select_video(videofiles)

        if selectedinfilename or tdef.get_live():
            if tdef.is_multifile_torrent():
                dscfg.set_selected_files([selectedinfilename])

            othertorrentspolicy = OTHERTORRENTS_STOP_RESTART
            self.manage_other_downloads(othertorrentspolicy,targetd = None)

            # Restart download
            dscfg.set_video_event_callback(self.sesscb_vod_event_callback)
            dscfg.set_video_events(self.get_supported_vod_events())
            print >>sys.stderr,"videoplay: Starting new VOD/live Download",`tdef.get_name()`

            download = self.utility.session.start_download(tdef,dscfg)
   
            if self.videoframe is not None:         
                self.videoframe.get_videopanel().SetLoadingText(selectedinfilename)
            

            self.set_vod_download(download)
            return download
        else:
            return None
        
    
    def sesscb_vod_event_callback(self,d,event,params):
        """ Called by the Session when the content of the Download is ready
         
        Called by Session thread """
        
        print >>sys.stderr,"videoplay: sesscb_vod_event_callback called",currentThread().getName(),"###########################################################"
        wx.CallAfter(self.gui_vod_event_callback,d,event,params)

    def gui_vod_event_callback(self,d,event,params):
        """ Also called by SwarmPlayer """

        print >>sys.stderr,"videoplay: gui_vod_event:",event
        if event == VODEVENT_START:
            filename = params["filename"]
            mimetype = params["mimetype"]
            stream   = params["stream"]
            length   = params["length"]

            if filename:
                self.play_file(filename)
            else:
                if d.get_def().get_live():
                    cachestream = stream
                    blocksize = d.get_def().get_piece_length()
                else:
                    piecelen = d.get_def().get_piece_length()
                    if piecelen > 2 ** 17:
                        # Arno, 2010-01-21:
                        # Workaround for streams with really large piece
                        # sizes. For some content/containers, VLC can do
                        # GET X-, GET X+10K-, GET X+20K HTTP requests
                        # and we would answer these by putting megabytes
                        # into the stream buffer, of which only 10K would be
                        # used. This kills performance. Hence I add a caching
                        # stream that tries to resolve answers from its internal
                        # buffer, before reading the engine's stream.
                        # This works, but only if the HTTP server doesn't
                        # read too aggressively, i.e., uses small blocksize.
                        #
                        cachestream = SmartCachingStream(stream)

                        blocksize = max(32768,piecelen/8)
                    else:
                        cachestream = stream
                        blocksize = piecelen

                if d.get_def().get_live() and is_ogg(d.get_def().get_name_as_unicode()):
                    # Live Ogg stream. To support this we need to do
                    # two things:
                    # 1. Write Ogg headers (stored in .tstream)
                    # 2. Find first Ogg page in stream.
                    cachestream = OggMagicLiveStream(d.get_def(),stream)

                
                # Estimate duration. Video player (e.g. VLC) often can't tell
                # when streaming.
                estduration = None
                if d.get_def().get_live():
                    # Set correct Ogg MIME type
                    if is_ogg(d.get_def().get_name_as_unicode()):
                        params['mimetype'] = 'application/ogg'
                else:
                    file = None
                    if d.get_def().is_multifile_torrent():
                        file = d.get_selected_files()[0]
                    bitrate = d.get_def().get_bitrate(file)
                    if bitrate is not None:
                        estduration = float(length) / float(bitrate)
                    
                    # Set correct Ogg MIME type
                    if file is None:
                        if is_ogg(d.get_def().get_name_as_unicode()):
                            params['mimetype'] = 'application/ogg'
                    else:
                        if is_ogg(file):
                            params['mimetype'] = 'application/ogg'
                        

                    
                streaminfo = {'mimetype':mimetype,'stream':cachestream,'length':length,'blocksize':blocksize,'estduration':estduration}
                self.play_stream(streaminfo)
                
        elif event == VODEVENT_PAUSE:
            if self.videoframe is not None: 
                self.videoframe.get_videopanel().Pause()
            self.set_player_status("Buffering...")
        elif event == VODEVENT_RESUME:
            if self.videoframe is not None:
                self.videoframe.get_videopanel().Resume()
            self.set_player_status("")

    def ask_user_to_select_video(self,videofiles):
        dlg = VideoChooser(self.videoframe.get_window(),self.utility,videofiles,title='Tribler',expl='Select which file to play')
        result = dlg.ShowModal()
        if result == wx.ID_OK:
            index = dlg.getChosenIndex()
            filename = videofiles[index]
        else:
            filename = None
        dlg.Destroy()
        return filename

    def is_ascii_filename(self,filename):
        if isinstance(filename,str):
            return True
        try:
            filename.encode('ascii','strict')
            return True
        except:
            print_exc()
            return False

    def warn_user(self,ds,infilename):
        
        islive = ds.get_download().get_def().get_live()
        if islive and not self.other_downloads:
            # If it's the only download and live, don't warn.
            return
        
        dlg = VODWarningDialog(self.videoframe.get_window(),self.utility,ds,infilename,self.other_downloads,islive)
        result = dlg.ShowModal()
        othertorrentspolicy = dlg.get_othertorrents_policy()
        dlg.Destroy()
        return [result == wx.ID_OK,othertorrentspolicy]

    def create_url(self,videoserver,upath):
        schemeserv = 'http://127.0.0.1:'+str(videoserver.get_port())
        asciipath = unicode2str(upath)
        return schemeserv+urllib.quote(asciipath)



    def get_video_player(self,ext,videourl,mimetype=None):

        video_player_path = self.utility.config.Read('videoplayerpath')
        if DEBUG:
            print >>sys.stderr,"videoplay: Default player is",video_player_path

        if mimetype is None:
            if sys.platform == 'win32':
                # TODO: Use Python's mailcap facility on Linux to find player
                [mimetype,playcmd] = win32_retrieve_video_play_command(ext,videourl)
                if DEBUG:
                    print >>sys.stderr,"videoplay: Win32 reg said playcmd is",playcmd
                    
            if mimetype is None:
                if ext == '.avi':
                    # Arno, 2010-01-08: Hmmm... video/avi is not official registered at IANA
                    mimetype = 'video/avi'
                elif ext == '.mpegts' or ext == '.ts':
                    mimetype = 'video/mp2t'
                else:
                    mimetype = 'video/mpeg'
        else:
            if sys.platform == 'win32':
                [mimetype,playcmd] = win32_retrieve_playcmd_from_mimetype(mimetype,videourl)

        if self.playbackmode == PLAYBACKMODE_INTERNAL:
            if DEBUG:
                print >>sys.stderr,"videoplay: using internal player"
            return [mimetype,videourl]
        elif self.playbackmode == PLAYBACKMODE_EXTERNAL_MIME and sys.platform == 'win32':
            if playcmd is not None:
                cmd = 'start /B "TriblerVideo" '+playcmd
                return [mimetype,cmd]

        if DEBUG:
            print >>sys.stderr,"videoplay: Defaulting to default player",video_player_path
        qprogpath = quote_program_path(video_player_path)
        #print >>sys.stderr,"videoplay: Defaulting to quoted prog",qprogpath
        if qprogpath is None:
            return [None,None]
        qvideourl = self.escape_path(videourl)
        playcmd = qprogpath+' '+qvideourl
        if sys.platform == 'win32':
            cmd = 'start /B "TriblerVideo" '+playcmd
        elif sys.platform == 'darwin':
            cmd = 'open -a '+playcmd
        else:
            cmd = playcmd
        if DEBUG:
            print >>sys.stderr,"videoplay: using external user-defined player by executing ",cmd
        return [mimetype,cmd]



    def exec_video_player(self,cmd):
        if DEBUG:
            print >>sys.stderr,"videoplay: Command is @"+cmd+"@"
        # I get a weird problem on Linux. When doing a
        # os.popen2("vlc /tmp/file.wmv") I get the following error:
        #[00000259] main interface error: no suitable interface module
        #[00000001] main private error: interface "(null)" initialization failed
        #
        # The only thing that appears to work is
        # os.system("vlc /tmp/file.wmv")
        # but that halts Tribler, as it waits for the created shell to
        # finish. Hmmmm....
        #
        try:
            if sys.platform == 'win32':
                #os.system(cmd)
                (self.player_out,self.player_in) = os.popen2( cmd, 'b' )
            else:
                (self.player_out,self.player_in) = os.popen2( cmd, 'b' )
        except Exception, e:
            print_exc()
            self.onError(self.utility.lang.get('videoplayerstartfailure'),cmd,str(e.__class__)+':'+str(e))



    def escape_path(self,path):
        if path[0] != '"' and path[0] != "'" and path.find(' ') != -1:
            if sys.platform == 'win32':
                # Add double quotes
                path = "\""+path+"\""
            else:
                path = "\'"+path+"\'"
        return path


    def onError(self,action,value,errmsg=u''):
        self.onMessage(wx.ICON_ERROR,action,value,errmsg)

    def onWarning(self,action,value,errmsg=u''):
        self.onMessage(wx.ICON_INFORMATION,action,value,errmsg)

    def onMessage(self,icon,action,value,errmsg=u''):
        # Don't use language independence stuff, self.utility may not be
        # valid.
        msg = action
        msg += '\n'
        msg += value
        msg += '\n'
        msg += errmsg
        msg += '\n'
        dlg = wx.MessageDialog(None, msg, self.utility.lang.get('videoplayererrortitle'), wx.OK|icon)
        result = dlg.ShowModal()
        dlg.Destroy()

    def set_vod_download(self,d):
        self.vod_download = d
        
    def get_vod_download(self):
        return self.vod_download

    #
    # Set information about video playback progress that is displayed
    # to the user.
    #
    @forceWxThread
    def set_content_name(self,name):
        if self.videoframe is not None:
            self.videoframe.get_videopanel().SetContentName(name)

    @forceWxThread
    def set_content_image(self,wximg):
        if self.videoframe is not None:
            self.videoframe.get_videopanel().SetContentImage(wximg)
    
    @forceWxThread
    def set_player_status(self,msg):
        if self.videoframe is not None:
            self.videoframe.get_videopanel().SetPlayerStatus(msg)

    @forceWxThread
    def set_player_status_and_progress(self,msg,pieces_complete):
        if self.videoframe is not None:
            self.videoframe.get_videopanel().UpdateStatus(msg,pieces_complete)
    
    @forceWxThread
    def set_save_button(self,enable,savebutteneventhandler):
        if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None:
            self.videoframe.get_videopanel().EnableSaveButton(enable,savebutteneventhandler)

    def get_state(self):
        if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None:
            return self.videoframe.get_videopanel().GetState()
        else:
            return MEDIASTATE_PLAYING

    def determine_playbackmode(self):
        feasible = return_feasible_playback_modes(self.utility.getPath())
        if self.preferredplaybackmode in feasible:
            self.playbackmode = self.preferredplaybackmode
        else:
            self.playbackmode = feasible[0]

    def get_playbackmode(self):
        return self.playbackmode

    #def set_preferredplaybackmode(self,mode):
    #    This is a bit complex: If there is no int. player avail we change
    #    the VideoFrame to contain some minimal info. Would have to dynamically
    #    change that back if we allow dynamic switching of video player.
    #    self.preferredplaybackmode = mode

    #
    # Internal methods
    #
    def videohttpserver_error_callback(self,e,url):
        """ Called by HTTP serving thread """
        wx.CallAfter(self.videohttpserver_error_guicallback,e,url)
        
    def videohttpserver_error_guicallback(self,e,url):
        print >>sys.stderr,"videoplay: Video HTTP server reported error",str(e)
        # if e[0] == ECONNRESET and self.closeextplayercallback is not None:
        if self.closeextplayercallback is not None:
            self.closeextplayercallback()

    def videohttpserver_set_status_callback(self,status):
        """ Called by HTTP serving thread """
        wx.CallAfter(self.videohttpserver_set_status_guicallback,status)

    def videohttpserver_set_status_guicallback(self,status):
        self.videoframe.get_videopanel().SetPlayerStatus(status)
 



class VideoChooser(wx.Dialog):
    
    def __init__(self,parent,utility,filelist,title=None,expl=None):
        
        self.utility = utility
        self.filelist = []
        
        # Convert to Unicode for display
        for file in filelist:
            u = bin2unicode(file)
            self.filelist.append(u)

        if DEBUG:
            print >>sys.stderr,"VideoChooser: filelist",self.filelist
        
        style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
        if title is None:
            title = self.utility.lang.get('selectvideofiletitle')
        wx.Dialog.__init__(self,parent,-1,title,style=style)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        filebox = wx.BoxSizer(wx.VERTICAL)
        self.file_chooser=wx.Choice(self, -1, wx.Point(-1, -1), wx.Size(300, -1), self.filelist)
        self.file_chooser.SetSelection(0)
        
        if expl is None:
            self.utility.lang.get('selectvideofile')
        filebox.Add(wx.StaticText(self, -1, expl), 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
        filebox.Add(self.file_chooser)
        sizer.Add(filebox, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
        
        buttonbox = wx.BoxSizer(wx.HORIZONTAL)
        okbtn = wx.Button(self, wx.ID_OK, label=self.utility.lang.get('ok'), style = wx.BU_EXACTFIT)
        buttonbox.Add(okbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
        cancelbtn = wx.Button(self, wx.ID_CANCEL, label=self.utility.lang.get('cancel'), style = wx.BU_EXACTFIT)
        buttonbox.Add(cancelbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
        sizer.Add(buttonbox, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)

        self.SetSizerAndFit(sizer)

    def getChosenIndex(self):        
        return self.file_chooser.GetSelection()



class VODWarningDialog(wx.Dialog):
    
    def __init__(self, parent, utility, ds, infilename, other_downloads, islive):
        self.parent = parent
        self.utility = utility

        style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
        if islive:
            title = self.utility.lang.get('livewarntitle')
        else:
            title = self.utility.lang.get('vodwarntitle')
        
        wx.Dialog.__init__(self,parent,-1,title,style=style)

        if islive:
            msg = self.utility.lang.get('livewarngeneral')
        else:
            msg = self.utility.lang.get('vodwarngeneral')
        
        """
        if bitrate is None:
            msg += self.utility.lang.get('vodwarnbitrateunknown')
            msg += self.is_mov_file(videoinfo)
            msg += self.utility.lang.get('vodwarnconclusionno')
        elif bitrate > maxuploadrate and maxuploadrate != 0:
            s = self.utility.lang.get('vodwarnbitrateinsufficient') % (str(bitrate/1024),str(maxuploadrate)+" KB/s")
            msg += s
            msg += self.is_mov_file(videoinfo)
            msg += self.utility.lang.get('vodwarnconclusionno')
        elif bitrate > maxmeasureduploadrate and maxuploadrate == 0:
            s = self.utility.lang.get('vodwarnbitrateinsufficientmeasured') % (str(bitrate/1024),str(maxuploadrate)+" KB/s")
            msg += s
            msg += self.is_mov_file(videoinfo)
            msg += self.utility.lang.get('vodwarnconclusionno')
            
        else:
            if maxuploadrate == 0:
                rate = self.utility.lang.get('unlimited')
            else:
                rate = str(maxuploadrate)+" KB/s"
            s = self.utility.lang.get('vodwarnbitratesufficient') % (str(bitrate/1024),rate)
            msg += s
            extra = self.is_mov_file(videoinfo)
            if extra  == '':
                msg += self.utility.lang.get('vodwarnconclusionyes')
            else:
                msg += extra
                msg += self.utility.lang.get('vodwarnconclusionno')
        
        """
        sizer = wx.BoxSizer(wx.VERTICAL)
        text = wx.StaticText(self, -1, msg)
        text.Wrap(500)
        sizer.Add(text, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5)

        # 22/08/08 boudewijn: only show the selectbox when there are
        # torrents that are actively downloading
        if other_downloads:
            otherslist = [self.utility.lang.get('vodrestartothertorrents'),
                          self.utility.lang.get('vodstopothertorrents'),
                          self.utility.lang.get('vodleaveothertorrents')]

            othersbox = wx.BoxSizer(wx.VERTICAL)
            self.others_chooser=wx.Choice(self, -1, wx.Point(-1, -1), wx.Size(-1, -1), otherslist)
            self.others_chooser.SetSelection(OTHERTORRENTS_STOP_RESTART)

            othersbox.Add(wx.StaticText(self, -1, self.utility.lang.get('vodwhataboutothertorrentspolicy')), 1, wx.ALIGN_CENTER_VERTICAL)
            othersbox.Add(self.others_chooser)
            sizer.Add(othersbox, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
        else:
            self.others_chooser = None

        sizer.Add(wx.StaticText(self, -1, self.utility.lang.get('vodwarnprompt')), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
        

        buttonbox = wx.BoxSizer(wx.HORIZONTAL)
        okbtn = wx.Button(self, wx.ID_OK, label=self.utility.lang.get('yes'), style = wx.BU_EXACTFIT)
        buttonbox.Add(okbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
        cancelbtn = wx.Button(self, wx.ID_CANCEL, label=self.utility.lang.get('no'), style = wx.BU_EXACTFIT)
        buttonbox.Add(cancelbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5)
        sizer.Add(buttonbox, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)

        self.SetSizerAndFit(sizer)

    def get_othertorrents_policy(self):
        if self.others_chooser:
            idx = self.others_chooser.GetSelection()
        else:
            idx = OTHERTORRENTS_STOP_RESTART
        if DEBUG:
            print >>sys.stderr,"videoplay: Other-torrents-policy is",idx
        return idx
    
    def is_mov_file(self,videoinfo):
        orig = videoinfo['inpath']
        (prefix,ext) = os.path.splitext(orig)
        low = ext.lower()
        if low == '.mov':
            return self.utility.lang.get('vodwarnmov')
        else:
            return ''
            

def parse_playtime_to_secs(hhmmss):
    if DEBUG:
        print >>sys.stderr,"videoplay: Playtime is",hhmmss
    r = re.compile("([0-9]+):*")
    occ = r.findall(hhmmss)
    t = None
    if len(occ) > 0:
        if len(occ) == 3:
            # hours as well
            t = int(occ[0])*3600 + int(occ[1])*60 + int(occ[2])
        elif len(occ) == 2:
            # minutes and seconds
            t = int(occ[0])*60 + int(occ[1])
        elif len(occ) == 1:
            # seconds
            t = int(occ[0])
    return t

def return_feasible_playback_modes(syspath):
    l = []
    try:
        if sys.platform == "darwin":
            oldpath = os.getcwd()
            os.chdir(os.path.join(syspath,'vlc','lib'))
            import vlc.lib.vlc as vlc
            os.chdir(oldpath)
        else:
            import Tribler.vlc as vlc
        
        # Niels: check version of vlc
        version = vlc.libvlc_get_version()
        subversions = version.split(".")
        if len(subversions) > 2:
            version = subversions[0]+"."+subversions[1]
        version = float(version)
        if version < 0.9:
            raise Exception("Incorrect vlc version. We require at least version 0.9, this is %s"%version)

        if USE_VLC_RAW_INTERFACE:
            # check if the special raw interface is available
            # pylint: disable-msg=E1101
            if not inspect.ismethoddescriptor(vlc.MediaControl.set_raw_callbacks):
                raise Exception("Incorrect vlc plugin. This does not provide the set_raw_callbacks method")
            # pylint: enable-msg=E1101
        l.append(PLAYBACKMODE_INTERNAL)
    except Exception:
        print_exc()
    
    if sys.platform == 'win32':
        l.append(PLAYBACKMODE_EXTERNAL_MIME)
        l.append(PLAYBACKMODE_EXTERNAL_DEFAULT)
    else:
        l.append(PLAYBACKMODE_EXTERNAL_DEFAULT)
    return l

