#!/usr/bin/python
# vim:fdl=0:
import calendar,math,md5,optparse,os,re,signal,struct,time,zlib,zipfile,shutil,sys
try:
	import curses
except:
	curses=None
import pygame
try:
	pygame.display.init()
	pygame.joystick.init()
	for i in range(pygame.joystick.get_count()):
		pygame.joystick.Joystick(i).init()
except:
	pygame=None
from romlib import *

#----------------------------------------------------------
# Automatic player
#----------------------------------------------------------
#signal.signal(signal.SIGCHLD, signal.SIG_IGN)
EMU_TMP='/tmp/emu'
EMU_MAME='sdlmame'
if os.environ['USER']=='wis': EMU_TMP='~/emutmp'

class PlayFile:
	def __init__(self):
		self.exttype={
			'.st':'hatari --fullscreen --frameskip',
			'.adf':'uae -s use_gui=false -s gfx_fullscreen_amiga=true -0',
			'.smc':'zsnes',
			'.prg':'mess c64pal -quik',
			'.p00':'mess c64pal -quik',
			'.t64':'x64 -autostart',
			'.d64':'x64 -autostart',
			'.dsk':'mess cpc6128 -flop1',
			'.img':'mess pc -flop1',
			'.z80':'mess spectrum -dump',
			'.tzx':'mess spectrum -cass',
			'.rom':'mess msx -cart1',
			'.nes':'mess nes -cart',
			'.sms':'mess sms -cart',
			'.bin': [
				('Sega MegaDrive/Genesis','mess gen_usa -cart'),
				('data','mess gamegear -cart'),
			],
			'.gb':'mess gbcolor -cart',
			'.gbc':'mess gbcolor -cart',
			'.sgb':'mess gbcolor -cart',
			'.a78':'mess a7800 -cart',
			'.gif':'qiv -tf',
			'.exe':self.playfile_dosbox,
			'.com':self.playfile_dosbox,
			'.bat':self.playfile_dosbox,
		}
	def playfile_dialog(choose):
		while 1: 
			rc=select(choose)
			if rc[0]=='select':
				return choose[rc[1]]
	def playfile_cmp(a,b):
		al=[0,a[::-1]]
		bl=[0,b[::-1]]
		for i in ['[a','[b','[h','ack)']:
			if a.find(i)!=-1:
				al[0]=1
			if b.find(i)!=-1:
				bl[0]=1
		return cmp(al,bl)
	def playfile_select(il,nbr):
		if len(il)>1:
			il.sort(playfile_cmp)
			n=playfile_dialog(il)
			c=[]
			for i in range(min(nbr,len(il))):
				c.append(il[(il.index(n)+i)%len(il)])
			return c
		else:
			return il
	def playfile_mess(emu,cart,flop,cass,dump,quik):
		c=[EMU_XMESS,machine,'-fullscreen']
		if machine=='c64' and len(flop):
			c.extend(['-playback','d64load'])
		playfile_run(c)
	def playfile_uae(flop):
		flop=playfile_select(flop,4)
		for i in range(len(flop)):
			c.append('-s')
			c.append('floppy%d=%s'%(i,flop[i]))
		playfile_run(c)
	def playfile_dosbox(self,vfile):
		#sys.stderr.write('%s\n'%c);sys.stderr.flush()
		if hasattr(vfile,'_zip'):
			zipname=vfile._zip
			path=vfile._path
			basename='.'.join(os.path.basename(zipname).split(".")[:-1])
			od=os.path.join(os.path.expanduser(EMU_TMP),basename)
			if not os.path.isdir(od):
				c=["7za","x","-o%s"%od,zipname]
				os.spawnvp(os.P_WAIT, c[0], c)
			path=path.replace('/','\\')
			c=['dosbox','-c','MOUNT C "%s"'%od,'-c','C:','-c',path]
			self.run(c)
	def run(self,c):
		os.system("setxkbmap us")
	#	os.environ["SDL_VIDEODRIVER"]="dummy"
	#	os.environ["SDL_VIDEODRIVER"]="dga";c.insert(0,'sudo')
	#	os.system("read a")
#		rc=os.spawnvp(os.P_WAIT, c[0], c)
	#	os.system("read a")
		pid=os.spawnvp(os.P_NOWAIT, c[0], c)
		pusht=0
		while 1:
			rc=os.waitpid(pid,os.WNOHANG)
			if rc[0]:
				break
			else:
				if pygame:
					t0=time.time()
					for event in pygame.event.get():
						if event.type == pygame.JOYBUTTONDOWN:
							pusht=t0
						if event.type == pygame.JOYBUTTONUP:
							pusht=t0
					push=0
					for i in range(pygame.joystick.get_count()):
						j=pygame.joystick.Joystick(i)
						j.init()
						push+=sum([j.get_button(k) for k in range(j.get_numbuttons())])
					if push>=5:
						if t0>pusht+2:
							os.kill(pid,15)
					else:
						pusht=t0
				time.sleep(0.2)
		os.system("setxkbmap be")
		return rc
	def play(self,vfile):
		name=vfile.name
		ext=name.lower().split('.')[-1]
		basename='.'.join(name.split(".")[:-1])
		od=os.path.join(os.path.expanduser(EMU_TMP),basename)
		of=os.path.join(od,name)
		cmd=self.exttype.get('.%s'%ext,0)
		if callable(cmd):
			cmd(vfile)
		elif cmd:
			if not os.path.isfile(of):
				if not os.path.isdir(od):
					os.makedirs(od)
				f=file(of,'w')
				f.write(vfile.read())
				f.close()
			if isinstance(cmd,list):
				out=popenlread(['file',of])
				for k,v in cmd:
					if re.search(k,out):
						cmd=v
						break
				else:
					cmd='echo'
			c=cmd.split(' ')
			c.append(of)
			self.run(c)

class BrowserCurse:
	def __init__(self,root):
		self.root=root
		self.vdir=VDir(root)
		self.cwd='.'
		self.cursesbegin()
	def cursesbegin(self):
		self.scr=curses.initscr()
		curses.noecho()
		curses.cbreak()
		self.scr.keypad(1)
		try:
			curses.start_color()
		except:
			pass
	def cursesend(self):
		self.scr.keypad(0)
		curses.echo()
		curses.nocbreak()
		curses.endwin()
	def select(self,arg,pos=0):
		scr=self.scr
		rc=(0,0)
		e=None
		filter=""
		l=all=[(i,arg[i]) for i in range(len(arg))]
		pl=pos
		pw=pos-(scr.getmaxyx()[0]/2)
		curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_RED)
		curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE)
		repeat={}
		scr.timeout(20)
		while 1:
			(hmax,wmax)=scr.getmaxyx()
			(h,w)=(hmax-1,wmax)
			#
			# Position handling
			#
			pl=min(pl,len(l)-1)
			pl=max(pl,0)
			if pl<pw: pw-=h/2
			elif pl>=pw+h: pw+=h/2
			pw=min(pw,len(l)-h)
			pw=max(pw,0)
			#
			# Drawing
			#
			fmt=' '*w
			for i in range(h):
				if pw+i<len(l):
					line=("%%-%ds"%(w-1))%l[pw+i][1]+chr(160)
					if pl==pw+i:
						scr.addstr(i,0,line,curses.color_pair(1))
					else:
						scr.addstr(i,0,line,curses.A_NORMAL)
				else:
					scr.addstr(i,0,("%%-%ds"%(w-1))%''+chr(160),curses.A_NORMAL)
			scr.refresh()
			#
			# Statusbar
			#
			scr.addstr(hmax-1,0,(("%%-%ds %6d/%-6d"%(w-15,pl+1,len(l)))%("Filter: %s"%filter))[:w-1],curses.color_pair(2))
			#
			# Event
			#
			key=-1
			try:
				try:
					key=scr.getkey()
				except curses.error:
					pass
			# python bug 1687125
			except KeyboardInterrupt:
				rc=('exit',l[pl][0])
				break
			if pygame:
				t0=time.time()
				for event in pygame.event.get():
					if event.type == pygame.QUIT:
						rc=('exit',l[pl][0])
						break
					if event.type == pygame.JOYAXISMOTION:
						m={ (0,-1):'KEY_UP', (0,1):'KEY_DOWN', (1,-1):'KEY_UP', (1,1):'KEY_DOWN', }
						key=m.get((event.axis%2,int(event.value)),key)
						if event.value==0:
							repeat={}
						else:
							repeat[key]=time.time()
					if event.type == pygame.JOYBUTTONDOWN:
						key=['\n','KEY_LEFT'][event.button%2]
				for k,v in repeat.items():
					if t0>v+0.2:
						step=min(12,int(t0-v)+1)
						if   k=="KEY_DOWN": pl+=step
						elif k=="KEY_UP": pl-=step
			if   key=="KEY_DOWN": pl+=1
			elif key=="KEY_UP": pl-=1
			elif key=="KEY_NPAGE": pw+=h; pl+=h
			elif key=="KEY_PPAGE": pw-=h; pl-=h
			elif key=="KEY_END": pl=len(l)-1; pw=pl
			elif key=="KEY_HOME": pl=0; pw=pl
			elif key=="\x1b":
				rc=('exit',l[pl][0])
				break
			elif key=="KEY_BACKSPACE":
				if len(filter):
					filter=filter[:-1]
					fl=filter.lower()
					l=[i for i in all if i[1].lower().find(fl)!=-1]
			elif key=="KEY_LEFT":
				rc=('back',l[pl][0])
				break
			elif key=='\n' or key=="KEY_RIGHT":
				rc=("select",l[pl][0])
				break
			elif isinstance(key,str) and len(key)==1:
				fil=filter+key
				fl=fil.lower()
				nl=[i for i in l if i[1].lower().find(fl)!=-1]
				if len(nl):
					filter=fil
					l=nl
		return rc
	def loop(self):
		pos=1
		old=""
		try:
			while 1:
				ldir=[]
				lfile=[]
				for i in self.vdir.list(self.cwd):
					if i.find('.')==-1:
						ldir.append(i+'/')
					elif i.find('.zip')!=-1:
						ldir.append(i+'/')
					elif i.find('.7z')!=-1:
						ldir.append(i+'/')
					else:
						lfile.append(i)
				ldir.sort()
				lfile.sort()
				lall=['../']
				lall.extend(ldir)
				lall.extend(lfile)
				if len(old):
					try:
						pos=lall.index(old+'/')
					except ValueError:
						pos=1
					old=""
				rc=self.select(lall,pos)
				pos=rc[1]
				if rc[0]=="exit":
					self.cursesend()
					break
				elif rc[0]=="back":
					pos=1
					old=os.path.basename(self.cwd)
					self.cwd=os.path.normpath(os.path.join(self.cwd,'..'))
				else:
					id=rc[1]
					sel=lall[id]
					name=lall[id].strip("/\n")
					if sel.endswith('/'):
						self.cwd=os.path.normpath(os.path.join(self.cwd,name))
						pos=1
					else:
						self.cursesend()
						PlayFile().play(self.vdir.vfile(os.path.normpath(os.path.join(self.cwd,name))))
						self.cursesbegin()
				if self.cwd=='..':
					self.cwd='.'
		except KeyboardInterrupt:
			self.cursesend()
		except Exception,e:
			self.cursesend()
			raise e
	def mameloop(self):
		data=0
		games=[]
		for l in os.popen(EMU_MAME+" -listfull"):
			l=l.strip()
			if l.startswith("Name:"):
				data=1
				continue
			if data:
				f=l[:8].strip()
				n=l[8:].strip('" ')
				games.append((n.lower(),n,f))
		games.sort()
		lall=["%-9s %s\n"%(i[2],i[1]) for i in games]
		pos=0
		while 1:
			rc=self.select(lall,pos)
			pos=rc[1]
			if rc[0]=="back":
				pass
			elif rc[0]=="exit":
				break
			else:
				c=[EMU_MAME,games[pos][2]]
				rc=PlayFile().run(c)
		self.cursesend()

class BrowserSDL:
	#value=range(16,88)
	#sin=[(math.sin((math.pi*2*i)/32)+1)/2.0 for i in range(32)]
	#table=[value[int(i*(len(value)-1))] for i in sin]
	#t=int(time.time()*20.3)%len(table)
	#curses.init_pair(1,curses.COLOR_WHITE,table[t])
	#for i in range(0,8):
	#	esc='\x1b]4;%s;#%02x0000\x1b\\'%(80+i,63+32+i*((256-(64+32))/8))
	#	sys.stdout.write(esc)
	pass

def main():
	myself=os.path.normpath(os.path.join(os.getcwd(),__file__))
	parser = optparse.OptionParser()
	parser.add_option("-b", "--browse", action="store_true", dest="browse", default=False, help="file browser")
	parser.add_option("-m", "--mame", action="store_true", dest="mame", default=False, help="mame browser")
	parser.add_option("-p", "--play", action="store_true", dest="play", default=False, help="play file")
	(o, a) = parser.parse_args()
	root=(a+['.'])[0]
	os.chdir(root)
	if o.play:
		playfile(a)
	elif o.mame:
		BrowserCurse('.').mameloop()
	else:
		BrowserCurse('.').loop()

main()

