from __future__ import division

import os
import sys
import lib
import time
import shlex
import signal
import socket
import ConfigParser
import logging as log

from select import *
from subprocess import *

class Media(object):

    def __init__(self, config):

        self.config = config
        self.do_cleanup()

    # __init__()


    def do_cleanup(self):
        self.path = ""
        self.args = []
        self.language = None
        self.subtitle = None
        self.mpegopts = None
        self.socket = None
        self.child_pid = None
        self.mplayer = None
        self.mencoder_pid = None
        self.mplayer_pid = None
        self.audio_opts = None
        self.video_opts = None
        self.gst_pipe = None
        self.gst_pid = None
        self.transcode_local = None

    # do_cleanup()


    def setup_opts(self, options):

        for opt in options:

            if opt == "local":
                self.mplayer = lib.which("mplayer")

            elif opt.find("language=") >= 0:
                try:
                    lan = opt.split("=")[1]
                    if len(lan) < 2:
                        self.language = lan
                except Exception, e:
                    log.error("Bad language option: %s" % opt)

            elif opt.find("subtitle=") >= 0:
                try:
                    sub = opt.split("=")[1]
                    if len(sub) < 2:
                        self.language = sub
                except Exception, e:
                    log.error("Bad subtitle option: %s" % opt)

            elif opt.find("format=") >= 0:
                try:
                    self.mpegopts = opt.split("=")[1]
                except Exception, e:
                    log.error("Bad format option: %s" % opt)

            elif opt.find("outfile=") >= 0:
                try:
                    self.transcode_local = opt.split("=")[1]
                except Exception, e:
                    log.error("Bad outfile option: %s" % opt)

    # setup_opts()


    def run_mplayer(self):
        msg = self.filename

        if self.kind == "dvd":
            msg = "dvd://" + msg

        self.mplayer_pid = Popen([self.mplayer, self.filename, "1> %s" % os.devnull,\
                                  "2> %s" % os.devnull], stdout=PIPE, close_fds=True)

    # run_mplayer()


    def setup_mencoder(self):
        self.path = self.config.get("Mencoder", "path")
        mp = Popen([self.path], stdout=PIPE, close_fds=True)

        version = mp.stdout.read().split("MEncoder ")[1].split(" (C)")[0].split("-")[-1]

        if version > "4.1.1": self.mencoder_old = False
        else: self.mencoder_old = True

        os.kill(mp.pid, signal.SIGKILL)
        log.info("Mencoder version: %s" % version)

        if self.mencoder_old:
            try:
                self.fifo = self.config.get("Mencoder", "fifo_path")
                os.mkfifo(self.fifo)
            except Exception, e:
                log.info("Fifo: %s" % e)
        else:
            self.fifo = "-"

    # setup_mencoder()


    def setup_audio(self):

        if self.acodec == "mp3lame":
            return "-oac mp3lame -lameopts cbr:br=%s vol=5" % self.abitrate
        else:
            return "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (\
                self.acodec, self.abitrate)

    # setup_audio()


    def setup_video(self):

        video = ""

        video += " -of %s" % self.mux
        video += " -ofps %s" % self.fps

        if self.vcodec == "nuv" or self.vcodec == "xvid"\
               or self.vcodec == "qtvideo" or self.vcodec == "copy":
            video += " -ovc %s" % self.vcodec
        else:
            video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
                self.vcodec, self.vbitrate)

        if self.mux == "mpeg" and self.mpegopts is not None:
            video += " -mpegopts format=%s" % self.mpegopts

        video += " -vf scale=%s:%s" % (self.width, self.height)

        return video

    # setup_video()


    def arg_append(self, args, options):
        l = shlex.split(options)
        for i in l:
            args.append(i)

    # arg_append()


    def setup_args(self, args):

        args.append(self.path)

        #args.append(self.filename)
        args.append("-")

        if self.language != None:
            self.arg_append(args, "-alang %s" % self.language)

        if self.subtitle != None:
            self.arg_append(args, "-slang %s" % self.subtitle)
            self.arg_append(args, "-subfps %s" % self.fps)

        self.arg_append(args, "-idx")
        self.arg_append(args, self.audio_opts)
        self.arg_append(args, self.video_opts)

        self.arg_append(args, "-really-quiet")
        self.arg_append(args, "-o %s" % self.fifo)
        self.arg_append(args, "2> %s" % os.devnull)

    # setup_args()


    def setup_filename(self, filename):
        try:
            self.kind, self.filename = filename.split("://")
        except:
            return (False, "Wrong filename protocol")

        if self.kind == "file":
            if not os.path.exists(self.filename):
                msg = "File requested does not exist. SETUP failed."
                log.error(msg)
                return (False, msg)

        elif self.kind == "dvd":
            self.filename = "dvd://" + filename

        elif self.kind == "myth":
            self.filename = filename
            self.gst_pipe = os.pipe()
            print self.gst_pipe[0]
            print self.gst_pipe[1]

        return (True, "")

    # setup_filename()


    def setup_socket(self):
        if self.socket != None:
            self.socket = None

        self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        try:
            self.socket.bind( ('', self.port) )
            self.socket.listen(1)
        except Exception, e:
            log.error("Could not create socket: %s" % e)
            return (False, e)

        return (True, "")

    # setup_socket()


    '''
    MENCODER SETUP DESCRIPTION
    ===========================

    -> mux, vcodecs and acodecs
     |-> mencoder (-of | -ovc | -oac) help

    -> if used mpeg as mux:
     |-> to setup format: format=%s as an option at the end

    '''


    # good one: /tmp/dvb.mpg avi mpeg4 400 25 mp3lame 192 320 240
    # file:///tmp/dvb.mpg mpeg mpeg1video 400 25 mp2 192 320 240 format=mpeg1
    # dvd://4 mpeg mpeg1video 400 25 mp3lame 192 400 240 language=en local
    # file:///tmp/mpg/bad_day.mpg avi mpeg4 400 25 mp3 192 320 240

    def setup(self, filename, mux, vcodec, vbitrate,\
              fps, acodec, abitrate, width, height, port, options):

        if self.args != []:
            self.do_cleanup()

        self.mux = mux
        self.vcodec = vcodec
        self.vbitrate = vbitrate
        self.fps = fps
        self.acodec = acodec
        self.abitrate = abitrate
        self.width = width
        self.height = height
        self.port = int(port)

        self.setup_mencoder()

        ret_val = self.setup_filename(filename)

        if not ret_val[0]:
            return ret_val

        self.setup_opts(options)
        self.audio_opts = self.setup_audio()
        self.video_opts = self.setup_video()
        self.setup_args(self.args)

        ret_val = self.setup_socket()
        return ret_val

    # setup()

    def play_loop(self, conn):
        data = self.pout.read(4096)

        conn.settimeout(5)
        retry = 0

        if not self.transcode_local:
            while data != "" and retry < 5:
                try:
                    conn.send(data)
                    r, w, x = select([conn], [], [], 0)
                    if conn in r:
                        back = conn.recv(1024)
                        if back == "OK" and self.mplayer and not self.mplayer_pid:
                            self.run_mplayer()

                except socket.error, e:
                    log.error("Socket error: %s" % e)
                    retry += 1

                data = self.pout.read(4096)

        else:
            local = open(self.transcode_local, "w")
            total = os.path.getsize(self.filename)
            partial = 4096

            while data != "":
                try:
                    local.write(data)
                except Exception, e:
                    log.error("Write error: %s" % e)

                data = self.pout.read(4096)
                partial += len(data)
                conn.send("%.2f\n" % (partial * 100 / total) )

            local.close()
            conn.send("DONE\n")

        return retry

    # play_loop()


    def play(self):

        log.info("Starting Mencoder: %s" % self.args )

        if self.gst_pipe:
            try:
                gst = [ lib.which("gst-launch-0.10") ]
                self.arg_append(gst, "gnomevfssrc location=%s" % self.filename)
                self.arg_append(gst, "! fdsink fd=%d" % self.gst_pipe[1])
                self.gst_pid = Popen(gst, stdout=self.gst_pipe[1], close_fds=True)
            except Exception, e:
                msg = "Could not init Gstreamer: %s" % e
                log.error(msg)
                return (False, msg)

        try:
            if not self.gst_pipe:
                self.stdin = open(self.filename)
            else:
                self.stdin = self.gst_pipe[0]

            self.mencoder_pid = Popen(self.args, stdin=self.stdin, stdout=PIPE, close_fds=True)
        except Exception, e:
            msg = "Could not init Mencoder: %s" % e
            log.error(msg)
            return (False, msg)

        if self.mencoder_old: self.pout = open(self.fifo)
        else: self.pout = self.mencoder_pid.stdout

        self.child_pid = os.fork()

        if self.child_pid == 0:
            conn, addr = self.socket.accept()

            log.info("Sending Data to client: %s" % addr[0])
            retry = self.play_loop(conn)

            if retry < 5:
                log.info("Finished sending Data to client: %s" % addr[0])
            else:
                log.error("Client timed out, retried more than %s times" % retry)

            os.kill(self.mencoder_pid.pid, signal.SIGKILL)
            sys.exit(0)

        return (True, "")

    # play()


    def stop(self):
        try:

            if self.mencoder_pid:
                os.kill(self.mencoder_pid.pid, signal.SIGTERM)
                self.mencoder_pid = None

            if self.mplayer_pid:
                os.kill(self.mplayer_pid.pid, signal.SIGTERM)
                self.mplayer_pid = None

            if self.socket:
                self.socket.close()
                self.socket = None

            if self.child_pid:
                os.kill(self.child_pid, signal.SIGTERM)
                self.child_pid = None

            if self.gst_pid:
                os.kill(self.gst_pid.pid, signal.SIGTERM)
                self.gst_pid = None

            self.do_cleanup()

            os.wait()

        except Exception, e:
            log.error("Stop error: %s" % e)

    # stop()
