/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2011 Sam Lantinga

    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.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org

    SDL1.3 DirectFB driver by couriersud@arcor.de
	
*/

#include "SDL_DirectFB_video.h"
#include "SDL_DirectFB_modes.h"
#include "SDL_DirectFB_window.h"
#include "SDL_DirectFB_shape.h"

#if SDL_DIRECTFB_OPENGL
#include "SDL_DirectFB_opengl.h"
#endif

#include "SDL_syswm.h"

#include "../SDL_pixels_c.h"

int
DirectFB_CreateWindow(_THIS, SDL_Window * window)
{
    SDL_DFB_DEVICEDATA(_this);
    SDL_DFB_DISPLAYDATA(window);
    DFB_WindowData *windata = NULL;
    DFBWindowOptions wopts;
    DFBWindowDescription desc;
    int x, y;
    int bshaped = 0;

    SDL_DFB_ALLOC_CLEAR(window->driverdata, sizeof(DFB_WindowData));
    windata = (DFB_WindowData *) window->driverdata;

    windata->is_managed = devdata->has_own_wm;
#if 1
    SDL_DFB_CHECKERR(devdata->dfb->SetCooperativeLevel(devdata->dfb,
                                                       DFSCL_NORMAL));
    SDL_DFB_CHECKERR(dispdata->layer->SetCooperativeLevel(dispdata->layer,
                                                          DLSCL_ADMINISTRATIVE));
#endif
	/* FIXME ... ughh, ugly */
	if (window->x == -1000 && window->y == -1000)
		bshaped = 1;
	
    /* Fill the window description. */
    if (SDL_WINDOWPOS_ISCENTERED(window->x)) {
        x = (dispdata->cw - window->w) / 2;
    } else if (SDL_WINDOWPOS_ISUNDEFINED(window->x)) {
        x = 0;
    } else {
        x = window->x;
    }
    
    if (SDL_WINDOWPOS_ISCENTERED(window->y)) {
        y = (dispdata->ch - window->h) / 2;
    } else if (SDL_WINDOWPOS_ISUNDEFINED(window->y)) {
        y = 0;
    } else {
        y = window->y;
    }
    
    if (window->flags & SDL_WINDOW_FULLSCREEN) {
        x = 0;
        y = 0;
    }

    DirectFB_WM_AdjustWindowLayout(window, window->flags, window->w, window->h);

    /* Create Window */
	desc.caps = 0;
    desc.flags =
        DWDESC_WIDTH | DWDESC_HEIGHT | DWDESC_POSX | DWDESC_POSY | DWDESC_SURFACE_CAPS;

    if (bshaped) {
    	desc.flags |= DWDESC_CAPS;
    	desc.caps |= DWCAPS_ALPHACHANNEL;
    }
    else
    {
    	desc.flags |= DWDESC_PIXELFORMAT;
    }

    if (!(window->flags & SDL_WINDOW_BORDERLESS))
		desc.caps |= DWCAPS_NODECORATION;

    desc.posx = x;
    desc.posy = y;
    desc.width = windata->size.w;
    desc.height = windata->size.h;
    desc.pixelformat = dispdata->pixelformat;
    desc.surface_caps = DSCAPS_PREMULTIPLIED;
    
    /* Create the window. */
    SDL_DFB_CHECKERR(dispdata->layer->CreateWindow(dispdata->layer, &desc,
                                                   &windata->dfbwin));

    /* Set Options */
    SDL_DFB_CHECK(windata->dfbwin->GetOptions(windata->dfbwin, &wopts));

    /* explicit rescaling of surface */
    wopts |= DWOP_SCALE;
    if (window->flags & SDL_WINDOW_RESIZABLE) {
        wopts &= ~DWOP_KEEP_SIZE;
    }
    else {
        wopts |= DWOP_KEEP_SIZE;
    }

    if (window->flags & SDL_WINDOW_FULLSCREEN) {
        wopts |= DWOP_KEEP_POSITION | DWOP_KEEP_STACKING | DWOP_KEEP_SIZE;
        SDL_DFB_CHECK(windata->dfbwin->SetStackingClass(windata->dfbwin, DWSC_UPPER));
    }
    
    if (bshaped) {
        wopts |= DWOP_SHAPED | DWOP_ALPHACHANNEL;
        wopts &= ~DWOP_OPAQUE_REGION;
    }

    SDL_DFB_CHECK(windata->dfbwin->SetOptions(windata->dfbwin, wopts));

    /* See what we got */
    SDL_DFB_CHECK(DirectFB_WM_GetClientSize
                     (_this, window, &window->w, &window->h));

    /* Get the window's surface. */
    SDL_DFB_CHECKERR(windata->dfbwin->GetSurface(windata->dfbwin,
                                                 &windata->window_surface));

    /* And get a subsurface for rendering */
    SDL_DFB_CHECKERR(windata->window_surface->
                     GetSubSurface(windata->window_surface, &windata->client,
                                   &windata->surface));

    SDL_DFB_CHECK(windata->dfbwin->SetOpacity(windata->dfbwin, 0xFF));

    /* Create Eventbuffer */

    SDL_DFB_CHECKERR(windata->dfbwin->CreateEventBuffer(windata->dfbwin,
                                                        &windata->
                                                        eventbuffer));
    SDL_DFB_CHECKERR(windata->dfbwin->
                     EnableEvents(windata->dfbwin, DWET_ALL));

    /* Create a font */
    /* FIXME: once during Video_Init */
    windata->font = NULL;

    /* Make it the top most window. */
    SDL_DFB_CHECK(windata->dfbwin->RaiseToTop(windata->dfbwin));

    /* remember parent */
    //windata->sdlwin = window;

    /* Add to list ... */

    windata->next = devdata->firstwin;
    windata->opacity = 0xFF;
    devdata->firstwin = window;

    /* Draw Frame */
    DirectFB_WM_RedrawLayout(_this, window);

    return 0;
  error:
	SDL_DFB_RELEASE(windata->surface);
    SDL_DFB_RELEASE(windata->dfbwin);
    return -1;
}

int
DirectFB_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
{
    SDL_Unsupported();
    return -1;
}

void
DirectFB_SetWindowTitle(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);

    if (windata->is_managed) {
        windata->wm_needs_redraw = 1;
        DirectFB_WM_RedrawLayout(_this, window);
    } else
        SDL_Unsupported();
}

void
DirectFB_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
{
    SDL_DFB_DEVICEDATA(_this);
    SDL_DFB_WINDOWDATA(window);
    SDL_Surface *surface = NULL;

    if (icon) {
        SDL_PixelFormat format;
        DFBSurfaceDescription dsc;
        Uint32 *dest;
        Uint32 *p;
        int pitch, i;

        /* Convert the icon to ARGB for modern window managers */
        SDL_InitFormat(&format, SDL_PIXELFORMAT_ARGB8888);
        surface = SDL_ConvertSurface(icon, &format, 0);
        if (!surface) {
            return;
        }
        dsc.flags =
            DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_CAPS;
        dsc.caps = DSCAPS_VIDEOONLY;
        dsc.width = surface->w;
        dsc.height = surface->h;
        dsc.pixelformat = DSPF_ARGB;

        SDL_DFB_CHECKERR(devdata->dfb->CreateSurface(devdata->dfb, &dsc,
                                                     &windata->icon));

        SDL_DFB_CHECKERR(windata->icon->Lock(windata->icon, DSLF_WRITE,
                                             (void *) &dest, &pitch));

        p = surface->pixels;
        for (i = 0; i < surface->h; i++)
            memcpy((char *) dest + i * pitch,
                   (char *) p + i * surface->pitch, 4 * surface->w);

        SDL_DFB_CHECK(windata->icon->Unlock(windata->icon));
        SDL_FreeSurface(surface);
    } else {
        SDL_DFB_RELEASE(windata->icon);
    }
    return;
  error:
    if (surface)
        SDL_FreeSurface(surface);
    SDL_DFB_RELEASE(windata->icon);
    return;
}

void
DirectFB_SetWindowPosition(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);
    SDL_DFB_DISPLAYDATA(window);
    int x, y;

    if (SDL_WINDOWPOS_ISCENTERED(window->x)) {
        x = (dispdata->cw - window->w) / 2;
    } else if (SDL_WINDOWPOS_ISUNDEFINED(window->x)) {
        x = 0;
    } else {
        x = window->x;
    }
    
    if (SDL_WINDOWPOS_ISCENTERED(window->y)) {
        y = (dispdata->ch - window->h) / 2;
    } else if (SDL_WINDOWPOS_ISUNDEFINED(window->y)) {
        y = 0;
    } else {
        y = window->y;
    }

    if (window->flags & SDL_WINDOW_FULLSCREEN) {
        x = 0;
        y = 0;
    }
    DirectFB_WM_AdjustWindowLayout(window, window->flags, window->w, window->h);
    SDL_DFB_CHECK(windata->dfbwin->MoveTo(windata->dfbwin, x, y));
}

void
DirectFB_SetWindowSize(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);

    if(SDL_IsShapedWindow(window))
        DirectFB_ResizeWindowShape(window);

    if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
        int cw;
        int ch;

        /* Make sure all events are disabled for this operation ! */
        SDL_DFB_CHECKERR(windata->dfbwin->DisableEvents(windata->dfbwin,
                                                        DWET_ALL));
        SDL_DFB_CHECKERR(DirectFB_WM_GetClientSize(_this, window, &cw, &ch));

        if (cw != window->w || ch != window->h) {

		    DirectFB_WM_AdjustWindowLayout(window, window->flags, window->w, window->h);
            SDL_DFB_CHECKERR(windata->dfbwin->Resize(windata->dfbwin,
                                                     windata->size.w,
                                                     windata->size.h));
        }

        SDL_DFB_CHECKERR(DirectFB_WM_GetClientSize
                     (_this, window, &window->w, &window->h));
        DirectFB_AdjustWindowSurface(window);

        SDL_DFB_CHECKERR(windata->dfbwin->EnableEvents(windata->dfbwin,
                                                       DWET_ALL));

    }
    return;
  error:
    SDL_DFB_CHECK(windata->dfbwin->EnableEvents(windata->dfbwin, DWET_ALL));
    return;
}

void
DirectFB_ShowWindow(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);

    SDL_DFB_CHECK(windata->dfbwin->SetOpacity(windata->dfbwin, windata->opacity));

}

void
DirectFB_HideWindow(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);

    SDL_DFB_CHECK(windata->dfbwin->GetOpacity(windata->dfbwin, &windata->opacity));
    SDL_DFB_CHECK(windata->dfbwin->SetOpacity(windata->dfbwin, 0));
}

void
DirectFB_RaiseWindow(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);

    SDL_DFB_CHECK(windata->dfbwin->RaiseToTop(windata->dfbwin));
    SDL_DFB_CHECK(windata->dfbwin->RequestFocus(windata->dfbwin));
}

void
DirectFB_MaximizeWindow(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
    DFBWindowOptions wopts;

    SDL_DFB_CHECK(windata->dfbwin->GetPosition(windata->dfbwin,
                                 &windata->restore.x, &windata->restore.y));
    SDL_DFB_CHECK(windata->dfbwin->GetSize(windata->dfbwin, &windata->restore.w,
                             &windata->restore.h));

    DirectFB_WM_AdjustWindowLayout(window, window->flags | SDL_WINDOW_MAXIMIZED, display->current_mode.w, display->current_mode.h) ;

    SDL_DFB_CHECK(windata->dfbwin->MoveTo(windata->dfbwin, 0, 0));
    SDL_DFB_CHECK(windata->dfbwin->Resize(windata->dfbwin,
                            display->current_mode.w, display->current_mode.h));

    /* Set Options */
    SDL_DFB_CHECK(windata->dfbwin->GetOptions(windata->dfbwin, &wopts));
    wopts |= DWOP_KEEP_SIZE | DWOP_KEEP_POSITION;
    SDL_DFB_CHECK(windata->dfbwin->SetOptions(windata->dfbwin, wopts));
}

void
DirectFB_MinimizeWindow(_THIS, SDL_Window * window)
{
    /* FIXME: Size to 32x32 ? */

    SDL_Unsupported();
}

void
DirectFB_RestoreWindow(_THIS, SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);
    DFBWindowOptions wopts;

    /* Set Options */
    SDL_DFB_CHECK(windata->dfbwin->GetOptions(windata->dfbwin, &wopts));
    wopts &= ~(DWOP_KEEP_SIZE | DWOP_KEEP_POSITION);
    SDL_DFB_CHECK(windata->dfbwin->SetOptions(windata->dfbwin, wopts));

    /* Window layout */
    DirectFB_WM_AdjustWindowLayout(window, window->flags & ~(SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED),
    	windata->restore.w, windata->restore.h);
    SDL_DFB_CHECK(windata->dfbwin->Resize(windata->dfbwin, windata->restore.w,
                            windata->restore.h));
    SDL_DFB_CHECK(windata->dfbwin->MoveTo(windata->dfbwin, windata->restore.x,
                            windata->restore.y));

    if (!(window->flags & SDL_WINDOW_RESIZABLE))
        wopts |= DWOP_KEEP_SIZE;

    if (window->flags & SDL_WINDOW_FULLSCREEN)
        wopts |= DWOP_KEEP_POSITION | DWOP_KEEP_SIZE;
    SDL_DFB_CHECK(windata->dfbwin->SetOptions(windata->dfbwin, wopts));


}

void
DirectFB_SetWindowGrab(_THIS, SDL_Window * window)
{
    SDL_DFB_DEVICEDATA(_this);
    SDL_DFB_WINDOWDATA(window);
    DFB_WindowData *gwindata = ((devdata->grabbed_window) ? (DFB_WindowData *) ((devdata->grabbed_window)->driverdata) : NULL);

    if ((window->flags & SDL_WINDOW_INPUT_GRABBED)) {
        if (gwindata != NULL)
        {
		    SDL_DFB_CHECK(gwindata->dfbwin->UngrabPointer(gwindata->dfbwin));
		    SDL_DFB_CHECK(gwindata->dfbwin->UngrabKeyboard(gwindata->dfbwin));
        }
        SDL_DFB_CHECK(windata->dfbwin->GrabPointer(windata->dfbwin));
        SDL_DFB_CHECK(windata->dfbwin->GrabKeyboard(windata->dfbwin));
        devdata->grabbed_window = window;
    } else {
        SDL_DFB_CHECK(windata->dfbwin->UngrabPointer(windata->dfbwin));
        SDL_DFB_CHECK(windata->dfbwin->UngrabKeyboard(windata->dfbwin));
        devdata->grabbed_window = NULL;
    }
}

void
DirectFB_DestroyWindow(_THIS, SDL_Window * window)
{
    SDL_DFB_DEVICEDATA(_this);
    SDL_DFB_WINDOWDATA(window);
    DFB_WindowData *p;

    /* Some cleanups */
    SDL_DFB_CHECK(windata->dfbwin->UngrabPointer(windata->dfbwin));
    SDL_DFB_CHECK(windata->dfbwin->UngrabKeyboard(windata->dfbwin));

#if SDL_DIRECTFB_OPENGL
	DirectFB_GL_DestroyWindowContexts(_this, window);
#endif

	if (window->shaper)
	{
	    SDL_ShapeData *data = window->shaper->driverdata;
	    SDL_DFB_CHECK(data->surface->ReleaseSource(data->surface));
    	SDL_DFB_RELEASE(data->surface);
    	SDL_DFB_FREE(data);
    	SDL_DFB_FREE(window->shaper);
	}

    SDL_DFB_CHECK(windata->window_surface->SetFont(windata->window_surface, NULL));
    SDL_DFB_CHECK(windata->surface->ReleaseSource(windata->surface));
    SDL_DFB_CHECK(windata->window_surface->ReleaseSource(windata->window_surface));
  	SDL_DFB_RELEASE(windata->icon);
    SDL_DFB_RELEASE(windata->font);
    SDL_DFB_RELEASE(windata->eventbuffer);
    SDL_DFB_RELEASE(windata->surface);
    SDL_DFB_RELEASE(windata->window_surface);

    SDL_DFB_RELEASE(windata->dfbwin);

    /* Remove from list ... */

    p = devdata->firstwin->driverdata;

    while (p && p->next != window)
        p = (p->next ? p->next->driverdata : NULL);
    if (p)
        p->next = windata->next;
    else
        devdata->firstwin = windata->next;
    SDL_free(windata);
    return;
}

SDL_bool
DirectFB_GetWindowWMInfo(_THIS, SDL_Window * window,
                         struct SDL_SysWMinfo * info)
{
    SDL_DFB_DEVICEDATA(_this);
    SDL_DFB_WINDOWDATA(window);

    if (info->version.major == SDL_MAJOR_VERSION &&
        info->version.minor == SDL_MINOR_VERSION) {
        info->subsystem = SDL_SYSWM_DIRECTFB;
        info->info.dfb.dfb = devdata->dfb;
        info->info.dfb.window = windata->dfbwin;
        info->info.dfb.surface = windata->surface;
        return SDL_TRUE;
    } else {
        SDL_SetError("Application not compiled with SDL %d.%d\n",
                     SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
        return SDL_FALSE;
    }
}

void
DirectFB_AdjustWindowSurface(SDL_Window * window)
{
    SDL_DFB_WINDOWDATA(window);
    int adjust = windata->wm_needs_redraw;
    int cw, ch;

    DirectFB_WM_AdjustWindowLayout(window, window->flags, window->w, window->h);

    SDL_DFB_CHECKERR(windata->
                     window_surface->GetSize(windata->window_surface, &cw,
                                             &ch));
    if (cw != windata->size.w || ch != windata->size.h) {
        adjust = 1;
    }

    if (adjust) {
#if SDL_DIRECTFB_OPENGL
		DirectFB_GL_FreeWindowContexts(SDL_GetVideoDevice(), window);
#endif

#if (DFB_VERSION_ATLEAST(1,2,1))
        SDL_DFB_CHECKERR(windata->dfbwin->ResizeSurface(windata->dfbwin,
                                                        windata->size.w,
                                                        windata->size.h));
        SDL_DFB_CHECKERR(windata->surface->MakeSubSurface(windata->surface,
                                                          windata->
                                                          window_surface,
                                                          &windata->client));
#else
        DFBWindowOptions opts;

        SDL_DFB_CHECKERR(windata->dfbwin->GetOptions(windata->dfbwin, &opts));
        /* recreate subsurface */
        SDL_DFB_RELEASE(windata->surface);

        if (opts & DWOP_SCALE)
            SDL_DFB_CHECKERR(windata->dfbwin->ResizeSurface(windata->dfbwin,
                                                            windata->size.w,
                                                            windata->size.h));
        SDL_DFB_CHECKERR(windata->window_surface->
                         GetSubSurface(windata->window_surface,
                                       &windata->client, &windata->surface));
#endif
        DirectFB_WM_RedrawLayout(SDL_GetVideoDevice(), window);
        
#if SDL_DIRECTFB_OPENGL
		DirectFB_GL_ReAllocWindowContexts(SDL_GetVideoDevice(), window);
#endif
   }
  error:
    return;
}
