import os
import subprocess
from collections import namedtuple
import gi

gi.require_version("Gst", "1.0")
gi.require_version("Tcam", "0.1")

from gi.repository import Tcam, Gst, GLib, GObject

DeviceInfo = namedtuple("DeviceInfo", "status name identifier connection_type")
CameraProperty = namedtuple("CameraProperty", "status value min max default step type flags category group")

# Disable pylint false positives
# pylint:disable=E0712


class Camera:
    """"""
    def __init__(self, serial, width, height, framerate, color, liveview):
        """ Constructor.
        Creates the sink pipeline and the source pipeline.

        :param serial: Serial number of the camera to use.
        :param width: Width of the video format, e.g. 640, 1920 etc,
        :param height: Height of the video format, e.g. 480, 1080
        :param framerate: Numerator of the frame rate, e.g. 15, 30, 60 etc
        :param color: If True, color is used, else gray scale
        :param liveview: If True an own live window is opened.
        """
        Gst.init([])
        self.height = height
        self.width = width
        self.sample = None
        self.samplelocked = False
        self.newsample = False
        self.pid = -1

        self.__remove_tmp_file()

        pixelformat = "BGRx"
        if not color:
            pixelformat = "GRAY8"

        if liveview:
            p = 'tcambin serial="%s" name=source ! video/x-raw,format=%s,width=%d,height=%d,framerate=%d/1' % (serial, pixelformat, width, height, framerate,)
            p += ' ! tee name=t'
            p += ' t. ! queue ! videoconvert ! video/x-raw,format=RGB ,width=%d,height=%d,framerate=%d/1! shmsink socket-path=/tmp/ros_mem'  % (width, height, framerate,)
            p += ' t. ! queue ! videoconvert ! ximagesink'
        else:
            p = 'tcambin serial="%s" name=source ! video/x-raw,format=%s,width=%d,height=%d,framerate=%d/1' % (
            serial, pixelformat, width, height, framerate,)
            p += ' ! videoconvert ! video/x-raw,format=RGB ,width=%d,height=%d,framerate=%d/1! shmsink socket-path=/tmp/ros_mem'  % (width, height, framerate,)

        print(p)

        try:
            self.pipeline = Gst.parse_launch(p)
        except GLib.Error as error:
            raise RuntimeError("Error creating pipeline: {0}".format(error))


        self.pipeline.set_state(Gst.State.READY)
        if self.pipeline.get_state(10 * Gst.SECOND)[0] != Gst.StateChangeReturn.SUCCESS:
            raise RuntimeError("Failed to start video stream.")
        # Query a pointer to our source, so we can set properties.
        self.source = self.pipeline.get_by_name("source")

        # Create gscam_config variable with content
        gscam = 'shmsrc socket-path=/tmp/ros_mem ! video/x-raw-rgb, width=%d,height=%d,framerate=%d/1' % (width, height, framerate,)
        gscam += ',bpp=24,depth=24,blue_mask=16711680, green_mask=65280, red_mask=255 ! ffmpegcolorspace'
        os.environ["GSCAM_CONFIG"] = gscam

    def start_pipeline(self):
        """ Starts the camera sink pipeline and the rosrun process

        :return:
        """
        try:
            self.pipeline.set_state(Gst.State.PLAYING)
            self.pid = subprocess.Popen(["rosrun", "gscam", "gscam"])

        except GLib.Error as error:
            print("Error starting pipeline: {0}".format(error))
            raise

    def stop_pipeline(self):
        """ Stops the camera pipeline. Should also kill the rosrun process, but is not implemented

        :return:
        """
        self.pipeline.set_state(Gst.State.PAUSED)
        self.pipeline.set_state(Gst.State.READY)
        self.pipeline.set_state(Gst.State.NULL)
        self.pid.kill()

    def list_properties(self):
        """ Helper function. List available properties

        :return:
        """
        for name in self.source.get_tcam_property_names():
            print(name)

    def get_property(self, property_name):
        """ Return the value of the passed property.

        Use list_properties for querying names of available properties.

        :param property_name: Name of the property, e.g. Gain, Exposure, Gain Auto.
        :return: Current value of the property.
        """
        try:
            return CameraProperty(*self.source.get_tcam_property(property_name))
        except GLib.Error as error:
            raise RuntimeError("Error get Property {0}: {1}", property_name, format(error))

    def set_property(self, property_name, value):
        """ Set a property. Use list_properties for querying names of available properties.

        :param property_name: Name of the property, e.g. Gain, Exposure, Gain Auto.
        :param value: Value to be set.
        :return:
        """
        try:
            self.source.set_tcam_property(property_name, value)
        except GLib.Error as error:
            raise RuntimeError("Error set Property {0}: {1}", property_name, format(error))

    def push_property(self, property_name):
        """ Simplify push properties, like Auto Focus one push

        :param property_name: Name of the property to be pushed
        :return:
        """
        try:
            self.source.set_tcam_property(property_name, True)

        except GLib.Error as error:
            raise RuntimeError("Error set Property {0}: {1}", property_name, format(error))

    def __remove_tmp_file(self):
        """ Delete the memory file used by the pipelines to share memory

        :return:
        """
        try:
            os.remove('/tmp/ros_mem')
        except OSError:
            pass

[((6, 0, 6, 32), 'gi.require_version', 'gi.require_version', ({(6, 19, 6, 24): '"""Gst"""', (6, 26, 6, 31): '"""1.0"""'}, {}), "('Gst', '1.0')", False, 'import gi\n'), ((7, 0, 7, 33), 'gi.require_version', 'gi.require_version', ({(7, 19, 7, 25): '"""Tcam"""', (7, 27, 7, 32): '"""0.1"""'}, {}), "('Tcam', '0.1')", False, 'import gi\n'), ((11, 13, 11, 79), 'collections.namedtuple', 'namedtuple', ({(11, 24, 11, 36): '"""DeviceInfo"""', (11, 38, 11, 78): '"""status name identifier connection_type"""'}, {}), "('DeviceInfo', 'status name identifier connection_type')", False, 'from collections import namedtuple\n'), ((12, 17, 12, 108), 'collections.namedtuple', 'namedtuple', ({(12, 28, 12, 44): '"""CameraProperty"""', (12, 46, 12, 107): '"""status value min max default step type flags category group"""'}, {}), "('CameraProperty',\n    'status value min max default step type flags category group')", False, 'from collections import namedtuple\n'), ((31, 8, 31, 20), 'gi.repository.Gst.init', 'Gst.init', ({(31, 17, 31, 19): '[]'}, {}), '([])', False, 'from gi.repository import Tcam, Gst, GLib, GObject\n'), ((58, 28, 58, 47), 'gi.repository.Gst.parse_launch', 'Gst.parse_launch', ({(58, 45, 58, 46): 'p'}, {}), '(p)', False, 'from gi.repository import Tcam, Gst, GLib, GObject\n'), ((81, 23, 81, 69), 'subprocess.Popen', 'subprocess.Popen', ({(81, 40, 81, 68): "['rosrun', 'gscam', 'gscam']"}, {}), "(['rosrun', 'gscam', 'gscam'])", False, 'import subprocess\n'), ((148, 12, 148, 37), 'os.remove', 'os.remove', ({(148, 22, 148, 36): '"""/tmp/ros_mem"""'}, {}), "('/tmp/ros_mem')", False, 'import os\n')]