#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os, sys, signal
import time
import glob
import subprocess
import codecs
import threading
import textwrap
import tempfile
import urllib
import atexit

from PIL import Image,ImageChops,ImageOps
from PIL import ImageDraw, ImageFont
from optparse import OptionParser
from multiprocessing import Process, Lock, Pipe

sys.path.append('regzamod')
from Common.RzCmnSubs import dbg,dbg2,dbg0,error,setdbg
from Common.RzCmnSubs import isAudio,isImage,isVideo,isWebVideo,isWebContents,conv_encoding
from Common.RzCmnSubs import gs_encode_sys,gs_debug,gs_verbose,gs_ff_loglev
from Common.RzCmnSubs import gs_python,gs_prog_ytdl,gs_prog_ytdl_s,gs_prog_streamer,gs_streamer_conf

#---- external progs
prog_ffmpeg  ="win32/ffmpeg-RZ.exe"
prog_ffprobe ="win32/ffprobe.exe"
prog_mencoder="win32/mencoder.exe"


#---- youtube-dl options
#ytdl_opt_getinfo=""
#ytdl_opt_getinfo="--rm-cache-dir"
ytdl_opt_getinfo="--quiet --no-warnings"

#ytdl_opt_play=""
ytdl_opt_play="--quiet --no-warnings"

#---- globals
nicosubs=1   #=1:display subs (currently dummy sub)

nico_account="--username regzam_yhg@yahoo.co.jp --password regzam2014"
#nico_account=""
menc_account="-user regzam_yhg@yahoo.co.jp -passwd regzam2014"
auto_pipe=0  # conect PIPE by SYS, todo: need watch dog when i died
use_ffmpeg=0
use_mencoder=0
#timeup_getinfo   =15
m3u8=0



def getMinfo(mode,mode2,url,timeup):
	rc=0
	outbuf=[]
	
	dbg("RzNicoPlayer.getMinfo: Start mode=%d url=%s" % (mode,url))
	if(True or url.find("www.nicovideo.jp")>=0):
		subs=nicosubs
		account=nico_account
	else:
		subs=0		#not nicovideo
		account=""	

	#---- mode: type of prober(for get mediainfo)
	if(mode==1): # kick mencoder
		#mencder can't connect directly --> use youtube-dl
		#ffpara ="%s %s -o NULL" % (url,menc_account)
		ffpara ="- -o NULL"
		para=[prog_mencoder]+ffpara.split()
		p1=exec_prog(para,subprocess.PIPE,subprocess.PIPE,sys.stderr,False)
		out=p1.stdout
	elif(mode==2): # kick ffmpeg
		ffpara =" -i -"
		para=[prog_ffmpeg]+ffpara.split()
		p1=exec_prog(para,subprocess.PIPE,sys.stderr,subprocess.PIPE,False)
		out=p1.stderr
	else: # kick ffprobe
		ffpara =" -show_format -show_streams -loglevel fatal pipe:0"
		#para=prog_ffprobe+ffpara
		para=[prog_ffprobe]+ffpara.split()
		p1=exec_prog(para,subprocess.PIPE,subprocess.PIPE,sys.stderr,False)
		out=p1.stdout
	
	#---- mode2: type of streamer
	if(mode2==1): # use youtube-dl
		ytpara="%s %s %s -o -" % (ytdl_opt_getinfo,account,url)
		para=gs_prog_ytdl+ytpara.split()
		#exec_prog(args,stdi,stdo,stde,sh)
		p2=exec_prog(para,None,p1.stdin,sys.stderr,False)
		
	else:  #use LiveStreamer
		ytpara=" %s best --stdout" % (url)
		#para=[prog_streamer]+ytpara.split()
		para=gs_prog_streamer+ytpara.split()
		#exec_prog(args,stdi,stdo,stde,sh)
		p2=exec_prog(para,None,p1.stdin,sys.stderr,False)
	
	#---- watchdog_timer: kill above proc when timeup
	p3=Process(target=watchdog_timer2,args=(p1.pid,p2.pid,timeup))
	p3.start()

	#---- kick watchdog "thread" for detect death of child proc p2
	t1=threading.Thread(target=watchdog_wait, name="watchdog_wait", args=(p1.pid,p2))
	t1.start()

	#---- join pipe data
	while (False):
		#small buffer may cause audio stattering
		buf=p2.stdout.read(1000000)
		if(buf):
			p1.stdin.write(buf)
		else:
			break
			
	#---- read output
	while True:
		line=out.readline()
		if not line:
			break
		outbuf.append(line)
	p1.wait()
	p3.terminate()
	t1.join()
	dbg("NicoPlayerProc: getMinfo: mode=%d rc=%d" % (mode,rc))
	if(gs_verbose>0):
		dbg("NicoPlayerProc: getMinfo: outbuf=%s" % (outbuf))
	return (rc,outbuf)
	
def exec_prog(args,stdi,stdo,stde,sh):
	dbg("NicoPlayerProc: exec_prog: args=%s" % args)
	p=None
	try:
		cwd="./"
		p = subprocess.Popen(args, shell=sh, bufsize=-1, stdin=stdi, stdout=stdo,
			stderr=stde, close_fds=False)
	except OSError:
		dbg("NicoPlayerProc: exec_prog: Failed to exec %s" % args)
	#p.wait()
	return p

def watchdog_timer(pid,timeup):
	time.sleep(timeup)
	os.kill(pid,9)
	dbg("NicoPlayerProc: watchdog_timer: timeout=%d, kill pid=%d" % (timeup,pid))

def watchdog_timer2(pid1,pid2,timeup):
	time.sleep(timeup)
	os.kill(pid1,9)
	os.kill(pid2,9)
	dbg("NicoPlayerProc: watchdog_timer: timeout=%d, kill pid=%d" % (timeup,pid))

def watchdog_wait(kill_pid,wait_proc):
	wait_proc.wait()
	dbg("watchdog_wait: target proc terminated")
	time.sleep(2.0)
	try:
		if(kill_pid>0):
			# kill will cause error messasge on PMS
			os.kill(kill_pid,9) 
		else:
			# kill target must be me
			#sys.exit(1) # may not surely terminate
			os._exit(1) 
	except Exception,e:
		dbg("watchdog_wait: exception error=%r" % e)

#---- watchdog
def watchdog_pipe(pc,ppid,pid1,pid2):
	import time  #need here
	# kill child(pid) when parent(ppid) diled
	dbg("NicoPlayerProc: watchdog_pipe: started, mypid=%d parent pid=%d target_pid=%d,%d" % (os.getpid(),ppid,pid1,pid2))
	time1=time.time()
	
	try:
		#read pipe in blocking mode
		#i.e. wait untill parent die, b/c parent never send to the pipe
		pc.recv()
	except EOFError:
		dbg("NicoPlayerProc: watchdog: EOFError on pipe.recv")
		#parent seems to be killed
		pass	#Noop
	except IOError:
		dbg("NicoPlayerProc: watchdog: IOError on pipe.recv")
		pass	#Noop
	except:
		dbg("NicoPlayerProc: watchdog: Unknown Exception")
		pass	#Noop
	
	dbg("NicoPlayerProc: watchdog: kill pid=%d,%d" % (pid1,pid2))
	try:
		#os.kill(pid,signal.SIGKILL)
		os.kill(pid1,9)
		os.kill(pid2,9)
	except WindowsError:
		dbg("NicoPlayerProc: watchdog_pipe: WindowsError: pid=%d,%d may not exists" % (pid1,pid2))

	time2=time.time()
	time=time2-time1
	dbg("NicoPlayerProc: watchdog_pipe: play time=%f" % time)
	#sys.exit()
	os._exit(0)

#----------------------------------------------------------------------------------
# main
#----------------------------------------------------------------------------------
if __name__ == '__main__':
	
	#setdbg(gs_debug)
	dbg("NicoPlayerProc: Main Start")
	#---- test of getMinfo
	test=0
	if(test):
		mode=int(sys.argv[1])
		url=sys.argv[2]
		timeup=15
		(rc,outbuf)=getMinfo(mode,1,url,timeup)
		dbg("NicoPlayerProc: output=%s" % outbuf)
		#sys.exit(0)
		os._exit(0)
		
	#---- get main args 
	if(len(sys.argv)<3):
		dbg("NicoPlayerProc: Usage: RzNicoPlayer.py {video_in(URL)} {video_out} [options]")
		sys.exit(1)
	myprog=sys.argv[0]
	url=sys.argv[1]
	video_out=sys.argv[2]
	timeseek=0
	
	#---- get main args: can't use b/c many -xxx args of ffmpeg/mencoder are used: cause parse error
	parser = OptionParser()
	parser.add_option("--timeseek",		action="store",	dest="timeseek",	type="float"	,default=0.0)
	parser.add_option("--m2ts",			action="store",	dest="m2ts",		type="int"		,default=0)
	parser.add_option("--h264",			action="store",	dest="h264",		type="int"		,default=0)
	parser.add_option("--remux",		action="store",	dest="remux",		type="int"		,default=0)
	parser.add_option("--asp_hack",		action="store",	dest="asp_hack",	type="int"		,default=0)
	parser.add_option("--dpsize_w",		action="store",	dest="dpsize_w",	type="int"		,default=856)
	parser.add_option("--dpsize_h",		action="store",	dest="dpsize_h",	type="int"		,default=482)
	#parser.add_option("--prog_ffmpeg",	action="store",	dest="prog_ffmpeg",	type="string"	,default="win32/ffmpeg-RZ.exe")
	#parser.add_option("--prog_mencoder",action="store",dest="prog_mencoder",type="string"	,default="win32/mencoder.exe")
	#parser.add_option("--gs_prog_ytdl",	action="store",	dest="gs_prog_ytdl",	type="string"	,default="win32/youtube-dl/youtube-dl.exe")
	#(opt,args) = parser.parse_args()
	
	if(url.find("www.nicovideo.jp")<0):
		nicosubs=0	#not nicovideo
		nico_account=""
		
	#---- kick transcoder
	if(use_ffmpeg):
		#---- use ffmpeg ---- bad, cause pipe read error in many cases
		#---- seems can't handle well nicovideo, when piped stream(not seekable)
		ffpara =" -probesize 32 -analyzeduration 0"
		#ffpara+=" -ss %f -i - -f mpegts -vcodec mpeg2video -acodec ac3" % (timeseek)
		ffpara+=" -i - -f mpegts -vcodec mpeg2video -acodec ac3" # ffmpeg can't seek for pipe
		ffpara+=" -ab 256000 -q:v 1 -vf scale=720:480 -aspect 16/9 "
		ffpara+=" -loglevel %s -y %s" % (gs_ff_loglev,video_out)
		dbg("NicoPlayerProc: ftpara=%s" % ffpara)
		para=[prog_ffmpeg]+ffpara.split()
		p1=exec_prog(para,subprocess.PIPE,None,None,False)
		#time.sleep(2)
	elif(use_mencoder):
		#use mencoder --- good, slow but minimum fails
		#mcpara =  " -ss %f - -quiet -cache 16384 " % (opt.timeseek)
		mcpara =  " -ss %f - -quiet -cache 64000 -prefer-ipv6 " % (timeseek)
		mcpara += " -of mpeg -oac lavc -ovc lavc "
		mcpara += " -mpegopts format=mpeg2 "
		mcpara += " -lavcopts aspect=16/9:vcodec=mpeg2video:acodec=ac3:abitrate=128:keyint=5:vqscale=1:vqmin=2:vbitrate=8000 "
		#mcpara += " -vf scale=684:456,expand=720:480 -mc 0.1 -af lavcresample=48000 -srate 48000"
		mcpara += " -vf scale=854:482 -mc 0.2 -af lavcresample=48000 -srate 48000"
		mcpara += " -ofps 30000/1001 -o %s" % (video_out)
		para=[prog_mencoder]+mcpara.split()
		p1=exec_prog(para,subprocess.PIPE,None,None,False)
	else:
		#use given args
		para=[]
		dbg("NicoPlayerProc: argv=%s" % sys.argv)
		prog=sys.argv[3]
		if(prog.find("mencoder")>=0):
			use_mencoder=1
		for i in range(3,len(sys.argv)):
			if(sys.argv[i]=="m3u8"):
				m3u8=1
				continue
			#if(use_mencoder==0 and sys.argv[i]=="-ss"):
			if(True):
				if(sys.argv[i]=="-ss"):
					if(float(sys.argv[i+1])!=0):
						sys.argv[i+1]="0"	#can't seek on pipe
			else:
				if(sys.argv[i]=="-ss"):
					sys.argv[i+1]="--"
					continue
				elif(sys.argv[i]=="--"):
					continue
			para.append(sys.argv[i])
		if(nicosubs and use_mencoder):
			#subp="-ass -fontconfig -subfont C:\WINDOWS\Fonts\Arial.ttf -sid 100 -utf8 -sub regzamod/RzWEBplayer/NicoSubsDummy.ass "
			subp="-ass -utf8 -sub regzamod/RzWEBplayer/NicoSubsDummy.ass"
			para+=subp.split()
		p1=exec_prog(para,subprocess.PIPE,None,None,False)
	
	#---- kick youtube-dl/Livestreamer
	if(url.find("ustream")>=0 and url.find("recorded")<0 or m3u8):  # ustream live/channel's url is not video but playlist
		ytpara=" %s best --stdout" % (url)
		para=gs_prog_streamer+ytpara.split()
	else:
		#ytpara="%s %s %s -q -o -" % (ytdl_opt_play,nico_account,url)
		ytpara="%s %s %s -o -" % (ytdl_opt_play,nico_account,url)
		para=gs_prog_ytdl+ytpara.split()
	if(auto_pipe):
		p2=exec_prog(para,sys.stdin,p1.stdin,sys.stderr,False)
	else:
		p2=exec_prog(para,sys.stdin,subprocess.PIPE,sys.stderr,False)
	
	#---- kick watchdog proc ------
	# Process: run module as a separate process
	pp,pc=Pipe()
	p3=Process(target=watchdog_pipe,args=(pc,os.getpid(),p1.pid,p2.pid))
	p3.start()
	
	#---- kick watchdog "thread" for detect death of child proc p1
	#wdwait=threading.Thread(target=watchdog_wait, name="watchdog_wait", args=(os.getpid(),p1))
	wdwait=threading.Thread(target=watchdog_wait, name="watchdog_wait", args=(0,p1))
	wdwait.start()

	#---- pipe pumping within above two proc
	dbg("NicoPlayerProc: Transfer Start")
	if(auto_pipe):
		#already pipe joined, so there is no work to do
		pass
	else:
		#transfer data inter-pipe
		#child progs will die if i died. b/c pipe will disconnected
		loop=1
		cnt=0
		err=0
		while (loop):
			cnt+=1
			try:
				#small buffer may cause audio stattering
				buf=p2.stdout.read(50000)
				if(buf):
					#dbg("NicoPlayerProc: Write Start cnt=%d, len=%d" % (cnt,sys.getsizeof(buf)))
					#sys.stdout.flush()
					p1.stdin.write(buf)
					#p1.stdin.flush()
					#dbg("NicoPlayerProc: Write End cnt=%d" % cnt)
					#sys.stdout.flush()
				else:
					dbg("NicoPlayerProc: read ended, exit loop")
					sys.stdout.flush()
					p1.stdin.close()
					loop=0
			except:
				dbg("NicoPlayerProc: Exception caught, exit loop")
				loop=0
	
	dbg("NicoPlayerProc: Ending Start")
	sys.stdout.flush()
	p2.terminate()
	p1.wait();
	p3.terminate()
	time.sleep(3)
	#sys.exit()
	os._exit(0)
