/*
 *  vo_kva.c
 *
 *  Copyleft 2007 by KO Myung-Hun (komh@chollian.net)
 *
 */

#define INCL_WIN
#include <os2.h>

#include <fourcc.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "config.h"
#include "mp_msg.h"
#include "help_mp.h"
#include "video_out.h"
#include "video_out_internal.h"

#include "fastmemcpy.h"
#include "osdep/keycodes.h"
#include "input/input.h"
#include "input/mouse.h"
#include "subopt-helper.h"

#include "kva.h"

static vo_info_t info =
{
    "DIVE/WarpOverlay! video output",
    "kva",
    "KO Myung-Hun <komh@chollian.net>",
    ""
};

LIBVO_EXTERN( kva )

#define WC_MPLAYER      "WC_MPLAYER"
#define COLOR_OVERLAY   0x000008

typedef struct tagVOKVAINTERNAL
{
    HAB         hab;
    HMQ         hmq;
    HWND        hwndFrame;
    HWND        hwndClient;
    HWND        hwndSysMenu;
    HWND        hwndTitleBar;
    HWND        hwndMinMax;
    BOOL        fFullScreen;
    FOURCC      fcc;
    INT         iImageFormat;
    KVASETUP    kvas;
    KVACAPS     kvac;
    RECTL       rclDst;
    INT         bpp;
    LONG        lStride;
    PBYTE       pbImage;
} VOKVAINTERNAL, *PVOKVAINTERNAL;

static VOKVAINTERNAL m_int;

extern void mplayer_put_key( int code );            // let mplayer handel the keyevents

static PBYTE imgCreate( VOID )
{
    return malloc( m_int.kvas.szlSrcSize.cy * m_int.lStride );
}

static VOID imgFree( PBYTE pbImage )
{
    if( pbImage )
        free( pbImage );
}

static VOID imgDisplay( PBYTE pbImage )
{
    PVOID       pBuffer;
    ULONG       ulBPL;

    if( !kvaLockBuffer( &pBuffer, &ulBPL ))
    {
        memcpy( pBuffer, pbImage, m_int.kvas.szlSrcSize.cy * ulBPL );

        kvaUnlockBuffer();
    }
}

#define MRETURN( ret )  return ( MRESULT )( ret )

static MRESULT EXPENTRY WndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
    switch( msg )
    {
        case WM_CLOSE :
            mplayer_put_key( KEY_CLOSE_WIN );

            MRETURN( 0 );

        case WM_CHAR :
        {
            USHORT fsFlags = SHORT1FROMMP( mp1 );
            USHORT usCh = SHORT1FROMMP( mp2 );
            USHORT usVk = SHORT2FROMMP( mp2 );

            if( fsFlags & KC_KEYUP )
                break;

            if( fsFlags & KC_VIRTUALKEY )
            {
                switch( usVk )
                {
                    case VK_LEFT :
                        mplayer_put_key( KEY_LEFT );
                        break;

                    case VK_UP :
                        mplayer_put_key( KEY_UP );
                        break;

                    case VK_RIGHT :
                        mplayer_put_key( KEY_RIGHT );
                        break;

                    case VK_DOWN :
                        mplayer_put_key( KEY_DOWN );
                        break;

                    case VK_TAB :
                        mplayer_put_key( KEY_TAB );
                        break;

                    case VK_BACKSPACE :
                        mplayer_put_key( KEY_BS );
                        break;

                    case VK_DELETE :
                        mplayer_put_key( KEY_DELETE );
                        break;

                    case VK_INSERT :
                        mplayer_put_key( KEY_INSERT );
                        break;

                    case VK_HOME :
                        mplayer_put_key( KEY_HOME );
                        break;

                    case VK_END :
                        mplayer_put_key( KEY_END );
                        break;

                    case VK_PAGEUP :
                        mplayer_put_key( KEY_PAGE_UP );
                        break;

                    case VK_PAGEDOWN :
                        mplayer_put_key( KEY_PAGE_DOWN );
                        break;

                    case VK_ESC :
                        mplayer_put_key( KEY_ESC );
                        break;

                    case VK_SPACE :
                        mplayer_put_key(' ');
                        break;
                }
            }
            else if(( fsFlags & KC_CHAR ) && !HIBYTE( usCh ))
                mplayer_put_key( usCh );

            MRETURN( TRUE );
        }

        case WM_BUTTON1DOWN :
            if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
            {
                WinSetFocus( HWND_DESKTOP, hwnd );
                MRETURN( TRUE );
            }

            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN0 );

            MRETURN( TRUE );

        case WM_BUTTON3DOWN :
            if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
            {
                WinSetFocus( HWND_DESKTOP, hwnd );
                MRETURN( TRUE );
            }

            if( !vo_nomouse_input )
               mplayer_put_key( MOUSE_BTN1 );

            MRETURN( TRUE );

        case WM_BUTTON2DOWN :
            if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
            {
                WinSetFocus( HWND_DESKTOP, hwnd );
                MRETURN( TRUE );
            }

            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN2 );

            MRETURN( TRUE );

        case WM_BUTTON1DBLCLK :
            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN0_DBL );

            MRETURN( TRUE );

        case WM_BUTTON3DBLCLK :
            if( !vo_nomouse_input )
                mplayer_put_key( MOUSE_BTN1_DBL );

            MRETURN( TRUE );

        case WM_BUTTON2DBLCLK :
            if( !vo_nomouse_input)
                mplayer_put_key( MOUSE_BTN2_DBL );

            MRETURN( TRUE );
    }

    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
}

static int preinit( const char *arg )
{
    ULONG   flFrameFlags;

    BOOL    fUseDive = FALSE;

    opt_t subopts[] = {
        {"dive", OPT_ARG_BOOL,  &fUseDive, NULL, 0},
        {NULL, 0, NULL, NULL, 0}
    };

    if( subopt_parse( arg, subopts ) != 0 )
        return -1;

    memset( &m_int, 0, sizeof( VOKVAINTERNAL ));

    m_int.hab = WinInitialize( 0 );
    m_int.hmq = WinCreateMsgQueue( m_int.hab, 0);

    WinRegisterClass(
        m_int.hab,
        WC_MPLAYER,
        WndProc,
        CS_SIZEREDRAW | CS_MOVENOTIFY,
        sizeof( PVOID )
    );

    flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER |
                   FCF_TASKLIST;

    m_int.hwndFrame =
        WinCreateStdWindow(
            HWND_DESKTOP,               // parent window handle
            WS_VISIBLE,                 // frame window style
            &flFrameFlags,              // window style
            WC_MPLAYER,                 // class name
            "",                         // window title
            0L,                         // default client style
            NULLHANDLE,                 // resource in exe file
            1,                          // frame window id
            &m_int.hwndClient           // client window handle
        );

    if( m_int.hwndFrame == NULLHANDLE )
        return -1;

    m_int.hwndSysMenu = WinWindowFromID( m_int.hwndFrame, FID_SYSMENU );
    m_int.hwndTitleBar = WinWindowFromID( m_int.hwndFrame, FID_TITLEBAR );
    m_int.hwndMinMax = WinWindowFromID( m_int.hwndFrame, FID_MINMAX );

    m_int.fFullScreen = FALSE;

    if( kvaInit( fUseDive, m_int.hwndClient, COLOR_OVERLAY ))
        return -1;

    kvaCaps( &m_int.kvac );

    switch( m_int.kvac.ulMode )
    {
        case KVAM_WO :
            m_int.fcc = FOURCC_Y422;
            m_int.iImageFormat = IMGFMT_YUY2;
            m_int.bpp = 2;
            break;

        case KVAM_DIVE :
        default :
            switch( m_int.kvac.fccScreen )
            {
                case FOURCC_BGR4 :
                case FOURCC_BGR3 :
                case FOURCC_LUT8 :  // maybe best T.T
                    m_int.fcc = FOURCC_BGR3;
                    m_int.iImageFormat = IMGFMT_BGR24;
                    m_int.bpp = 3;
                    break;

                case FOURCC_R565 :
                    m_int.fcc = FOURCC_R565;
                    m_int.iImageFormat = IMGFMT_BGR16;
                    m_int.bpp = 2;
                    break;

                case FOURCC_R555 :
                    m_int.fcc = FOURCC_R555;
                    m_int.iImageFormat = IMGFMT_BGR15;
                    m_int.bpp = 2;
                    break;
            }
            break;
    }

    return 0;
}

static void uninit( void )
{
    if( m_int.pbImage )
        imgFree( m_int.pbImage );

    if( m_int.hwndFrame != NULLHANDLE )
    {
        kvaDone();
        WinDestroyWindow( m_int.hwndFrame );
    }

    WinDestroyMsgQueue( m_int.hmq );
    WinTerminate( m_int.hab );
}

static int config( uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format )
{
    RECTL   rcl;

    if( format != m_int.iImageFormat )
    {
        mp_msg( MSGT_VO, MSGL_ERR, "KVA : Unsupported image format 0x%X(%s)\n", format, vo_format_name(format));
        return -1;
    }

    if( mp_msg_test( MSGT_VO, MSGL_V ))
        mp_msg( MSGT_VO, MSGL_V, "KVA: Using 0x%X (%s) image format\n", format, vo_format_name(format));

    if( m_int.pbImage )
        imgFree( m_int.pbImage );

    m_int.kvas.ulLength = sizeof( KVASETUP );
    m_int.kvas.szlSrcSize.cx = width;
    m_int.kvas.szlSrcSize.cy = height;
    m_int.kvas.rclSrcRect.xLeft = 0;
    m_int.kvas.rclSrcRect.yTop = 0;
    m_int.kvas.rclSrcRect.xRight = width;
    m_int.kvas.rclSrcRect.yBottom = height;
    m_int.kvas.ulRatio = KVAR_ORIGINAL;
    m_int.kvas.fccSrcColor = m_int.fcc;

    kvaSetup( &m_int.kvas );

    m_int.lStride = width * m_int.bpp;

    m_int.pbImage = imgCreate();

    WinSetWindowText( m_int.hwndFrame, title );

    if( !d_width )
        d_width = width;

    if( !d_height )
        d_height = height;

    m_int.rclDst.xLeft = (( LONG )m_int.kvac.cxScreen - ( LONG )d_width ) / 2;
    m_int.rclDst.yBottom = (( LONG )m_int.kvac.cyScreen - ( LONG )d_height ) /2 ;
    m_int.rclDst.xRight = m_int.rclDst.xLeft + d_width;
    m_int.rclDst.yTop = m_int.rclDst.yBottom + d_height;

    m_int.fFullScreen = flags & VOFLAG_FULLSCREEN;
    if( m_int.fFullScreen )
    {
        d_width = m_int.kvac.cxScreen;
        d_height = m_int.kvac.cyScreen;

        WinSetParent( m_int.hwndSysMenu, HWND_OBJECT, FALSE );
        WinSetParent( m_int.hwndTitleBar, HWND_OBJECT, FALSE );
        WinSetParent( m_int.hwndMinMax, HWND_OBJECT, FALSE );
    }

    rcl.xLeft = (( LONG )m_int.kvac.cxScreen - ( LONG )d_width ) / 2;
    rcl.yBottom = (( LONG )m_int.kvac.cyScreen - ( LONG )d_height ) /2 ;
    rcl.xRight = rcl.xLeft + d_width;
    rcl.yTop = rcl.yBottom + d_height;

    WinCalcFrameRect( m_int.hwndFrame, &rcl, FALSE );

    WinSetWindowPos( m_int.hwndFrame, HWND_TOP,
                     rcl.xLeft, rcl.yBottom,
                     rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
                     SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW | SWP_ACTIVATE );

    return 0;
}

static uint32_t get_image( mp_image_t *mpi )
{
    if( mpi->imgfmt != m_int.iImageFormat )
        return VO_FALSE;

    mpi->planes[0] = m_int.pbImage;
    mpi->stride[0] = m_int.lStride;
    mpi->flags |= MP_IMGFLAG_DIRECT;

    return VO_TRUE;
}

static int query_format( uint32_t format )
{
    if( format == m_int.iImageFormat )
        return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_OSD |
               VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;

    return 0;
}

static int control( uint32_t request, void *data, ... )
{
    switch( request )
    {
        case VOCTRL_GET_IMAGE :
            return get_image( data );

        case VOCTRL_QUERY_FORMAT :
            return query_format( *(( uint32_t * )data ));

        case VOCTRL_FULLSCREEN :
        {
            RECTL   rcl;

            m_int.fFullScreen = !m_int.fFullScreen;

            if( m_int.fFullScreen )
            {
                WinSetParent( m_int.hwndSysMenu, HWND_OBJECT, FALSE );
                WinSetParent( m_int.hwndTitleBar, HWND_OBJECT, FALSE );
                WinSetParent( m_int.hwndMinMax, HWND_OBJECT, FALSE );

                rcl.xLeft = 0;
                rcl.yBottom = 0;
                rcl.xRight = m_int.kvac.cxScreen;
                rcl.yTop = m_int.kvac.cyScreen;
            }
            else
            {
                WinSetParent( m_int.hwndSysMenu, m_int.hwndFrame, FALSE );
                WinSetParent( m_int.hwndTitleBar, m_int.hwndFrame, FALSE );
                WinSetParent( m_int.hwndMinMax, m_int.hwndFrame, FALSE );

                rcl = m_int.rclDst;
            }

            WinCalcFrameRect( m_int.hwndFrame, &rcl, FALSE );

            WinSetWindowPos( m_int.hwndFrame, HWND_TOP,
                             rcl.xLeft, rcl.yBottom,
                             rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
                             SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW | SWP_ACTIVATE );

            return VO_TRUE;
        }
    }

    return VO_NOTIMPL;
}

static int draw_frame( uint8_t *src[] )
{
    memcpy( m_int.pbImage, src[ 0 ], m_int.kvas.szlSrcSize.cy * m_int.lStride );

    return 0;
}

static int draw_slice( uint8_t *image[], int stride[], int w, int h, int x, int y )
{
    mp_msg( MSGT_VO, MSGL_ERR, "KVA : draw_slice() is not implemented\n");

    return 0;
}

#define vo_draw_alpha( imgfmt ) \
    vo_draw_alpha_##imgfmt( w, h, src, srca, stride, \
                            m_int.pbImage + m_int.lStride * y0 + m_int.bpp * x0 , \
                            m_int.lStride )

static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src,
        unsigned char *srca, int stride)
{
    switch( m_int.iImageFormat )
    {
        case IMGFMT_YUY2 :
            vo_draw_alpha( yuy2 );
            break;

        case IMGFMT_BGR24 :
            vo_draw_alpha( rgb24 );
            break;

        case IMGFMT_BGR16 :
            vo_draw_alpha( rgb16 );
            break;

        case IMGFMT_BGR15 :
            vo_draw_alpha( rgb15 );
            break;
    }
}

static void draw_osd( void )
{
    vo_draw_text( m_int.kvas.szlSrcSize.cx, m_int.kvas.szlSrcSize.cy, draw_alpha );
}

static void flip_page( void )
{
    imgDisplay( m_int.pbImage );
}

static void check_events( void )
{
    QMSG    qm;

    while( WinPeekMsg( m_int.hab, &qm, NULLHANDLE, 0, 0, PM_REMOVE ))
        WinDispatchMsg( m_int.hab, &qm );
}


