/*  $Id: graphic_context_sdl.cpp,v 1.17 2004/04/23 15:47:49 sphair Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "Display/display_precomp.h"
#include "Display/display_window_generic.h"
#include "graphic_context_sdl.h"
#include "API/Display/color.h"
#include "API/Core/System/error.h"
#include "API/Core/System/log.h"
#include "API/Display/gradient.h"

#include <SDL/SDL.h>

#ifdef HAVE_LIBSDL_GFX
#  include <SDL_gfxPrimitives.h>
#endif

/////////////////////////////////////////////////////////////////////////////
// CL_GraphicContext_SDL construction:

CL_GraphicContext_SDL::CL_GraphicContext_SDL(CL_DisplayWindow_Generic *window)
: window(window)
{
        cliprects.pop_back();
        cliprects.push_back(CL_Rect(0,0, window->get_width(), window->get_height()));
}

CL_GraphicContext_SDL::~CL_GraphicContext_SDL()
{
}

/////////////////////////////////////////////////////////////////////////////
// CL_GraphicContext_SDL attributes:

int CL_GraphicContext_SDL::get_width() const
{
	return window->get_width();
}

int CL_GraphicContext_SDL::get_height() const
{
	return window->get_height();
}

CL_Color CL_GraphicContext_SDL::get_pixel(int x, int y) const
{
	//Lawouach : shamelessly taken from the SDL doc
	SDL_Surface *screen = SDL_GetVideoSurface();

	if(SDL_MUSTLOCK(screen)){
		if(SDL_LockSurface(screen) < 0){
			throw CL_Error(SDL_GetError());
		}
	}

	x += static_cast<int>(get_modelview().get_origin_x());
	y += static_cast<int>(get_modelview().get_origin_y());

	int bpp = screen->format->BytesPerPixel;

	/* Here p is the address to the pixel we want to retrieve */
	Uint8 *p = (Uint8 *)screen->pixels + y * screen->pitch + x * bpp;
	Uint32 pixel;

	switch(bpp) {
	case 1:
		pixel = *p;
		break;
	case 2:
		pixel = *(Uint16 *)p;
		break;
	case 3:
		if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
			pixel = p[0] << 16 | p[1] << 8 | p[2];
		else
			pixel = p[0] | p[1] << 8 | p[2] << 16;
		break;
	case 4:
		pixel = *(Uint32 *)p;
		break;
	default:
		pixel = 0;
		break;
	}
	
	if(SDL_MUSTLOCK(screen)){
		SDL_UnlockSurface(screen);
	}

	Uint8 r,g,b,a;

	SDL_GetRGBA(pixel,screen->format,&r,&b,&g,&a);

	return CL_Color(r,g,b,a);
}

/////////////////////////////////////////////////////////////////////////////
// CL_GraphicContext_SDL operations:

void CL_GraphicContext_SDL::draw_pixel(int x, int y, const CL_Color &color)
{
	//Lawouach : shamelessly taken from the SDL doc
	SDL_Surface *screen = SDL_GetVideoSurface();

	Uint32 pixel = SDL_MapRGBA(screen->format,color.get_red(),color.get_green(),color.get_blue(),color.get_alpha());

	if(SDL_MUSTLOCK(screen))
		if(SDL_LockSurface(screen) < 0)
			throw CL_Error(SDL_GetError());

	x += static_cast<int>(get_modelview().get_origin_x());
	y += static_cast<int>(get_modelview().get_origin_y());

	int bpp = screen->format->BytesPerPixel;

	// Here p is the address to the pixel we want to set
	Uint8 *p = (Uint8 *)screen->pixels + y * screen->pitch + x * bpp;

	switch(bpp) {
	case 1:
		*p = pixel;
		break;
	case 2:
		*(Uint16 *)p = pixel;
		break; 
	case 3:
		if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
			p[0] = (pixel >> 16) & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = pixel & 0xff;
		} else {
			p[0] = pixel & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = (pixel >> 16) & 0xff;
		}
		break;
	case 4:
		*(Uint32 *)p = pixel;
		break;
	}

	if(SDL_MUSTLOCK(screen)){
		SDL_UnlockSurface(screen);
	}
}

void CL_GraphicContext_SDL::draw_line(int x1, int y1, int x2, int y2, const CL_Color &color)
{
	x1 += static_cast<int>(get_modelview().get_origin_x());
	y1 += static_cast<int>(get_modelview().get_origin_y());
	
	x2 += static_cast<int>(get_modelview().get_origin_x());
	y2 += static_cast<int>(get_modelview().get_origin_y());
	
#ifdef HAVE_LIBSDL_GFX
	lineRGBA(SDL_GetVideoSurface(), x1, y1, x2, y2, 
				color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
#else
	int w,h;
	(x2-x1) == 0 ? w = 1 : w = x2-x1;
	(y2-y1) == 0 ? h = 1 : h = y2-y1;

	SDL_Rect dest = {x1,y1,w,h};

	SDL_FillRect(SDL_GetVideoSurface(),&dest,
					 SDL_MapRGBA(SDL_GetVideoSurface()->format, 
									 color.get_red(), color.get_green(), color.get_blue(), color.get_alpha()));
#endif
}

void CL_GraphicContext_SDL::draw_rect(const CL_Rect &rect, const CL_Color &color)
{
	int x,y;
	x = static_cast<int>(rect.left + get_modelview().get_origin_x());
	y = static_cast<int>(rect.top  + get_modelview().get_origin_y());

#ifdef HAVE_LIBSDL_GFX
	rectangleRGBA(SDL_GetVideoSurface(), x, y, x + rect.get_width()-1, y + rect.get_height()-1,
					  color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
#else
	draw_line(rect.left,rect.top,rect.right - 1,rect.top,color);
	draw_line(rect.left,rect.bottom - 1,rect.right,rect.bottom - 1,color);
	draw_line(rect.left,rect.top,rect.left,rect.bottom - 1,color);
	draw_line(rect.right - 1,rect.top,rect.right - 1,rect.bottom - 1,color);
#endif
}

void CL_GraphicContext_SDL::fill_rect(const CL_Rect &rect, const CL_Color &color)
{
	int x,y;
	x = static_cast<int>(rect.left + get_modelview().get_origin_x());
	y = static_cast<int>(rect.top  + get_modelview().get_origin_y());

#ifdef HAVE_LIBSDL_GFX
	boxRGBA(SDL_GetVideoSurface(), x, y, x + rect.get_width()-1, y + rect.get_height()-1, 
			  color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
#else
	SDL_Rect dest = {x,y,rect.get_width(),rect.get_height()};
	SDL_FillRect(SDL_GetVideoSurface(),&dest,
                     SDL_MapRGBA(SDL_GetVideoSurface()->format, 
                                 color.get_red(), color.get_green(), color.get_blue(), color.get_alpha()));
#endif
}

void CL_GraphicContext_SDL::fill_rect(const CL_Rect &rect, const CL_Gradient &gradient)
{
	// todo: fill gradient rect with sdl
	CL_Log::log("debug","fill rect method with CL_Gradient is not yet implemented");
	// right now we just fill the rectangle with the top_left color and set a log message
	fill_rect(rect, CL_Color((gradient.top_left.get_red() + gradient.top_right.get_red() + gradient.bottom_left.get_red() + gradient.bottom_right.get_red())/4,
                                 (gradient.top_left.get_green() + gradient.top_right.get_green() + gradient.bottom_left.get_green() + gradient.bottom_right.get_green())/4,
                                 (gradient.top_left.get_blue() + gradient.top_right.get_blue() + gradient.bottom_left.get_blue() + gradient.bottom_right.get_blue())/4,
                                 (gradient.top_left.get_alpha() + gradient.top_right.get_alpha() + gradient.bottom_left.get_alpha() + gradient.bottom_right.get_alpha())/4));
}

void CL_GraphicContext_SDL::draw_quad(const CL_Quad &quad, const CL_Color &color)
{
	// todo: draw quad with sdl
	CL_Log::log("debug","draw quad method is not yet implemented");
	// right now we just fill the rectangle with the top_left color and set a log message
	draw_rect(quad.get_bounds(),color);
}

void CL_GraphicContext_SDL::fill_quad(const CL_Quad &quad, const CL_Gradient &gradient)
{
	// todo: fill gradient quad with sdl
	CL_Log::log("debug","fill quad method with CL_Gradient is not yet implemented");
	
	fill_rect(quad.get_bounds(),gradient);
}

void CL_GraphicContext_SDL::fill_quad(const CL_Quad &quad, const CL_Color &color)
{
// todo: fill quad with sdl
	CL_Log::log("debug","fill quad method is not yet implemented");
	
	fill_rect(quad.get_bounds(),color);
}

void CL_GraphicContext_SDL::clear(const CL_Color &color)
{
	SDL_FillRect(SDL_GetVideoSurface(),0,SDL_MapRGBA(SDL_GetVideoSurface()->format,color.get_red(),color.get_green(),color.get_blue(),color.get_alpha()));
}

/////////////////////////////////////////////////////////////////////////////
// CL_GraphicContext_SDL implementation:

void CL_GraphicContext_SDL::setup_2d()
{
}

void CL_GraphicContext_SDL::setup_3d()
{
}

void CL_GraphicContext_SDL::set_cliprect(const CL_Rect &rect)
{
	CL_GraphicContext_Generic::set_cliprect(rect);
        update_cliprect();
}

void CL_GraphicContext_SDL::push_cliprect(const CL_Rect &rect)
{
	CL_GraphicContext_Generic::push_cliprect(rect);
        update_cliprect();
}

void CL_GraphicContext_SDL::push_cliprect()
{
	CL_GraphicContext_Generic::push_cliprect();
        update_cliprect();
}

void CL_GraphicContext_SDL::pop_cliprect()
{
	CL_GraphicContext_Generic::pop_cliprect();
        update_cliprect();
}

void CL_GraphicContext_SDL::update_cliprect()
{
        CL_Rect rect = CL_GraphicContext_Generic::get_cliprect();
        if (rect.top == 0 && rect.bottom == 0 && rect.left == 0 && rect.right == 0)
        {
                SDL_SetClipRect(SDL_GetVideoSurface(),0);
        }
        else
        {
                SDL_Rect crect = {rect.left,rect.top,rect.get_width(),rect.get_height()};
                SDL_SetClipRect(SDL_GetVideoSurface(),&crect);                
	}
}

void CL_GraphicContext_SDL::modelview_changed()
{
}

void CL_GraphicContext_SDL::clear_stencil(int /*value*/)
{
}

void CL_GraphicContext_SDL::set_stencil_operation(CL_StencilOp /*pass*/, CL_StencilOp /*fail*/, CL_StencilFunc /*func*/, int /*ref*/)
{
}

void CL_GraphicContext_SDL::enable_stencil_write(bool /*enabled*/, bool /*visible*/, float /*alpha_limit*/)
{
}

void CL_GraphicContext_SDL::enable_stencil_test(bool /*enabled*/, CL_StencilFunc /*func*/, int /*ref*/)
{
}
