// VideoInput.h: Video input processing using Gstreamer
// 
//   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef GNASH_VIDEOINPUTGST_H
#define GNASH_VIDEOINPUTGST_H

#include <vector>
#include <cstdint> // for C99 int types
#include "VideoInput.h"
#include "gst/gst.h"

namespace gnash {
namespace media {
/// \namespace gst
///
/// This namespace is used for the Gstreamer implmentation of the VideoInput
/// class.
namespace gst {

class GnashWebcam;
class GnashWebcamPrivate;

/// \class FramerateFraction
///
/// Convience wrapper class which allows easily calculating fractions from
/// the information returned from probing hardware cameras for supported
/// framerates.
class FramerateFraction
{
public:
    /// \var FramerateFraction::numerator
    /// \brief contains a gint value for the numerator portion of a fraction.
    gint numerator;
    /// \var FramerateFraction::denominator
    /// \brief contains a gint value for the denominator portion of a fraction.
    gint denominator;
    
    /// \brief Constructor which sets the numerator and denominator fields upon construction.
    ///
    /// @param num The integer numerator value to initialize the FramerateFraction class with.
    ///
    /// @param denom The integer denominator value to initialzie the FramerateFraction class with.
    FramerateFraction(gint num, gint denom);
    
    /// Create a new empty FramerateFraction class.
    FramerateFraction();
};

/// \class WebcamVidFormat
///
/// Class used to hold enumerated information about usable video formats.
///
class WebcamVidFormat
{
public:
    /// \var WebcamVidFormat::mimetype
    /// \brief Contains a gchar* which describes the raw video input stream
    ///       from the camera formated in a Gstreamer video format
    ///       type (e.g. video/x-raw-rgb or video/x-raw-yuv).
    gchar *mimetype;
    
    /// \var WebcamVidFormat::width
    /// \brief Contains a gint value describing the width of the selected
    ///       format.
    gint   width;
    
    /// \var WebcamVidFormat::height
    /// \brief Contains a gint value describing the height of the selected
    ///       format.
    gint   height;
    
    /// \var WebcamVidFormat::numFramerates
    /// \brief Contains a gint value representing the number of framerate
    ///       values supported by the format described in the mimetype var.
    gint   numFramerates;
    
    /// \var WebcamVidFormat::framerates
    /// \brief Pointer to a FramerateFraction class which simply holds a
    ///      temporary framerate variable while trying to determine the
    ///      highest possible supported framerate for the format described
    ///      in the mimetype var.
    FramerateFraction *framerates;
    
    /// \var WebcamVidFormat::highestFramerate
    /// \brief Holds the highest_frame supported by the format described
    ///      in the mimetype var.
    FramerateFraction highestFramerate;
    
    /// Constructor for the WebcamVidFormat class
    WebcamVidFormat();
};


/// \class VideoInputGst
///
/// This class is the main class that interacts with the other classes
/// defined in this header file. However, most of the significant information
/// is actually stored in a GnashWebcamPrivate class.
///
class VideoInputGst : public VideoInput
{
public:
 
    /// Constructor for the VideoInputGst class
    VideoInputGst();
    
    /// Destructor for the VideoInputGst class
    ~VideoInputGst();
    
    static void getNames(std::vector<std::string>& names);
    
    /// Return the current activity level of the webcam
    //
    /// @return     A double specifying the amount of motion currently
    ///             detected by the camera.
    double activityLevel () const { return _activityLevel; }
    
    /// The maximum available bandwidth for outgoing connections
    //
    /// TODO: see if this should really be here.
    size_t bandwidth() const { return _bandwidth; }
    
    void setBandwidth(size_t bandwidth) {
        _bandwidth = bandwidth;
    }

    /// The current frame rate of the webcam
    //
    /// @return     A double specifying the webcam's current FPS
    double currentFPS() const { return _currentFPS; }
    
    /// The maximum FPS rate of the webcam
    //
    /// @return     A double specifying the webcam's maximum FPS
    double fps() const { return _fps; }

    /// Return the height of the webcam's frame
    size_t height() const { return _height; }
    
    /// Return the width of the webcam's frame
    size_t width() const { return _width; }
    
    /// The index of the camera
    size_t index() const { return _index; }
    
    /// Request a native mode most closely matching the passed variables.
    //
    /// @param width            The required width
    /// @param height           The required height
    /// @param fps              The required frame rate
    /// @param favorArea        How to match the requested mode.
    void requestMode(size_t width, size_t height, double fps, bool favorArea);

    /// Set the amount of motion required before notifying the core
    void setMotionLevel(int m) { _motionLevel = m; }

    /// Return the current motionLevel setting
    int motionLevel() const { return _motionLevel; }
    
    /// Set time without motion in milliseconds before core is notified
    void setMotionTimeout(int m) { _motionTimeout = m; }

    /// Return the current motionTimeout setting.
    int motionTimeout() const { return _motionTimeout; }
    
    void mute(bool m) { _muted = m; }
    bool muted() const { return _muted; }
    
    /// Return the name of this webcam
    //
    /// @return     a string specifying the name of the webcam.
    const std::string& name() const { return _name; }

    /// Set the quality of the webcam
    void setQuality(int q) { _quality = q; }

    /// Return the current quality of the webcam
    int quality() const { return _quality; }
    
    /// \brief Function starts up the pipeline designed earlier in code
    ///      execution. This puts everything into motion.
    ///
    /// @return True if the pipeline was started correctly, false otherwise.
    bool play();
    
    /// \brief Function stops the pipeline designed earlier in code execution.
    ///
    /// @return True if the pipeline was stopped correctly, false otherwise.
    bool stop();
    

    /// Set this VideoInput's webcam to the device corresponding to an index.
    //
    ///       Now transfer the
    ///       important information from the GnashWebcam structure to the
    ///       GnashWebcamPrivate structure which is larger because it has
    ///       space to store Gstreamer pipeline, element and bin elements.
    ///       See definition of GnashWebcamPrivate for more info.
    ///
    /// @param dev_select The index of the camera the user wants to
    ///            select.
    /// @return    If the device index doesn't exist, return false
    bool setWebcam(size_t index);
    
    /// Call all functions necessary for initializing the camera.
    //
    /// For gstreamer this includes setting up bins.
    //
    /// Return false on failure of any initialization.
    /// TODO: better throw MediaException.
    bool init();
    
    /// \brief This function is important in the flow of the code. It looks
    ///       in the gnashrc file to see if you have a default camera defined
    ///       and selects that one (if it's defined), otherwise a videotestsrc
    ///       will be used for the remainder of the execution of the code.
    ///       Currently this code also calls other functions to make bins and
    ///       pipelines. It might be abstracted into separate functions later.
    /// @return The integer value respresenting the selected webcam from the
    ///   gnashrc file.
    int makeWebcamDeviceSelection();
    
    /// ==================================
    /// Functions that shouldn't be public.
    /// ==================================
    
    /// \brief Function links the videoSaveBin to the videoSaveQueue in the
    ///   main bin.
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///              created previously in a call to transferToPrivate()
    ///
    /// @return True if the link to the videoSaveQueue was successfully, false
    ///    otherwise.
    gboolean webcamMakeVideoSaveLink();

    /// \brief Function breaks link between the videoSaveBin and the videoSaveQueue
    ///   in the main bin.
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///             created previously in a call to transferToPrivate()
    /// @return True if the link was succesfully broken, false otherwise
    gboolean webcamBreakVideoSaveLink();

    /// \brief Function creates the save bin. For more information on pipeline
    ///       implementation and this function in general see the definition of
    ///       the _webcam_save_bin variable in the GnashWebcamPrivate structure
    ///       documentation.
    ///
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///             created previously in a call to transferToPrivate()
    ///
    /// @return True if everything went correctly (making elements, dropping
    ///        into bins and linking elements), false otherwis
    gboolean webcamCreateSaveBin();
    
    /// \brief Function links the video_display_bin to the video_display_queue
    ///       in the main bin.
    ///
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///             created previously in a call to transferToPrivate()
    ///
    /// @return True if the link to the video_display_queue was successful,
    ///        False otherwise.
    gboolean webcamMakeVideoDisplayLink();
    
    /// \brief Function breaks the link between the _videoDisplayBin and the
    ///    _videoDisplayQueue in the main bin
    ///
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///            created previously in a call to transferToPrivate()
    ///
    /// @return True if the link was successfully broken, false otherwise
    gboolean webcamBreakVideoDisplayLink();

private:

    
    /// \brief This function makes a temporary pipeline with the selected device
    ///       to determine its capabilities (Gstreamer calls these caps). This
    ///       information is saved in a GnashWebcamPrivate class and will be
    ///       used when enumerating additional information about the camera and
    ///       in creating the real pipeline to capture video.
    ///
    /// @param dev_select The integer value of the camera the user wants to select.
    ///                 This might be changed to the name of the camera, but it's
    ///                 currently an integer (if it changes, we need to change the
    ///                 gnashrc element). If this value is 0, you've selected a
    ///                 videotestsrc.
    ///
    /// @return Nothing. All pertantent information is stored in a GnashWebcam class.
    void getSelectedCaps(gint dev_select);
    
    /// \brief This function is called by get_selected caps to determine what
    ///       formats the input device can handle. It saves all the information
    ///       it finds into an array of WebcamVidFormat pointers. This information
    ///       will then be analyzed and actually added as a supported format in 
    ///       a call to addSupportedFormat.
    ///
    /// @param cam A pointer to the GnashWebcam structure you want to use as input.
    /// @param caps A pointer to the capabilities discovered in the getSelectedCaps() function.
    ///
    /// @return Nothing. All pertantent information is stored in a GnashWebcam class.
    void getSupportedFormats(GnashWebcam *cam, GstCaps *caps);
    
    /// \brief This function is called by getSupportedFormats when it has
    ///       determined that the format being analyzed can be used to capture
    ///       information from the hardware device.
    ///
    /// @param cam A pointer to the GnashWebcam structure you want to use as input.
    /// @param video_format A pointer to a WebcamVidFormat class that has had all
    ///                    variables initialize to their respective values.
    ///
    /// @return Nothing. All pertantent information is stored in a GnashWebcam class.
    void addSupportedFormat(GnashWebcam *cam, WebcamVidFormat *video_format,
        GstStructure *format_structure);
    
    /// \brief This function is called by addSupportedFormat. Since we have
    ///    found a format that will work with the input device, we now
    ///    need to figure out what framerate the camera can capture at
    ///    that corresponds with the format being analyzed.
    ///
    /// @param video_format A pointer to a WebcamVidFormat class that has
    ///    had all variables initialized to their respective values.
    /// @param structure A pointer to a structure initialized with the
    ///         capabilities of the selected input device.
    ///
    /// @return Nothing. All pertintent information is stored in a
    ///         WebcamVidFormat class.
    void getSupportedFramerates(WebcamVidFormat *video_format,
            GstStructure *structure);

    /// \brief This function checks to see if the current format selected for the
    ///     webcam supports the framerate passed in as the second argument
    /// @param webcam A pointer to the selected GnashWebcamPrivate structure to
    ///     check for the supported framerate value
    /// @param fps An integer value to check for support
    /// @return True if the framerate is supported, false otherwise
    gboolean checkForSupportedFramerate (GnashWebcamPrivate *webcam, int fps);

    /// \brief This function runs through the list of framerates determined by
    ///       getSupportedFramerates() and finds the highest supported framerate
    ///       less than 30fps.
    ///
    /// @param format A pointer to the chosen format structure.
    ///
    /// @return Nothing. All pertantent information is stored in the structure
    ///        passed in (a WebcamVidFormat class).
    void findHighestFramerate(WebcamVidFormat *format);
    
    /// \brief Function creates the source bin. For more information on pipeline
    ///      implementation and this function in general see the definition of
    ///      the _webcamSourceBin variable in the GnashWebcamPrivate structure
    ///      documentation.
    ///
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///             created previously in a call to transferToPrivate()
    ///
    /// @return True if everything went correctly (making elements, dropping
    ///        into bins and linking elements), false otherwise.
    gboolean webcamCreateSourceBin();
    
    /// \brief Function is called when changes have been made to certain variables
    ///      that effect the video source's capabilities (specifically resolution
    ///      and fps values)
    /// @return True if the changes to the source's capabilities happened succesfully
    ///      false otherwise.
    gboolean webcamChangeSourceBin();
    
    /// \brief Function creates the main bin. For more information on pipeline
    ///       implementation and this function in general see the definition of
    ///       the _webcamMainBin variable in the GnashWebcamPrivate structure
    ///       documentation.
    ///
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///             created previously in a call to transferToPrivate()
    ///
    /// @return True if everything went correctly (making elements, dropping
    ///        into bins and linking elements), false otherwise.
    gboolean webcamCreateMainBin();
    
    /// \brief Function creates the display bin. For more information on pipeline
    ///       implementation and this function in general see the definition of
    ///       the _videoDisplayBin variable in the GnashWebcamPrivate structure
    ///       documentation.
    ///
    /// @param webcam A pointer to the GnashWebcamPrivate webcam structure
    ///             created previously in a call to transferToPrivate()
    ///
    /// @return True if everything went correctly (making elements, dropping
    ///        into bins and linking elements), false otherwise.
    gboolean webcamCreateDisplayBin();
    
    
    
    /// \brief Accessor to return a pointer to the global GnashWebcamPrivate
    ///    variable
    /// @return A pointer to the global GnashWebcamPrivate pointer
    GnashWebcamPrivate* getGlobalWebcam() {return _globalWebcam;}
    
    /// \brief This function interacts with the hardware on the machine
    ///       to enumerate information about devices connected. Currently
    ///       this function only looks for videotestsources (implemented
    ///       in Gstreamer), video4linux and video4linux2 sources.
    //
    /// @return Nothing. All pertintent information is stored to the passed
    ///                  vector. Note: elements can also be null.
    static void findVidDevs(std::vector<GnashWebcam*>& cams);
    
    /// \var VideoInputGst::_vidVect
    /// \brief A vector containing pointers to GnashWebcam classes.
    std::vector<GnashWebcam*> _vidVect;
    
    /// \var VideoInputGst::_devSelection
    /// \brief An integer value representing the original GnashWebcam data struct
    ///     _vidVect
    int _devSelection;
    
    /// \var VideoInputGst::_globalWebcam
    /// \brief Convienient pointer to the selected device's GnashWebcamPrivate
    ///       class structure.
    GnashWebcamPrivate *_globalWebcam;

    /// TODO: see which of these need to be retrieved from the camera,
    /// which of them should be stored like this, and which should
    /// be stored in the Camera_as relay object.

    /// The currently detected activity level. This should be queried from 
    /// the camera.
    double _activityLevel;

    /// The available bandwidth. This probably shouldn't be dealt with by
    /// the camera class. But maybe it should.
    size_t _bandwidth;

    /// The current FPS of the camera. This should be queried from the
    /// camera.
    double _currentFPS;

    /// The maximum FPS allowed.
    double _fps;

    /// The height of the frame. This should probably be retrieved from
    /// the camera
    size_t _height;

    /// The width of the frame. This should probably be retrieved from
    /// the camera
    size_t _width;

    /// The index of this Webcam
    size_t _index;

    /// The motion level required to trigger a notification to the core
    int _motionLevel;

    /// The length of inactivity required to trigger a notification to the core.
    int _motionTimeout;

    /// Whether access to the camera is allowed. This depends on the rcfile
    /// setting
    bool _muted;

    /// The name of this camera.
    std::string _name;

    /// The current quality setting.
    int _quality;

};




} //gst namespace
} // gnash.media namespace 
} // gnash namespace

#endif 
