#!/usr/bin/env python

__author__ = "Artur Duque de Souza"
__author_email__ = "artur.souza@indt.org.br"
__license__ = "GPL"
__version__ = "0.3"

import os
import time
import fcntl
import shlex
import socket
import struct
import signal
import subprocess

import lib.utils as utils
import lib.server as server
import plugins.transcoders.mencoder_lib.mythtv as mythtv

from select import select
import lib.transcoder as transcoder

__all__ = ("TranscoderMencoder",)

class TranscoderMencoder(transcoder.Transcoder):
    """Transcoder class that implements a transcoder using Mencoder"""
    mencoder_path = utils.which("mencoder")
    name = "mencoder"
    priority = -1
    args = {}
    proc = None
    gmyth = None

    # only works with avi container
    status = 0

    def _setup_params(self):
        params_first = self.params_first

        # general_opts
        self.args["local"]    = params_first("local", False)
        self.args["language"] = params_first("language", False)
        self.args["subtitle"] = params_first("subtitle", False)
        self.args["format"]   = params_first("format", "mpeg1")
        self.args["outfile"]  = params_first("outfile", "-")

        # input_opt
        self.args["type"]     = params_first("type", "file")
        self.args["input"]    = params_first("uri", "-")

        # audio_opts
        self.args["acodec"]   = params_first("acodec", "mp2")
        self.args["abitrate"] = params_first("abitrate", 192)
        self.args["volume"]   = params_first("volume", 5)

        # video_opts
        self.args["mux"]      = params_first("mux", "mpeg")
        self.args["fps"]      = params_first("fps", 25)
        self.args["vcodec"]   = params_first("vcodec", "mpeg1video")
        self.args["vbitrate"] = params_first("vbitrate", 400)
        self.args["width"]    = params_first("width", 320)
        self.args["height"]   = params_first("height", 240)
    # _setup_params()


    def _setup_audio(self):
        if self.args["acodec"] == "mp3lame":
            audio = "-oac mp3lame -lameopts cbr:br=%s vol=%s" % (
                self.args["abitrate"], self.args["volume"])
        else:
            audio = "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (
                self.args["acodec"], self.args["abitrate"])

        return audio
    # _setup_audio()


    def _setup_video(self):
        video = " -of %s" % self.args["mux"]
        video += " -ofps %s" % self.args["fps"]

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

        if self.args["mux"] == "mpeg":
            video += " -mpegopts format=%s" % self.args["format"]

        video += " -vf scale=%s:%s" % (self.args["width"], self.args["height"])
        return video
    # _setup_video()


    def _arg_append(self, args, options):
        for arg in shlex.split(options):
            args.append(arg)
    # arg_append()

    def _setup_mencoder_opts(self, args):
        args.append(self.mencoder_path)

        if self.args["type"] and self.args["type"] == "tv":
            self._arg_append(args, self.args["tv"])
        elif self.args["outfile"] == "-" and self.args["type"]:
            args.append(self.args["input"])
        else:
            args.append("-")

        if self.args["language"]:
            self._arg_append(args, "-alang %s" % self.args["language"])

        if self.args["subtitle"]:
            self._arg_append(args, "-slang %s" % self.args["subtitle"])
            self._arg_append(args, "-subfps %s" % self.args["fps"])

        self._arg_append(args, "-idx")
        self._arg_append(args, "-cache 1024")
        self._arg_append(args, self._setup_audio())
        self._arg_append(args, self._setup_video())

        self._arg_append(args, "-really-quiet")

        if self.args["outfile"] != "-":
            self.args["outfile"] = ".transcoded/%s" % (
                                   os.path.basename(self.args["outfile"]))

        self._arg_append(args, "-o %s" % self.args["outfile"])
        self._arg_append(args, "2>%s" % os.devnull)
    # _setup_args()

    def _setup_filename(self):
        """This function setups the file to encode parsing the uri.
        So, type can be:
        * file
        * dvd
        * myth

        If the last one is detected we have to parse the uri to find args.
        Then we store all the args inside a dictionary: self.args['gmyth-cat']
        """
        _type = self.args["type"]

        if _type == "file":
            if not os.path.exists(self.args["input"]):
                raise IOError,\
                      "File requested does not exist: %s." % self.args["input"]
            else:
                self.args["input"] = "file://%s" % self.args["input"]

        elif _type == "dvd":
            self.args["input"] = "dvd://%s" % self.args["input"]

        elif _type == "myth":
            self.args["gmyth-cat"] = mythtv._setup_mythfilename(self)

        elif _type == "tv":
            driver = self.params_first("driver", "v4l2")
            norm = self.params_first("norm", "pal-m")
            channel = self.params_first("channel", "13")
            chanlist = self.params_first("chanlist", "us-bcast")
            outfmt = self.params_first("outfmt", "yuy2")
            vdev = self.params_first("vdev", "/dev/video0")
            adev = self.params_first("adev", "/dev/dsp")
            self.args["tv"] = "tv:// -v -tv driver=%s:norm=%s:channel=%s:" \
                              "chanlist=%s:width=%s:height=%s:outfmt=%s:" \
                              "device=%s:adevice=%s" % (driver, norm,
                                                        channel, chanlist,
                                                        self.args["width"],
                                                        self.args["height"],
                                                        outfmt, vdev, adev)
    # _setup_filename()


    def __init__(self, params):
        transcoder.Transcoder.__init__(self, params)
        self.mencoder_opts = []

        try:
            self._setup_params()
            self._setup_filename()
            self._setup_mencoder_opts(self.mencoder_opts)
        except Exception, e:
            if self.log:
                self.log.error(self.tid, "Error: %s" % e)
            else:
                raise
    # __init__()


    def _check_opened_file(self, stdw, _stdin):
        loop = True
        while loop:
            try:
                return open(self.args["outfile"])
            except:
                os.write(stdw, _stdin.read(1024))
    # _check_opened_file


    def _start_outfile(self, outfd):
        finished = False

        # Configuring stdin
        try:
            filename = self.args["input"].split("://")[1]
            _stdin = open(filename)
            size = int(os.path.getsize(filename))
        except Exception, e:
            self.log.error(self.tid, "Error: Mencoder stdin"\
                           " setup error: %s" % e)
            outfd.write("Error: Mencoder stdin setup error: %s" %e)
            return False

        self.status = 0
        total_read = 0

        # Configuring pipes
        stdr, stdw = os.pipe()

        if not self._run_mencoder(input=stdr):
            return False

        stdout = self._check_opened_file(stdw, _stdin)
        outfd.write("OK   ")

        try:
            while self.proc and self.proc.poll() == None:
                if not finished:
                    data_in = _stdin.read(4096)
                    if data_in != "":
                        os.write(stdw, data_in)
                        total_read += 4096
                        d = stdout.read(4096)
                        self.status = utils.progress_bar(total_read,
                                                         size, 50)
                    else:
                        finished = True
                        os.close(stdw)

                else:
                    d = stdout.read(4096)

        except Exception, e:
            self.log.error(self.tid, "Error: %s" % e)
            self.stop()
            return False

        self.log.info(self.tid, "OK: Done")
        return True
    # _start_outfile()


    def _start(self, outfd):
        # Play a file on disk or DVD
        if not self._run_mencoder(output=subprocess.PIPE):
            return False

        try:
            while self.proc and self.proc.poll() == None:
                d = self.proc.stdout.read(1024)
                outfd.write(d)
        except Exception, e:
            self.log.error(self.tid, "Error: %s" % e)
            return False

        self.log.info(self.tid, "OK: Done")
        return True
    # _start()

    def _run_mencoder(self, input=None, output=None):
        try:
            self.proc = subprocess.Popen(self.mencoder_opts, stdin=input,
                                         stdout=output, close_fds=True)
        except Exception, e:
            self.log.error(self.tid, "Error: Mencoder: %s" % e)
            return False

        return True
    # _run_mencoder()

    def start(self, outfd):
        cmd = " ".join(self.mencoder_opts)
        self.log.debug(self.tid, "Plugin's tid: %s" % self.tid)
        self.log.debug(self.tid, "Mencoder: %s" % cmd)

        ret = False

        if self.args["outfile"] == "-" and \
               self.args["type"] in ["file", "dvd", "tv"]:
            ret = self._start(outfd)

        elif self.args["type"] == "myth":
            ret = mythtv.start_myth(self, outfd)

        else:
            ret = self._start_outfile(outfd)

        self.stop()

        if not ret:
            self.log.error(self.tid, "Error: Problems while "\
                           "starting streaming.")

        return ret
    # start()

    def _aux_stop(self, obj, next=False):
        if obj:
            try:
                os.kill(obj.pid, signal.SIGKILL)
                if next:
                    os.kill(obj.pid+1, signal.SIGKILL)
            except OSError, e:
                pass

            try:
                obj.wait()
            except Exception, e:
                pass

            obj = None
    # _aux_stop

    def stop(self):
        self._aux_stop(self.proc, True)
        self._aux_stop(self.gmyth)
    # stop()

# TranscoderMencoder
