//
// 
//	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
// 
//	This program is free software; you can redistribute it 
//	and/or modify it under the terms of the GLOBALBASE 
//	Library General Public License (G-LGPL) as published by 
//
//	http://www.globalbase.org/
// 
//	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.
//
//

extern "C" {

#include	<stdlib.h>
#include	<stdio.h>
#include	"memory_debug.h"
#include	"v.h"
#include	"CLEMWindow.h"


void er_panic(char*);

void clear_line(VIMAGE * obj,VPOINT st,VPOINT end,int mode);
void set_gb2m_mask_and_shift(V_MAP_TBL * tbl, short color_bit);

int
_v_redraw_image(void * arg)
{
VIMAGE * vi = (VIMAGE *)arg;
Rect dest;

	vi->_h.win->w->BeginDraw();
	RGBForeColor(&RGB_black);
	RGBBackColor(&RGB_white);
	SetRect(&dest, vi->x, vi->y, vi->x+vi->w, vi->y+vi->h);
	vi->image->CopyImage(vi->_h.win->w->GetMacPort(), dest);
	vi->_h.win->w->EndDraw();

	if ( vi->select_flag ) {
	VPOINT st,end;
		st = vi->select[0];
		end = st;
		end.x = vi->select[1].x;
		clear_line(vi,st,end,1);
		st = end;
		end = vi->select[1];
		clear_line(vi,st,end,1);
		st = end;
		end.x = vi->select[0].x;
		clear_line(vi,st,end,1);
		st = end;
		end = vi->select[0];
		clear_line(vi,st,end,1);
	}
	return 0;
}

void v_redraw_image(VIMAGE * vi);

void
v_redraw_image(VIMAGE * vi)
{
	ms_do((int(*)(void*))_v_redraw_image, (void *)vi, "v_redraw_image");
}

void
v_image_handler(
	VOBJECT *	obj,
	int		cmd)
{
	switch ( cmd ) {
	case VE_REDRAW:
		v_redraw_image(&obj->vimage);
		break;
	case VE_BUTTON:
		break;
	default:
		fprintf(stderr,"v_image::unsupport cmd %i\n",cmd);
	}
}


typedef struct v_create_image_s {
	VERROR *		err;
	VOBJECT *	ret;

	VWINDOW *	win;
	int			x;
	int 			y;
	int			w;
	int			h;
} V_CREATE_IMAGE_S;

int _v_create_image(V_CREATE_IMAGE_S * v);


int
_v_create_image(V_CREATE_IMAGE_S * v)
{
VOBJECT * obj;
VWINDOW * win = v->win;
int x = v->x;
int y = v->y;
int w = v->w;
int h = v->h;
PixMapPtr	pixmap;

	obj = (VOBJECT*)d_alloc(sizeof(VIMAGE));
	obj->vimage.select_flag = 0;
	obj->vimage.buf_8 = 0;
	obj->vimage.buf_16 = 0;
	obj->vimage.buf_32 = 0;
	
	Rect rect;
	SetRect(&rect, x, y, x+w, y+h);
	win->w->FocusDraw();
	obj->vimage.image = new LGWorld(rect);
	obj->vimage.image->BeginDrawing();
	RGBBackColor(&RGB_black);
	EraseRect(&rect);
	obj->vimage.image->EndDrawing();	
	pixmap = *GetGWorldPixMap(obj->vimage.image->GetMacGWorld());
	if ( pixmap->pixelSize ) {
		obj->vimage.image->Update(rect, 16);
		obj->vimage.image->BeginDrawing();
		RGBBackColor(&RGB_black);
		EraseRect(&rect);
		obj->vimage.image->EndDrawing();
		pixmap = *GetGWorldPixMap(obj->vimage.image->GetMacGWorld());		
	}
	if ( ! LockPixels(&pixmap) )
		er_panic("_v_set_image memory ful?");
	
	switch( pixmap->pixelSize ) {
	case 16:
		obj->vimage.buf_16 = (short*)GetPixBaseAddr(&pixmap);
		obj->vimage.w_border = (pixmap->rowBytes&(0x3fff)) / sizeof(short);
		set_gb2m_mask_and_shift(&win->d->gb2m_map,16);		
		break;
	case 32:
		obj->vimage.buf_32 = (long*)GetPixBaseAddr(&pixmap);
		obj->vimage.w_border = (pixmap->rowBytes&(0x3fff)) / sizeof(long);
		set_gb2m_mask_and_shift(&win->d->gb2m_map,32);	
		break;
	default:
		er_panic("v_create_image");
	}
	
	obj->vimage.w = w;
	obj->vimage.h = h;
	obj->vimage.x = x;
	obj->vimage.y = y;
	obj->header.type = VT_IMAGE;
	obj->header.handler = (void(*)())v_image_handler;
	obj->header.win = win;
	obj->header.next = win->obj_list;
	win->obj_list = obj;

	v->err->err1 = E_OK;
	v->ret = obj;
	return 0;
}


VOBJECT *
v_create_image(VERROR * err,
	VWINDOW *	win,
	int		x,
	int 		y,
	int		w,
	int		h)
{
V_CREATE_IMAGE_S v;
	v.win = win;
	v.x = x;
	v.y = y;
	v.w = w;
	v.h = h;

	v.ret = 0;
	v.err = err;
	ms_do((int(*)(void*))_v_create_image,&v,"v_create_image");
	return v.ret;
}

typedef struct v_set_image_s {
	VERROR	*err;
	VOBJECT	*obj;
	int		x;
	int 		y;
	int		w;
	int		h;
} V_SET_IMAGE_S;

void _v_set_image(V_SET_IMAGE_S * arg);

void
_v_set_image(V_SET_IMAGE_S * arg)
{
VERROR * err = arg->err;
VOBJECT * obj = arg->obj;
int x = arg->x;
int y = arg->y;
int w = arg->w;
int h = arg->h;
int depth=0;
PixMapPtr pixmap;

	if ( h != obj->vimage.h || w != obj->vimage.w )
		obj->vimage.select_flag = 0;
	Rect new_rect;
	SetRect(&new_rect,x,y,x+w,y+h);
	UnlockPixels(GetGWorldPixMap(obj->vimage.image->GetMacGWorld()));
retry:
	obj->header.win->w->FocusDraw();
	obj->vimage.image->Update(new_rect, depth);
	obj->vimage.image->BeginDrawing();
	RGBBackColor(&RGB_black);
	EraseRect(&new_rect);
	obj->vimage.image->EndDrawing();
	pixmap = *GetGWorldPixMap(obj->vimage.image->GetMacGWorld());
	if ( ! LockPixels(&pixmap) )
		er_panic("_v_set_image memory full?");

	obj->vimage.buf_8 = 0;
	obj->vimage.buf_16 = 0;
	obj->vimage.buf_32 = 0;
	switch( pixmap->pixelSize ) {
	case 16:
		obj->vimage.buf_16 = (short*)GetPixBaseAddr(&pixmap);
		obj->vimage.w_border = (pixmap->rowBytes&(0x3fff)) >> 1;
		set_gb2m_mask_and_shift(&obj->header.win->d->gb2m_map,16);
printf("16 bit color mode\n");
		break;
	case 32:
		obj->vimage.buf_32 = (long*)GetPixBaseAddr(&pixmap);
		obj->vimage.w_border = (pixmap->rowBytes&(0x3fff)) >> 2;
		set_gb2m_mask_and_shift(&obj->header.win->d->gb2m_map,32);
printf("32 bit color mode\n");
		break;
	default:
		depth = 16;
		goto retry;
	}
	obj->vimage.w = w;
	obj->vimage.h = h;
	obj->vimage.x = x;
	obj->vimage.y = y;
	
	err->err1 = E_OK;
}

void
v_set_image(VERROR * err,
	VOBJECT *	obj,
	int		x,
	int 		y,
	int		w,
	int		h)
{
V_SET_IMAGE_S v = {err, obj, x, y, w, h};
	ms_do((int(*)(void*))_v_set_image, &v, "_v_set_image");
}

int
v_image_minrect(VRECT * r,VOBJECT * obj)
{
	r->tl.x = obj->vimage.x;
	r->tl.y = obj->vimage.y;
	r->br.x = obj->vimage.x + obj->vimage.w;
	r->br.y = obj->vimage.y + obj->vimage.h;
	return 0;
}


typedef struct v_select_box_s {
	VIMAGE * 	obj;
	int		select_flag;
	VPOINT		select[2];
} V_SELECT_BOX_S;

void
clear_line(VIMAGE * obj,VPOINT st,VPOINT end,int mode)
{
int length;
VPOINT pt;
int x,y,i;
int w,h;
long * data_32;
short * data_16;
long mask;
	if ( st.x == end.x ) {
		if ( end.y < st.y ) {
			pt = st;
			st = end;
			end = pt;
		}
		length = end.y - st.y + 1;
		w = 1;
		h = length;
	}
	else if ( st.y == end.y ) {
		if ( end.x < st.x ) {
			pt = st;
			st = end;
			end = pt;
		}
		length = end.x - st.x + 1;
		w = length;
		h = 1;
	}
	else	er_panic("clear_line");
	mask = obj->_h.win->d->gb2m_map.red_mask|
		obj->_h.win->d->gb2m_map.green_mask|
		obj->_h.win->d->gb2m_map.blue_mask;
	switch ( obj->_h.win->d->gb2m_map.pixel_bits ) {
	case 32:
		data_32 = (long*)d_alloc(sizeof(long)*length);

		i = 0;
		if ( st.x == end.x ) {
			i = 0;
			for ( y = st.y ; y <= end.y ; y ++ )
				data_32[i++] = obj->buf_32[st.x + y*obj->w];
		}
		else {
			i = 0;
			for ( x = st.x ; x <= end.x ; x ++ )
				data_32[i++] = obj->buf_32[x + st.y*obj->w];
		}
		if ( mode )
			for ( i = 0 ; i < length ; i ++ )
				data_32[i] = (~data_32[i])&mask;
		break;
	case 16:
		data_16 = (short*)d_alloc(sizeof(short)*length);

		i = 0;
		if ( st.x == end.x ) {
			i = 0;
			for ( y = st.y ; y <= end.y ; y ++ )
				data_16[i++] = obj->buf_16[st.x + y*obj->w];
		}
		else {
			i = 0;
			for ( x = st.x ; x <= end.x ; x ++ )
				data_16[i++] = obj->buf_16[x + st.y*obj->w];
		}
		if ( mode )
			for ( i = 0 ; i < length ; i ++ )
				data_16[i] = (~data_16[i])&mask;
		break;
	default:
		er_panic("clear_line");
	}

	Rect src;
	Rect dest;
	SetRect(&src, 0, 0, w, h);
	SetRect(&dest, st.x, st.y, st.x+w, st.y+h);
	RGBForeColor(&RGB_black);
	RGBBackColor(&RGB_white);
	obj->_h.win->w->BeginDraw();
	CopyBits( (BitMap *) *GetGWorldPixMap(obj->image->GetMacGWorld()),
			(BitMap *) *GetPortPixMap(obj->_h.win->w->GetMacPort()),
			&src, &dest, srcCopy, nil);
	obj->_h.win->w->EndDraw(&dest);
	if ( data_16 )
		d_f_ree(data_16);
	if ( data_32 )
		d_f_ree(data_32);
}

int _v_select_box(V_SELECT_BOX_S * v);

int
_v_select_box(V_SELECT_BOX_S * v)
{
VPOINT st,end;

	if ( v->obj->select_flag ) {
		st = end = v->obj->select[0];
		end.x = v->obj->select[1].x;
		clear_line(v->obj,st,end,0);
		st = end;
		end = v->obj->select[1];
		clear_line(v->obj,st,end,0);
		st = end;
		end.x = v->obj->select[0].x;
		clear_line(v->obj,st,end,0);
		st = end;
		end = v->obj->select[0];
		clear_line(v->obj,st,end,0);
	}
	if ( v->select_flag ) {
		v->obj->select_flag = 1;
		st = v->select[0];
		if ( st.x < v->obj->x )
			st.x = v->obj->x;
		if ( st.x >= v->obj->x + v->obj->w )
			st.x = v->obj->x + v->obj->w - 1;
		if ( st.y < v->obj->y )
			st.y = v->obj->y;
		if ( st.y >= v->obj->y + v->obj->h )
			st.y = v->obj->y + v->obj->h - 1;
		end = v->select[1];
		if ( end.x < v->obj->x )
			end.x = v->obj->x;
		if ( end.x >= v->obj->x + v->obj->w )
			end.x = v->obj->x + v->obj->w - 1;
		if ( end.y < v->obj->y )
			end.y = v->obj->y;
		if ( end.y >= v->obj->y + v->obj->h )
			end.y = v->obj->y + v->obj->h - 1;
		v->obj->select[0] = st;
		v->obj->select[1] = end;
		end = st;
		end.x = v->obj->select[1].x;
		clear_line(v->obj,st,end,1);
		st = end;
		end = v->obj->select[1];
		clear_line(v->obj,st,end,1);
		st = end;
		end.x = v->obj->select[0].x;
		clear_line(v->obj,st,end,1);
		st = end;
		end = v->obj->select[0];
		clear_line(v->obj,st,end,1);
	}
	else	v->obj->select_flag = 0;
	return 0;
}


int
v_select_box(VOBJECT * obj,VPOINT st,VPOINT end,int flag)
{
V_SELECT_BOX_S v;
	v.obj = &obj->vimage;
	v.select[0] = st;
	v.select[1] = end;
	v.select_flag = flag;
	ms_do((int(*)(void*))_v_select_box,&v,"v_select_box");
	return 0;
}

typedef struct v_part_redraw_image {
	VOBJECT * 	obj;
	int		x;
	int		y;
	int		w;
	int		h;
} V_PART_REDRAW_IMAGE;

int _v_part_redraw_image(V_PART_REDRAW_IMAGE * v);

int
_v_part_redraw_image(V_PART_REDRAW_IMAGE * v)
{
Rect src;
Rect dest;
	SetRect(&src, v->obj->vimage.x+v->x, v->obj->vimage.y + v->y,
				v->obj->vimage.x + v->x + v->w, v->obj->vimage.y + v->y + v->h);
	SetRect(&dest, v->x, v->y, v->x + v->w, v->y + v->h);
	v->obj->header.win->w->BeginDraw();
	RGBForeColor(&RGB_black);
	RGBBackColor(&RGB_white);
	CopyBits( (BitMap *) *GetGWorldPixMap(v->obj->vimage.image->GetMacGWorld()),
			(BitMap *) *GetPortPixMap(v->obj->header.win->w->GetMacPort()),
			&src, &dest, srcCopy, nil);
	v->obj->header.win->w->EndDraw(&dest);
	return 0;
}

void
v_part_redraw_image(VOBJECT * obj,int x,int y,int w,int h);

void
v_part_redraw_image(VOBJECT * obj,int x,int y,int w,int h)
{
V_PART_REDRAW_IMAGE v;
	v.obj = obj;
	v.x = x;
	v.y = y;
	v.w = w;
	v.h = h;
	ms_do((int(*)(void*))_v_part_redraw_image,&v,"v_part_redraw_image");
}

typedef struct v_scroll_image {
	VOBJECT *	obj;
	int		dx;
	int		dy;
} V_SCROLL_IMAGE;

int
_v_scroll_image(V_SCROLL_IMAGE * v);

int
_v_scroll_image(V_SCROLL_IMAGE * v)
{
int dx,dy;
Rect rect;
RgnHandle updateRgn;
VOBJECT * obj;

	dx = v->dx;
	dy = v->dy;
	obj = v->obj;
	obj->vimage.image->BeginDrawing();
	RGBBackColor(&RGB_white);
	SetRect(&rect, obj->vimage.x, obj->vimage.y,
				 obj->vimage.x + obj->vimage.w, obj->vimage.y + obj->vimage.h);
	updateRgn = NewRgn();
	ScrollRect(&rect, dx, dy, updateRgn);
	DisposeRgn(updateRgn);
	obj->vimage.image->EndDrawing();
	v_redraw_image(&obj->vimage);
	return 0;
}

void
v_scroll_image(VOBJECT * obj,int dx,int dy)
{
V_SCROLL_IMAGE v;
	v.obj = obj;
	v.dx = dx;
	v.dy = dy;
	ms_do((int(*)(void*))_v_scroll_image,&v,"v_scroll_image");
}



typedef struct v_gn {
	void *		buf;
	int		element_size;
	VOBJECT * 	obj;
} V_GN;

int
_v_get_and_new_image(void * arg)
{
V_GN * v = (V_GN*)arg;
int y = v->obj->vimage.y;
int w = v->obj->vimage.w;
int h = v->obj->vimage.h;
int wb = v->obj->vimage.w_border;
int yy;

	v = (V_GN*)arg;
	if ( v->obj->vimage.buf_32 ) {
		v->buf = d_alloc(sizeof(long) * w * h);
		v->element_size = sizeof(long);
		for ( yy = y ; yy < y+h ; yy++  )
			memcpy((long*)v->buf + yy*w, v->obj->vimage.buf_32 + yy*wb, w*sizeof(long));
	}
	else if ( v->obj->vimage.buf_16 ) {
		v->buf = d_alloc(sizeof(short) * w * h);
		v->element_size = sizeof(short);
		for ( yy = y ; yy < y+h ; yy++  )
			memcpy((short*)v->buf + yy*w, v->obj->vimage.buf_16 + yy*wb, w*sizeof(short));
	}
	else {
		er_panic("_v_get_and_new_image");
	}
	return 0;
}

void *
v_get_and_new_image(int * element_size,VOBJECT * obj)
{
V_GN v;
	v.buf = 0;
	v.element_size = 0;
	v.obj = obj;
	ms_do((int(*)(void*))_v_get_and_new_image, (void *)&v, "v_get_and_new_image");
	if ( element_size )
		*element_size = v.element_size;
	return v.buf;
}


} // extern "C"