#if 0 /*
# -----------------------------------------------------------------------
# cd_burn.py - Plugin for copying files to a cd
# -----------------------------------------------------------------------
# Change log:
#  0.03 :
#    - You can now burn DVD Video from your rips
#  0.02 :
#    - BurnCD is now a sub menu
#    - Directory burning
# ----------------------------------------------------------------------- */
#endif

import sys
import threading
import re
import shutil

import util.fileops
import plugin
import rc

import os
import pickle

import string
import menu
import time
import item
import re
import glob
import config
import popen2
import signal

import select
import fcntl, FCNTL

from gui.PopupBox import PopupBox
from gui.ConfirmBox import ConfirmBox
from gui.AlertBox import AlertBox
#from gui.YesNoBox import YesNoBox
from popen2 import Popen3,Popen4
from os import *
from stat import *
from os.path import *

class Logger:
    def __init__(self):
        config.LOGDIR = '/tmp'
        self.filename = '%s/%s-%s.log' % (config.LOGDIR, 'burn_cd-helpers', os.getuid())
	print self.filename
        self.file     = file(self.filename,"a")
	
    def log (self,line=None):
	self.file.write(line + "\n")
	self.file.flush()

class BurnCDItem:
    def __init__(self,item,filename=None,plugin=None,menu=None,burn_mode="data_cd"):
        self.item          = item
	#self.menuw         = menu 
	self.plugin        = plugin 
	self.files         = []
	self.volume_name   = None
	self.burn_mode     = burn_mode 

    def menu_back (self):
	# go back in menustack
        #for i in range(2):
        for i in range(1):
     	    self.menuw.back_one_menu(arg='reload')
        self.menuw.refresh



    #starts de decision process of burning the CD
    def burn (self, arg=None, menuw=None):
	if self.burn_mode == "data_cd":
		self.burn_data_cd()
	elif self.burn_mode == "dvd_video":
		self.burn_dvd_video()
	elif self.burn_mode == "audio_cd":
		self.burn_audio_cd()
	else:
		AlertBox(text=_('Not Yet implemented :)')).show()
		return

    #checks if the file "program" with the program name exists and is executable
    #if not displays a alert box and returns 0
    def check_program (self, program=None, program_name=None) :
	if not ( os.path.exists(program) and os.path.isfile(program) and access(program, X_OK)  ) :
	  print "CDBURN : Progam Error"
          AlertBox(text=_('Cannot find %s (%s). Please configure the right path in your config file and make sure it has the right permissions.' % (program_name,program)), handler=self.menu_back).show()
          return 0
	else:
	  return 1

    def burn_audio_cd(self):
	"""
	Checks for burnlist cleanup, mplayer availability
	"""
        if not self.check_program(program=config.MPLAYER_CMD,  program_name="mplayer"):

		return
	
	if not self.clean_up_burndir():
	    	return

  	#ConfirmBox(text=_('Start burning %s audio files?' % len(self.files)),
        #             handler=self.start_burning, default_choice=0).show()
        self.start_burning()


    def clean_up_burndir (self):
	"""
	Tries to cleanup /tmp/burndir, if it not exists it try's to create it
	the final result of this function must a be a existing and empty
	/tmp/burnlist
	"""
	try:
	    if os.stat("/tmp/burnlist"):
	      for a in os.listdir("/tmp/burnlist"):
		os.unlink("/tmp/burnlist/" + a)
	except:
	    AlertBox(text='"Aborted, could not empty /tmp/burnlist"')
	    return 0

	try:
	    if not os.stat("/tmp/burnlist"):
 	           os.makedirs("/tmp/burnlist", 0777)
	    else:
		   if os.listdir("/tmp/burnlist"):
			   os.system('rm -rf /tmp/burnlist/*')

        except:
	    os.system('rm -rf /tmp/burnlist/*')
	return 1

    # Starts the burning thread
    def start_burning (self, arg=None, menuw=None):
	self.thread_burn = main_burn_thread(token=self)
        self.thread_burn.start()
	#self.plugin.thread_burn = self.thread_burn
	#self.menu_back()

    #
    # Routines to find files for burning
    #

    #Finds the file of a single file item
    def findFileFromItem (self) :
        """
        Normally this would return the file name etc from the mmpython 
        item.. but i've hacked it for this test.
        """
	print "CDBURN : findFileFromItem() ran"
	self.volume_name = "test"
	self.files.append(self.item)

# Thread that really burns the CD
#
class main_burn_thread(threading.Thread):
    def __init__(self, token=None):
        threading.Thread.__init__(self)
	self.token       = token
	self.childs      = []
	self.running     = 0
	self.status      = ""
	self.stopping    = False
	self.logger      = Logger()

    def stop(self):
        self.stopping = True
        starttime = time.time()
	
        while self.stopping and (time.time()- starttime < 15):
            print 'Waiting for the thread to terminate...'
            time.sleep(1)


    def cleanup(self):
        for child in self.childs:
	    if child.poll() == 0:
	        continue
            try:
                _debug_('killing process group %d with signal 15' % (child.pid))
                os.kill(-child.pid, signal.SIGTERM)
            except OSError:
                _debug_('killing process group %d FAILED' % (child.pid))
                pass
                    
            for i in range(20):
                #_debug_('Waiting for process group %d to terminate...' % (child.pid))
                try:
                    p,s = os.waitpid((-child.pid), os.WNOHANG)
                except OSError:
                    _debug_('Process group %d exited' % (child.pid))
                    break
                if p == child.pid:
                    _debug_('Process group %d terminated with signal 15' % child.pid)
                    break
                time.sleep(0.1)
            else:
                try:
                    _debug_('killing process group %d with signal 9' % (child.pid))
                    os.kill(-child.pid, signal.SIGKILL)
                except OSError:
                    _debug_('killing process group %d FAILED' % (child.pid))
                    pass
                for i in range(20):
                    #_debug_('Waiting for process group %d to terminate...' % (child.pid))
                    try:
                        p,s = os.waitpid(-child.pid, os.WNOHANG)
                    except OSError:
                        _debug_('Process group %d exited' % (child.pid))
                        break
                    if p == child.pid:
                        _debug_('Process group %d terminated with signal 9' % child.pid)
                        break
                    time.sleep(0.1)

            self.running = 0 
	    self.status  = "Aborted by user"
   
    #mick, all childs are spwaned using this method
    def run_child(self,cmd=None,cwd=None,wait=0):
    	"""
    	Spwans a child using command cmd.
    	If cwd is filled the os will change to that dir before the spwan
    	If wait is 1 (or True), this function will wait for child "death" before returning
    	This function returns the child object, if it does not return any value, the process
     	 was aborted.
    	"""
	_debug_("CDBURN : run_child called...")
    	if cwd:
		_debug_("CDBURN : Changing working dir to %s" % cwd)
		os.chdir(cwd)

	cmd = cmd + " 2>&1"
	_debug_("CDBURN : run_child calling %s" %cmd )
	start = time.time()
    	child_app = util.popen3.Popen3(cmd)
        self.logger.log(cmd)
    	self.childs.append(child_app)
        #time to get pid...
	time.sleep(0.1)
	_debug_("CDBURN : run_child pid is %s" %child_app.pid )

    	if wait:
		print 'CDBURN : Waitting for %s' % child_app.pid
	        _debug_("CDBURN : run_child runtime is %s" %int(time.time()-start) )
		
		self.makeNonBlocking(child_app.fromchild.fileno())

		while child_app.poll() < 0:
                        try:
                            _debug_('Checking for WNOHANG')
			    #not sure what dies does??
                            #p,s = os.waitpid(child_app.pid, os.WNOHANG)
                            os.waitpid(-1, os.WNOHANG)
                            _debug_('WNOHANG went well..????')
                            #if p == child.pid:
                            #    _debug_('WNOHANG found on p,s: %s, %s'%(p,s))
                            #    break
                        #except OSError:
                        except Exception, e:
                            #_debug_('Process group %d exited' % child_app.pid)
                            _debug_('WNOHANG error %s ' % e)
                            pass
	                _debug_("CDBURN : run_child runtime is %s" %int(time.time()-start) )
			print "CDBURN : Polled %s (pid : %s), status is %s" % (cmd,child_app.pid,child_app.poll())
			if self.stopping:
				self.cleanup()
				self.stopping = False
				return

			try:
			 """
			 This allow us to parse the output of the commands,
			 its a approach to the status bar
			 """
			 while 1:
				line = child_app.fromchild.readline()
				if line:
				 pass
				 self.logger.log(line[0:-1])
				 #TODO, call the parser to get the progress
				else:
				 break
			except IOError:
			 #the fd was not ready to read
			 pass

			#print 'CDBURN : string = %s' % last_string 

			time.sleep(1)

		print 'CDBURN : %s exited with code %s' % (cmd,child_app.poll())
		if self.stopping:
			self.cleanup()
			self.stopping = False
			return
	return child_app

    def makeNonBlocking(self,fd):
    	fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    	try:
		fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY)
    	except AttributeError:
		fcntl.fcntl(fd, fcntl.F_SETFL, fl | fcntl.FNDELAY)

    def run(self, arg=None, menuw=None, token=None):
	"""
	Starts the burning process and helpers
	"""
	self.running = 1
	self.starttime = time.time()

	print 'CDBURN : running burning thread with token.burn_mode = %s' % self.token.burn_mode

	if self.token.burn_mode == "audio_cd":
		for a in self.token.files:
			self.status = "Converting %s" % os.path.basename(a)
			print "CDBURN : Converting %s" % os.path.basename(a)
			convert_cmd = 'mplayer -ao pcm -waveheader -vc dummy -vo null "%s"' % a

			conv_child = self.run_child(cmd=convert_cmd,cwd='/tmp/burnlist',wait=1)
			if not conv_child:
			   return

			if conv_child.poll() != 0:
				self.status = "Error : Could not convert %s" % os.path.basename(a)
				self.running = 0
				return

			print "CDBURN : Conversion done"
			os.rename('/tmp/burnlist/audiodump.wav', _('/tmp/burnlist/%s' % os.path.basename(a)))
			
		self.status = "Burning Audio to CD"
		config.CDBURN_CDRECORD_PATH = '/usr/bin/cdrecord'
		config.CDBURN_SPEED = '24'
		config.CDBURN_DEV = '1,0,0'
		config.CDBURN_AUDIO_MODE = 'tao'

		cdrec_cmd = '%s -s -blank=fast -eject -v speed=%s dev=%s -%s -pad -useinfo /tmp/burnlist/*' % (config.CDBURN_CDRECORD_PATH,config.CDBURN_SPEED,config.CDBURN_DEV,config.CDBURN_AUDIO_MODE) 
		print 'CDBURN : %s' % cdrec_cmd

		rec_child = self.run_child(cmd=cdrec_cmd,wait=1) 
		if not rec_child:
			return

		if rec_child.poll() != 0:
			self.status = "Error : Could burn audio tracks to CD" 
			self.running = 0
			return
		
	#lets wait for all childs to stop
	for child in self.childs:
            while child.poll() < 0:
		child.fromchild.readlines()
                _debug_('CDBURN : Waiting for process %d...' % child.pid)
                time.sleep(1)

        elapsed = (time.time() - self.starttime)
        print 'CDBURN : Time elapsed: %d' % elapsed
	self.running = 0 
	self.status = "Burn sucessfull (in %s sec)" % elapsed
        return

class PluginInterface(plugin.ItemPlugin):        
    """
    Enables writing selected item to compatable device.  So far we can burn
    files to cd, DVD (VIDEO_TS) to video dvd, and files (mp3 and Ogg) to 
    Audio cd.

    Place cd_burn.py in:
    freevo/plugins/.
    
    Activate in local_conf.py by:
    plugin.activate(cd_burn)
    """
	
    def __init__(self):
        plugin.ItemPlugin.__init__(self)
        self.device = ''
	self.item   = None
        self.thread_burn = None 

    def config(self):
        return [ ('CDBURN_CDRECORD_PATH', '/usr/bin/cdrecord', 'Path to cdrecord'),
                 ('CDBURN_MKISOFS_PATH', '/usr/bin/mkisofs', 'Path to mkisofs'),
		 # bruno:
		 # in tao (track at once mode) cd record will leave 2 sec gaps between tracks
		 # in dao (disk at once) no gaps at all, but not all drives support it
		 ('CDBURN_AUDIO_MODE','tao','CDRecord mode to burn audio cds'),
                 ('CDBURN_SPEED', '8', 'Speed to burn with cdrecord'),
		 # michaell:
		 # temp file?  Any reason why the user would declare this? Why not
		 # just declare the temp dir?
		 # bruno:
		 # you are right, i'll change this as soon as i have a time to cleanup the code
                 ('CDBURN_TEMP_FILE', '/tmp/image.img', 'Temp file name used by cdrecord'),
                 ('CDBURN_DEV', 'ATAPI:0,0,0', 'Device for cdrecord to burn with (not auto detected)') ]

    def show_t_status (self, arg=None, menuw=None):
        AlertBox(text=_('Burning status: %s' % self.thread_burn.status )).show()

    def stop_burning(self, arg,  menuw=None):
        pop = PopupBox(text=_('Interrupting burning process...'))
        pop.show()

        self.thread_burn.stop()
        pop.destroy()

	if menuw:
        	menuw.back_one_menu(arg='reload')
        	menuw.refresh

        AlertBox(text=_('Backup interrupted')).show()

    def actions(self, item):
        self.item = item
	show_burn_menu = 0;

	print _('CDBURN : Item type is %s' % item.type)

	if self.thread_burn and self.thread_burn.running == 1:
		show_burn_menu = 1;

	if ( item.type == 'audio' or item.type == 'image' or item.type == 'video' or item.type == 'dir' ):
		show_burn_menu = 1;

	print _('CDBURN : Should show the menu? %i' % show_burn_menu)

	if show_burn_menu:
		return [ (self.fill_menu, 'Burn CD') ]
	else:
		return []
		
    def draw_menu(self,menuw=None,items=None):
    #draws the menu with the options on the screen
	menu_items = []
	if items:
 
	 for a in items:
	   menu_items.append(menu.MenuItem(a[1],a[0]))

	 moviemenu = menu.Menu(_('CD Burn Menu'), menu_items)
         menuw.pushmenu(moviemenu)


    def fill_menu(self,arg=None, menuw=None): 
    #chooses the options available for this type of item
	to_return = []
	item = self.item
	print "CDBURN : Item type = %s" % item.type
	if self.thread_burn and self.thread_burn.running == 1:
	   to_return.append( (self.show_t_status, 'Show burning status' ));
	   to_return.append( (self.stop_burning, 'Stop the burn process' ));
	   return self.draw_menu(menuw=menuw,items=to_return)


	if item.type == 'dir':
	#dirs
	  print 'CDBURN : Dir has media : %s ' % item.media
	  try:
           cur = BurnCDItem(item=item, plugin=self,menu=menuw)
	   cur.findFromDir()

	   cur2 = BurnCDItem(item=item, plugin=self,menu=menuw,burn_mode="audio_cd")
	   cur2.findFromDirMatches(suffix=['mp3','ogg','wav'])

	   if cur.files:
             to_return.append(( cur.burn, 'Copy dir to CD') )
	   if len(cur2.files)>0:
	     to_return.append(( cur2.burn, 'Burn audio files (MP3, Wav and Ogg) as Audio CD'))
	  except Exception, e:
	     print e
	     pass

	try:
	#any item except dirs
	    if (not item.subitems) and (not item.type == 'dir') :
	     cur = BurnCDItem(item=item, plugin=self,menu=menuw)
	     cur.findFileFromItem()
	     #cur.addFilesFromItem()
	     if cur.files:
              to_return.append( ( cur.burn, _('Copy this file to CD')) ) 

	#any joined item except dirs
	    elif not item.type == 'dir' and item.subitems:
               for a in item.subitems:
	     	cur = BurnCDItem(item=a, plugin=self,menu=menuw)
	     	cur.findFileFromItem()
	     	if cur.files:
              		to_return.append( ( cur.burn, _('Copy %s to CD' % a.name )) ) 
	     
	except:
	    pass


	#single video file
	try:
	 if item.filename and item.type == 'video' and ( not item.subitems):
           cur = BurnCDItem(item=item, plugin=self,menu=menuw)
	   cur.findRelated()
	   #cur2 = BurnCDItem(ite=item, plugin=self,menu=menuw)
	   #cur2.findRelated(mode=1)

	   if len(cur.files) > 1:
             to_return.append(( cur.burn, _('Copy %s, and related, to CD' % item.name)) )
             #to_return.append(( cur2.delete, _('Delete %s, and related' % item.name)) )
	except:
	 pass

	#joined video files
	try:
 	 if item.subitems:
            if item.type == 'video':
               for a in item.subitems:
                   if a.filename:
                       cur = BurnCDItem(item=a, plugin=self,menu=menuw)
		       cur.findRelated()

		       if len(cur.files) > 1:
                          to_return.append(( cur.burn,    _('Copy %s, and related, file to CD' % a.name)) )
	except:
	 pass

        #DVD movie on file system (ir DIR containing VIDEO_TS and/or AUDIO_TS)
        #Not sure how freevo works this about but bellow if seems to be fairly
        #accurate..
        try:
            if item.type == 'video' and item.mode == 'dvd' and item.media == None:
                cur = BurnCDItem(item=item, plugin=self, menu=menuw, burn_mode="dvd_video")
                cur.findFileFromItem()

                if cur.files and len(cur.files) == 1:
	            print "CDBURN : Adding DVD-VIDEO Item to menu"
                    to_return.append(( cur.burn, 'Burn as DVD-VIDEO disc') )
	        elif len(self.files) > 1:
	            print "CDBURN : To many objects to burn into a DVD Video"

        except:
            print "CDBURN : Not possible to findFileFromItem for DVD Movie"

	if self.thread_burn and self.thread_burn.running == 0:
	   to_return.append( (self.show_t_status, 'Show last burn status/result' ));

	return self.draw_menu(menuw=menuw,items=to_return)

#
# Main function
#
if __name__ == "__main__":
    #file = sys.argv[1]
    print sys.argv[1]
    cur = BurnCDItem(item=sys.argv[1],burn_mode="audio_cd")
    cur.findFileFromItem()
    cur.burn()
   
