/*
    videodevice.cpp  -  Kopete Video Device Low-level Support

    Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro   <taupter@gmail.com>
    Copyright (c) 2010      by Frank Schaefer                 <fschaefer.oss@googlemail.com>

    Kopete    (c) 2002-2003      by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This library is free software; you can redistribute it and/or         *
    * modify it under the terms of the GNU Lesser General Public            *
    * License as published by the Free Software Foundation; either          *
    * version 2 of the License, or (at your option) any later version.      *
    *                                                                       *
    *************************************************************************
*/

#define ENABLE_AV

#ifndef KOPETE_AVVIDEODEVICELISTITEM_H
#define KOPETE_AVVIDEODEVICELISTITEM_H

#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdint.h>

#if (defined(__linux__) || defined(__FreeBSD__)) && defined(ENABLE_AV)

#if defined(__linux__)
#include <asm/types.h>
#endif
#undef __STRICT_ANSI__
#ifndef __u64 //required by videodev.h
#define __u64 unsigned long long
#endif // __u64
#ifndef __s64 //required by videodev.h
#define __s64 signed long long
#endif // __s64

#ifndef pgoff_t
#define pgoff_t unsigned long
#endif

#if defined(__linux__)
#include <linux/fs.h>
#include <linux/kernel.h>
#endif

#ifdef HAVE_V4L
#include <linux/videodev.h>
#endif // have_v4l

#define VIDEO_MODE_PAL_Nc  3
#define VIDEO_MODE_PAL_M   4
#define VIDEO_MODE_PAL_N   5
#define VIDEO_MODE_NTSC_JP 6
#define __STRICT_ANSI__

#if defined(HAVE_LIBV4L2) && !defined(HAVE_V4L)
#include <linux/videodev2.h>
#endif

#ifdef HAVE_LIBV4L2
#include <libv4l2.h>
#endif // HAVE_V4L2

#endif // __linux__ __FreeBSD__ ENABLE_AV

#include <qstring.h>
#include <qfile.h>
#include <qimage.h>

#include "videoinput.h"

namespace Kopete {
namespace AV {
/**
@author Kopete Developers
*/
typedef enum
{
    VIDEODEV_DRIVER_NONE
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(ENABLE_AV)
    ,
    VIDEODEV_DRIVER_V4L
#ifdef V4L2_CAP_VIDEO_CAPTURE
    ,
    VIDEODEV_DRIVER_V4L2
#endif
#endif
} videodev_driver;

typedef enum
{
// Packed RGB formats
    PIXELFORMAT_NONE = 0,
    PIXELFORMAT_GREY = (1 << 0),
    PIXELFORMAT_RGB332 = (1 << 1),
    PIXELFORMAT_RGB444 = (1 << 2),
    PIXELFORMAT_RGB555 = (1 << 3),
    PIXELFORMAT_RGB565 = (1 << 4),
    PIXELFORMAT_RGB555X = (1 << 5),
    PIXELFORMAT_RGB565X = (1 << 6),
    PIXELFORMAT_BGR24 = (1 << 7),
    PIXELFORMAT_RGB24 = (1 << 8),
    PIXELFORMAT_BGR32 = (1 << 9),
    PIXELFORMAT_RGB32 = (1 << 10),

// Bayer RGB format
    PIXELFORMAT_SBGGR8 = (1 << 11),

// YUV formats
    PIXELFORMAT_YUYV = (1 << 12),
    PIXELFORMAT_UYVY = (1 << 13),
    PIXELFORMAT_YUV420P = (1 << 14),
    PIXELFORMAT_YUV422P = (1 << 15),

// Compressed formats
    PIXELFORMAT_JPEG = (1 << 16),
    PIXELFORMAT_MPEG = (1 << 17),

// Reserved formats
    PIXELFORMAT_DV = (1 << 18),
    PIXELFORMAT_ET61X251 = (1 << 19),
    PIXELFORMAT_HI240 = (1 << 20),
    PIXELFORMAT_HM12 = (1 << 21),
    PIXELFORMAT_MJPEG = (1 << 22),
    PIXELFORMAT_PWC1 = (1 << 23),
    PIXELFORMAT_PWC2 = (1 << 24),
    PIXELFORMAT_SN9C10X = (1 << 25),
    PIXELFORMAT_WNVA = (1 << 26),
    PIXELFORMAT_YYUV = (1 << 27)

//	PIXELFORMAT_ALL		= 0x00003FFF
} pixel_format;

typedef enum
{
// One bit for each
    STANDARD_PAL_B = (1 << 0),
    STANDARD_PAL_B1 = (1 << 1),
    STANDARD_PAL_G = (1 << 2),
    STANDARD_PAL_H = (1 << 3),
    STANDARD_PAL_I = (1 << 4),
    STANDARD_PAL_D = (1 << 5),
    STANDARD_PAL_D1 = (1 << 6),
    STANDARD_PAL_K = (1 << 7),

    STANDARD_PAL_M = (1 << 8),
    STANDARD_PAL_N = (1 << 9),
    STANDARD_PAL_Nc = (1 << 10),
    STANDARD_PAL_60 = (1 << 11),
// STANDARD_PAL_60 is a hybrid standard with 525 lines, 60 Hz refresh rate, and PAL color modulation with a 4.43 MHz color subcarrier. Some PAL video recorders can play back NTSC tapes in this mode for display on a 50/60 Hz agnostic PAL TV.

    STANDARD_NTSC_M = (1 << 12),
    STANDARD_NTSC_M_JP = (1 << 13),
    STANDARD_NTSC_443 = (1 << 14),
// STANDARD_NTSC_443 is a hybrid standard with 525 lines, 60 Hz refresh rate, and NTSC color modulation with a 4.43 MHz color subcarrier.
    STANDARD_NTSC_M_KR = (1 << 15),

    STANDARD_SECAM_B = (1 << 16),
    STANDARD_SECAM_D = (1 << 17),
    STANDARD_SECAM_G = (1 << 18),
    STANDARD_SECAM_H = (1 << 19),
    STANDARD_SECAM_K = (1 << 20),
    STANDARD_SECAM_K1 = (1 << 21),
    STANDARD_SECAM_L = (1 << 22),
    STANDARD_SECAM_LC = (1 << 23),

// ATSC/HDTV
    STANDARD_ATSC_8_VSB = (1 << 24),
    STANDARD_ATSC_16_VSB = (1 << 25),

// Some common needed stuff
    STANDARD_PAL_BG = (STANDARD_PAL_B   | STANDARD_PAL_B1   | STANDARD_PAL_G),
    STANDARD_PAL_DK = (STANDARD_PAL_D   | STANDARD_PAL_D1   | STANDARD_PAL_K),
    STANDARD_PAL = (STANDARD_PAL_BG  | STANDARD_PAL_DK   | STANDARD_PAL_H    | STANDARD_PAL_I),
    STANDARD_NTSC = (STANDARD_NTSC_M  | STANDARD_NTSC_M_JP| STANDARD_NTSC_M_KR),
    STANDARD_SECAM_DK = (STANDARD_SECAM_D | STANDARD_SECAM_K  | STANDARD_SECAM_K1),
    STANDARD_SECAM = (STANDARD_SECAM_B | STANDARD_SECAM_G  | STANDARD_SECAM_H  | STANDARD_SECAM_DK | STANDARD_SECAM_L | STANDARD_SECAM_LC),

// some merged standards */
    STANDARD_MN = (STANDARD_PAL_M  | STANDARD_PAL_N    | STANDARD_PAL_Nc  | STANDARD_NTSC),
    STANDARD_B = (STANDARD_PAL_B  | STANDARD_PAL_B1   | STANDARD_SECAM_B),
    STANDARD_GH = (STANDARD_PAL_G  | STANDARD_PAL_H    | STANDARD_SECAM_G | STANDARD_SECAM_H),
    STANDARD_DK = (STANDARD_PAL_DK | STANDARD_SECAM_DK),

    STANDARD_525_60 = (STANDARD_PAL_M   | STANDARD_PAL_60   | STANDARD_NTSC     | STANDARD_NTSC_443),
    STANDARD_625_50 = (STANDARD_PAL     | STANDARD_PAL_N    | STANDARD_PAL_Nc   | STANDARD_SECAM),
    STANDARD_ATSC = (STANDARD_ATSC_8_VSB | STANDARD_ATSC_16_VSB),

    STANDARD_UNKNOWN = 0,
    STANDARD_ALL = (STANDARD_525_60  | STANDARD_625_50)
} signal_standard;

typedef enum
{
    IO_METHOD_NONE,
    IO_METHOD_READ,
    IO_METHOD_MMAP,
    IO_METHOD_USERPTR
} io_method;

struct imagebuffer
{
    int height;
    int width;
    pixel_format pixelformat;
    QVector <uchar> data; // maybe it should be a rawbuffer instead of it? It could make us avoid a memory copy
};

struct rawbuffer // raw buffer
{
    uchar *start;
    size_t length;
};

/*!
    \class ActionVideoControl
    Data structure for action-video-controls
*/
class ActionVideoControl
{
public:
    quint32 id;     /*!< The ID of the control */
    QString name;       /*!< The name/title of the control */
};

/*!
    \class BooleanVideoControl
    Data structure for boolean video-controls
*/
class BooleanVideoControl : public ActionVideoControl
{
public:
    qint32 value_default;   /*!< The default value for the control */
};

/*!
    \class NumericVideoControl
    Data structure for numeric video-controls
*/
class NumericVideoControl : public BooleanVideoControl
{
public:
    qint32 value_min;   /*!< The minimum value for the control */
    qint32 value_max;   /*!< The maximum value allowed for the control */
    qint32 value_step;  /*!< The step-size which has to be considered when changing the value of the control */
};

/*!
    \class MenuVideoControl
    Data structure for menu-video-controls
*/
class MenuVideoControl : public ActionVideoControl
{
public:
    qint32 index_default;   /*!< The index of the default option for this control */
    QStringList options;    /*!< The list of options provided by the control */
};

class VideoDevice
{
public:
    VideoDevice();
    virtual ~VideoDevice();
    int setFileName(QString filename);
    QString fileName();
    void setUdi(const QString &);
    QString udi() const;
    virtual int open();
    virtual bool isOpen();

    int showDeviceCapabilities();

    int width();
    int minWidth();
    int maxWidth();
    int height();
    int minHeight();
    int maxHeight();
    virtual int setSize(int newwidth, int newheight);

    virtual pixel_format setPixelFormat(pixel_format newformat);
    int pixelFormatCode(pixel_format pixelformat);
    pixel_format pixelFormatForPalette(int palette);
    int pixelFormatDepth(pixel_format pixelformat);
    QString pixelFormatName(pixel_format pixelformat);
    QString pixelFormatName(int pixelformat);
    int detectPixelFormats();

    __u64 signalStandardCode(signal_standard standard);
    QString signalStandardName(signal_standard standard);
    QString signalStandardName(int standard);
    int detectSignalStandards();

    int inputs();
    int currentInput();
    int selectInput(int input);

    virtual int startCapturing();
    virtual int getFrame();
    virtual int getFrame(imagebuffer *imgbuffer);
    virtual int getImage(QImage *qimage);
    virtual int stopCapturing();
    virtual int close();

    QList<NumericVideoControl> getSupportedNumericControls();
    QList<BooleanVideoControl> getSupportedBooleanControls();
    QList<MenuVideoControl> getSupportedMenuControls();
    QList<ActionVideoControl> getSupportedActionControls();

    int getControlValue(quint32 ctrl_id, qint32 *value);
    int setControlValue(quint32 ctrl_id, qint32 value);

    bool canCapture();
    bool canChromakey();
    bool canScale();
    bool canOverlay();
    bool canRead();
    bool canAsyncIO();
    bool canStream();

    QString m_name;
    QVector<Kopete::AV::VideoInput> m_input;

protected:
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(ENABLE_AV)
    /*!
        \enum VideoDevice::imgctrl_id Control-IDs used for V4L1- and software-controls
     */
    typedef enum
    {
        IMGCTRL_ID_V4L1_BRIGHTNESS,
        IMGCTRL_ID_V4L1_HUE,
        IMGCTRL_ID_V4L1_COLOR,
        IMGCTRL_ID_V4L1_CONTRAST,
        IMGCTRL_ID_V4L1_WHITENESS,
        IMGCTRL_ID_SOFT_AUTOBRIGHTNESSCONTRASTCORR,
        IMGCTRL_ID_SOFT_AUTOCOLORCORR,
        IMGCTRL_ID_SOFT_VFLIP,
        IMGCTRL_ID_SOFT_HFLIP
    } imgctrl_id;
    /* NOTE: V4L2_CID_BASE = 0x980900 */
#endif

    QList<NumericVideoControl> m_numericCtrls;  /*!< Supported muneric controls for the current input */
    QList<BooleanVideoControl> m_booleanCtrls;  /*!< Supported boolean controls for the current input */
    QList<MenuVideoControl> m_menuCtrls;        /*!< Supported menu-controls for the current input */
    QList<ActionVideoControl> m_actionCtrls;    /*!< Supported action-controls for the current input */

    QString full_filename;
    QString m_udi;
    int descriptor;
    videodev_driver m_driver;
    QString m_model;
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(ENABLE_AV)
#ifdef V4L2_CAP_VIDEO_CAPTURE
    struct v4l2_capability V4L2_capabilities;
    struct v4l2_format fmt;
//	struct v4l2_input m_input;
#endif
#ifdef HAVE_V4L
    struct video_capability V4L_capabilities;
    struct video_buffer V4L_videobuffer;
#endif
#endif
    int currentwidth, minwidth, maxwidth, currentheight, minheight, maxheight;

    QVector<rawbuffer> m_rawbuffers;
    unsigned int m_streambuffers;
    imagebuffer m_currentbuffer;
    int m_buffer_size;

    int m_current_input;
    pixel_format m_pixelformat;

    io_method m_io_method;
    bool m_videocapture;
    bool m_videochromakey;
    bool m_videoscale;
    bool m_videooverlay;
    bool m_videoread;
    bool m_videoasyncio;
    bool m_videostream;

    virtual int checkDevice();
    virtual int initDevice();

    void setupControls();
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(ENABLE_AV) && defined(V4L2_CAP_VIDEO_CAPTURE)
    bool getMenuCtrlOptions(quint32 id, quint32 maxindex, QStringList *options);
    void saveV4L2ControlData(struct v4l2_queryctrl qctrl);
    const char *getUnifiedV4L2StdCtrlName(quint32 std_ctrl_id);
#endif
    int xioctl(int request, void *arg);
    int errnoReturn(const char *s);
    int initRead();
    int initMmap();
    int initUserptr();
};
}
}

#endif
