/**********************************************************************
 
	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.

**********************************************************************/



#include	<stdlib.h>
#include	<stdio.h>
#include	"memory_debug.h"
#include	"v.h"
#include	"machine/msequence.h"

//001218 yoshida
#include "gbview.h"
//end

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);

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

extern int loop_task_id;

void updateDib(VIMAGE *img){
	if(img->buf_32){
		memcpy(img->dib_buffer, img->buf_32, img->w*img->h*4);
	}else if(img->buf_16){
		memcpy(img->dib_buffer, img->buf_16, (((img->w+1)/2)*2)*img->h*2);
	}else{
		memcpy(img->dib_buffer, img->buf_8, (((img->w+3)/4)*4)*img->h);
	}
}

int _v_set_image(V_SET_IMAGE_S *v)
{
VERROR * err = v->err;
VOBJECT * obj = v->obj;
int x = v->x;
int y = v->y;
int w = v->w;
int h = v->h;

int size1,size2;
long * buf_32;
short * buf_16;
char * buf_8;
int i,j,minh;
int pixelbits;

HBITMAP hbm;
BITMAPINFO info;

	pixelbits = obj->header.win->d->gb2m_map.pixel_bits;
	
	if ( h != obj->vimage.h || w != obj->vimage.w )
		obj->vimage.select_flag = 0;
	size1 = obj->vimage.w*obj->vimage.h;
	size2 = w*h;
	minh = h < obj->vimage.h ? h : obj->vimage.h;
	switch ( pixelbits ) {
	case 32:
		if ( size1 < size2 )
			buf_32 = d_re_alloc(obj->vimage.buf_32,size2*
					sizeof(long));
		else	buf_32 = obj->vimage.buf_32;
		if ( w < obj->vimage.w ) {
			for ( i = 1 ; i < minh ; i ++ )
				for ( j = 0 ; j < w ; j ++ )
					buf_32[i*w+j] 
						= buf_32[i*obj->vimage.w+j];
		}
		else if ( w > obj->vimage.w ) {
			for ( i = minh-1 ; i >= 0 ; i -- )
				for ( j = obj->vimage.w-1;
						j >= 0;
						j -- )
					buf_32[i*w+j] 
						= buf_32[i*obj->vimage.w+j];
		}
		if ( size1 > size2 )
			buf_32 = d_re_alloc(buf_32,size2*sizeof(long));
		obj->vimage.buf_32 = buf_32;
		break;
	case 16:
		if ( size1 < size2 )
			buf_16 = d_re_alloc(obj->vimage.buf_16,size2*
					sizeof(long));
		else	buf_16 = obj->vimage.buf_16;
		if ( w < obj->vimage.w ) {
			for ( i = 1 ; i < minh ; i ++ )
				for ( j = 0 ; j < w ; j ++ )
					buf_16[i*w+j] 
						= buf_16[i*obj->vimage.w+j];
		}
		else if ( w > obj->vimage.w ) {
			for ( i = minh-1 ; i >= 0 ; i -- )
				for ( j = obj->vimage.w-1;
						j >= 0;
						j -- )
					buf_16[i*w+j] 
						= buf_16[i*obj->vimage.w+j];
		}
		if ( size1 > size2 )
			buf_16 = d_re_alloc(buf_16,size2*sizeof(long));
		obj->vimage.buf_16 = buf_16;
		break;
	case 8:
		if ( size1 < size2 )
			buf_8 = d_re_alloc(obj->vimage.buf_8,size2*
					sizeof(long));
		else	buf_8 = obj->vimage.buf_8;
		if ( w < obj->vimage.w ) {
			for ( i = 1 ; i < minh ; i ++ )
				for ( j = 0 ; j < w ; j ++ )
					buf_8[i*w+j] 
						= buf_8[i*obj->vimage.w+j];
		}
		else if ( w > obj->vimage.w ) {
			for ( i = minh-1 ; i >= 0 ; i -- )
				for ( j = obj->vimage.w-1;
						j >= 0;
						j -- )
					buf_8[i*w+j] 
						= buf_8[i*obj->vimage.w+j];
		}
		if ( size1 > size2 )
			buf_8 = d_re_alloc(buf_8,size2*sizeof(long));
		obj->vimage.buf_8 = buf_8;
		break;
	default:
		er_panic("v_set_image");
	}
	obj->vimage.w = w;
	obj->vimage.h = h;
	obj->vimage.x = x;
	obj->vimage.y = y;
	obj->vimage.w_border = w;
	
	info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	info.bmiHeader.biPlanes = 1;
	info.bmiHeader.biBitCount = pixelbits;
	info.bmiHeader.biCompression = BI_RGB;
	info.bmiHeader.biSizeImage = 0;
	info.bmiHeader.biXPelsPerMeter = 0;
	info.bmiHeader.biYPelsPerMeter = 0;
	info.bmiHeader.biClrUsed = 0;
	info.bmiHeader.biClrImportant = 0;
	info.bmiHeader.biWidth = w;
	info.bmiHeader.biHeight = -h;
	
	hbm = CreateDIBSection(ghBufferDC, &info, DIB_RGB_COLORS, &(obj->vimage.dib_buffer), NULL, 0);
	if (obj->vimage.ximage!=NULL) 
		DeleteObject(obj->vimage.ximage);
	obj->vimage.ximage = hbm;
	updateDib(&obj->vimage);
	err->err1 = E_OK;
	return 0;
}

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

int wait_window_update(){
extern int window_update_flag;
	window_update_flag = 1;
	sleep_task(&window_update_flag,0);
}

int
_v_redraw_image(void * arg)
{
	VIMAGE * vi;
	HDC memDC;
	BOOL bPaint=TRUE;
	HBITMAP obmp;
	RECT update_rect;

	vi = &(((VOBJECT *)arg)->vimage);

	updateDib(vi);
	
	memDC = CreateCompatibleDC(ghBufferDC);
	obmp = SelectObject(memDC, vi->ximage);
	BitBlt(ghBufferDC, vi->x, vi->x, vi->w, vi->h, memDC, 0, 0, SRCCOPY);
	SelectObject(memDC,obmp);
	DeleteDC(memDC);

	update_rect.left = vi->x;
	update_rect.top = vi->y;
	update_rect.right = vi->x + vi->w;
	update_rect.bottom = vi->y + vi->h;
	InvalidateRect(vi->_h.win->w, &update_rect, FALSE);

	if(get_tid() == loop_task_id)
		UpdateWindow(vi->_h.win->w);
	else
		wait_window_update();
	
	return 0;
}



void v_redraw_image(VOBJECT *obj){
	ms_do(_v_redraw_image, (void *)obj, "v_redraw");	
}

v_image_handler(VOBJECT * obj,int cmd)
{
	switch ( cmd ) {
	case VE_REDRAW:
		v_redraw_image(obj);
		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)
{
HBITMAP		hbm;
BITMAPINFO	info;
VOBJECT *	obj;
VWINDOW *	win;
int			x;
int			y;
int			w;
int			h;
HDC			hdc;
int			buf_alloc_size;
int			pixel_bits;
int			border;


	win = v->win;
	x = v->x;
	y = v->y;
	w = v->w;
	h = v->h;

	pixel_bits = win->d->gb2m_map.pixel_bits;
	obj = d_alloc(sizeof(VIMAGE));
	memset(obj, 0, sizeof(VIMAGE));
	obj->vimage.ximage=NULL;
	info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	info.bmiHeader.biPlanes = 1;
	info.bmiHeader.biBitCount = pixel_bits;
	info.bmiHeader.biCompression = BI_RGB;
	info.bmiHeader.biSizeImage = 0;
	info.bmiHeader.biXPelsPerMeter = 0;
	info.bmiHeader.biYPelsPerMeter = 0;
	info.bmiHeader.biClrUsed = 0;
	info.bmiHeader.biClrImportant = 0;
	info.bmiHeader.biWidth = w;
	info.bmiHeader.biHeight = -h;
	hdc = GetDC(win->w);
	hbm = CreateDIBSection( hdc,&info,DIB_RGB_COLORS, &(obj->vimage.dib_buffer), NULL, 0);

	/*
	Cannt use vimage.buf_32 direct. buf_32 is d_f_reed at interface.c
	hbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, &(obj->vimage.buf_32), NULL, 0);
	*/

	ReleaseDC(v->win->w,hdc);
	
	switch(pixel_bits){
	case 32:
		border = w;
		buf_alloc_size = border*h*4;
		obj->vimage.buf_32 = (long*)d_alloc(buf_alloc_size);
		memset(obj->vimage.buf_32, 0, buf_alloc_size);
		break;
	case 16:
		border = ((w+1)/2)*2;
		buf_alloc_size = border*h*2;
		obj->vimage.buf_16 = (short*)d_alloc(buf_alloc_size);
		memset(obj->vimage.buf_16, 0, buf_alloc_size);
		break;
	case 8:
		border = ((w+3)/4)*4;
		buf_alloc_size = border*h;		
		obj->vimage.buf_8 = (char*)d_alloc(buf_alloc_size);
		memset(obj->vimage.buf_8, 0, buf_alloc_size);
		break;
	}
	
	obj->vimage.select_flag = 0;
	obj->vimage.ximage = hbm;
	obj->vimage.w = w;
	obj->vimage.h = h;
	obj->vimage.x = x;
	obj->vimage.y = y;
	obj->vimage.w_border = border;
	obj->header.type = VT_IMAGE;
	obj->header.handler = v_image_handler;
	obj->header.win = win;
	obj->header.next = win->obj_list;
	v->win->obj_list = obj;
	v->err->err1 = E_OK;

	return (int)obj;
}

VOBJECT *v_create_image(VERROR * err,VWINDOW *win,int x,int y,int w,int h)
{
	V_CREATE_IMAGE_S v;
	v.err = err;
	v.win = win;
	v.x = x;
	v.y = y;
	v.w = w;
	v.h = h;
	return (VOBJECT *)ms_do(_v_create_image,(void*)&v, "v_create_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;
}

void
clear_line(VIMAGE * obj,VPOINT st,VPOINT end,int mode)
{
HBITMAP old;
HDC hdc,memDC;
int length;
VPOINT pt;
int w,h;

	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");

	if(mode)
	{
		MoveToEx(ghBufferDC,st.x,st.y,NULL);
		LineTo(ghBufferDC,end.x,end.y);
	}
	else
	{
		RECT rt;
		hdc = GetDC(ghWnd);
		GetClientRect(ghWnd,&rt);
		memDC = CreateCompatibleDC(hdc);
		old = SelectObject(memDC,obj->ximage);
		PatBlt(ghBufferDC,st.x,st.y,w,h,WHITENESS);
		BitBlt(ghBufferDC,st.x,st.y,w,h,memDC,st.x,st.y,SRCCOPY);
		SelectObject(memDC,old);
		DeleteDC(memDC);
		ReleaseDC(ghWnd,hdc);
	}
}

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(_v_select_box, &v, "v_select_box");

	return 0;
}

typedef struct v_part_redraw_image_s{
	VOBJECT * 	obj;
	int		x;
	int		y;
	int		w;
	int		h;
}V_PART_REDRAW_IMAGE_S;

int
_v_part_redraw_image(V_PART_REDRAW_IMAGE_S *v)
{
	VOBJECT * obj = v->obj;
	int x = v->x;
	int y = v->y;
	int w = v->w;
	int h = v->h;
	
	HDC memDC;
	BOOL bPaint=TRUE;
	HBITMAP obmp;
	
	updateDib(&obj->vimage);
	
	memDC = CreateCompatibleDC(ghBufferDC);
	obmp = SelectObject(memDC, obj->vimage.ximage);
	x = x + obj->vimage.x;
	y = y + obj->vimage.y;
	BitBlt(ghBufferDC,x,y,w,h,memDC,x,y,SRCCOPY);
	SelectObject(memDC,obmp);
	DeleteDC(memDC);
/*
	{
		HDC dc = GetDC(0);
		BitBlt(dc,x,y,w,h,memDC,x,y,SRCCOPY);
		ReleaseDC(0, dc);
	}
*/
	{
		RECT r = {x, y, x+w, y+h };
		InvalidateRect(obj->vimage._h.win->w, &r, FALSE);
	}
	return 0;
}

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

typedef struct v_scroll_image_s{
	VOBJECT * obj;
	int dx;
	int dy;
}V_SCROLL_IMAGE_S;

int _v_scroll_image(V_SCROLL_IMAGE_S *v)
{
	
int w,h,src_x,src_y,dest_x,dest_y;
VRECT r1,r2;
int dx = v->dx;
int dy = v->dx;
VOBJECT * obj = v->obj;
	if ( dx < 0 ) {
		if ( -dx >= obj->vimage.w )
			goto all;
		w = obj->vimage.w + dx;
		src_x = -dx;
		dest_x = 0;
		r1.tl.x = w;
		r1.tl.y = 0;
		r1.br.x = obj->vimage.w;
		r1.br.y = obj->vimage.h;
	}
	else {
		if ( dx >= obj->vimage.w )
			goto all;
		w = obj->vimage.w - dx;
		src_x = 0;
		dest_x = dx;
		r1.tl.x = 0;
		r1.tl.y = 0;
		r1.br.x = dx;
		r1.br.y = obj->vimage.h;
	}
	if ( dy < 0 ) {
		if ( -dy >= obj->vimage.h )
			goto all;
		h = obj->vimage.h + dy;
		src_y = -dy;
		dest_y = 0;
		r1.br.y = h;

		r2.tl.x = 0;
		r2.tl.y = h;
		r2.br.x = obj->vimage.w;
		r2.br.y = obj->vimage.h;
	}
	else {
		if ( dy >= obj->vimage.h )
			goto all;
		h = obj->vimage.h - dy;
		src_y = 0;
		dest_y = dy;

		r1.tl.y = dy;

		r2.tl.x = 0;
		r2.tl.y = 0;
		r2.br.x = obj->vimage.w;
		r2.br.y = dy;
	}
	v_part_redraw_image(obj,
		r1.tl.x,
		r1.tl.y,
		r1.br.x - r1.tl.x,
		r1.br.y - r1.tl.y);
	v_part_redraw_image(obj,
		r2.tl.x,
		r2.tl.y,
		r2.br.x - r2.tl.x,
		r2.br.y - r2.tl.y);
	return 0;
all:
	v_redraw_image(obj);
	return 0;
}

int
v_scroll_image(VOBJECT * obj,int dx,int dy)
{
	V_SCROLL_IMAGE_S v;
	v.obj = obj;
	v.dx = dx;
	v.dy = dy;
	return ms_do(_v_scroll_image, (void*)&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 = arg;
	if ( v->obj->vimage.buf_32 ) {
		v->buf = v->obj->vimage.buf_32;
		v->element_size = sizeof(long);
		v->obj->vimage.buf_32 = d_alloc(sizeof(long)*
				v->obj->vimage.w*
				v->obj->vimage.h);
	}
	else if ( v->obj->vimage.buf_16 ) {
		v->buf = v->obj->vimage.buf_16;
		v->element_size = sizeof(short);
		v->obj->vimage.buf_16 = d_alloc(sizeof(short)*
				((v->obj->vimage.w+1)/2)*2*
				v->obj->vimage.h);
	}
	else if ( v->obj->vimage.buf_8 ) {
		v->buf = v->obj->vimage.buf_8;
		v->element_size = sizeof(char);
		v->obj->vimage.buf_8 = d_alloc(sizeof(char)*
				((v->obj->vimage.w+3)/4)*4*
				v->obj->vimage.h);
	}
	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(_v_get_and_new_image, (void *)&v, "v_get_and_new_image");
    if ( element_size )
        *element_size = v.element_size;
    return v.buf;
}
