/*
    armlcd.c - LCD display emulation in an X window.
    ARMulator extensions for the ARM7100 family.
    Copyright (C) 1999  Ben Williamson

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    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.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <assert.h>
/* chy change 2002-12-3
#include <gtk/gtk.h>
*/

#include <sys/time.h>
static struct timeval scan16start;
static struct timeval scan16stop;
static int scan16sdiff;
static double scan16msdiff;
static int scan16counter=0;
static double scan16sum=0;


//#define MAX_DEPTH	4		/* bits per pixel */
//#define GREY_LEVELS	16

//#define LCD_BASE	0xC0000000
static int lcd_base;

//chy 2004-02-x the global variables
//chy 2004-03-11 this variables is in state.io...
//int skyeye_lcd_enabled;
//int skyeye_lcd_begin;
//int skyeye_lcd_limit;
/*chy change 2002-12-3*/
//#define NO_LCD
#ifdef NO_LCD
#include "armdefs.h"
void	lcd_enable(ARMul_State *state, int width, int height, int depth){};
void	lcd_disable(ARMul_State *state) {};
void	lcd_write(ARMul_State *state, ARMword addr, ARMword data){};
void	lcd_cycle(ARMul_State *state){};
void	lcd_dirty(ARMul_State *state, ARMword addr){};
void	lcd_clean(ARMul_State *state){};

#else
#include <gtk/gtk.h>
#include <gdk/gdk.h>//added by ywc
#ifdef SKYEYE_LCD_TEST
typedef unsigned long                 ARMword;
typedef unsigned long                 ARMul_State;
#else
#include "armdefs.h"
#endif //SKYEYE_LCD_TEST

//zy 2004-4-2 Add draw area 
static GtkWidget *window;
//static GtkWidget *darea;

static GtkWidget *TouchScreen;  //add by ywc
static GtkWidget *LCD;        //add by ywc
//zy 2004-4-2 Use Gdkgdb now ,so do not need GdkGc
//static GdkGC *gc[GREY_LEVELS];

//static GdkColormap *colormap;// by ywc

static GdkColormap *point_colormap;
static GdkColor point_color; // by ywc
static GdkGC *point_gc;

//int no_X = 0;
static int lcd_width, lcd_height, lcd_depth;
//zy 2004-4-2 Use Gdkgdb now ,so do not need GdkGc
//static int lcd_palette[GREY_LEVELS] = {0, 15,1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,0};
//static int lcd_palette[GREY_LEVELS] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
//static int lcd_palette[GREY_LEVELS] = {0, 2, 4, 3, 6, 7, 5, 1, 14, 13, 11, 12, 9, 8, 10, 15};

//zy 2004-4-2 use rgbbuf to restore lcd memory
//#define LCD_MAX_WIDTH 160
//#define LCD_MAX_HEIGHT 160

//#define LCD_MAX_WIDTH 320
//#define LCD_MAX_HEIGHT 240


guchar * rgbbuf;
guint32 * fbSkyeyeADDR;
//static guchar buf[320 * 240 * 3];

//test ywc 2004-04-04
//static guchar * rgbbuf;

static struct GdkRgbCmap *colormap;
//static gboolean lcd_changed;
unsigned char lcd_changed;


//by ywc 2004-07-24
extern mem_bank_t *bank_ptr(ARMword addr);

//static guchar *buf_1_2_4;
//static guchar *buf_16;

expose_event (GtkWidget *widget, GdkEventExpose *event)
{
	int i,x,y,pix,pixnum,bit;
	
	int wordnum;//for lcd_depth==16 ,  1 word contain 2 pixel
	guint32 fbdata;   // |R1,G1,B1,R0,G0,B0|

	int tribytenum;//for lcd_depth==12, 3 byte contain 2 pixel
	guchar fbdata8_0; // |G0,R0|	
	guchar fbdata8_1; // |R1,B0|	
	guchar fbdata8_2; // |B1,G1|	

	wordnum=lcd_width*lcd_height*lcd_depth/32;

	tribytenum=lcd_width*lcd_height*lcd_depth/24;

	pixnum=lcd_width*lcd_height;
	switch (lcd_depth) {
		case 1:
		case 2:
		case 4: 
			// should add code for scan the fb data to buf
			gdk_draw_indexed_image (widget->window,
						widget->style->fg_gc[GTK_STATE_NORMAL],
	                       			0 , 0,lcd_width ,lcd_height,
	                      			GDK_RGB_DITHER_NORMAL, rgbbuf,lcd_width,colormap);
			break;
                case 8:
			gdk_draw_indexed_image (widget->window,
						widget->style->fg_gc[GTK_STATE_NORMAL],
	                       			0 , 0,lcd_width ,lcd_height,
	                      			GDK_RGB_DITHER_NORMAL, rgbbuf,lcd_width,colormap);
			//gdk_draw_gray_image (widget->window,
			//			widget->style->fg_gc[GTK_STATE_NORMAL],
	                //     			0 , 0,lcd_width ,lcd_height,
	                //  			GDK_RGB_DITHER_NORMAL, rgbbuf,lcd_width);
			break;
		case 12:
			for(i=0;i<tribytenum;i++){
				fbdata8_0 =*((guchar *)fbSkyeyeADDR+i*3); 
				fbdata8_1 =*((guchar *)fbSkyeyeADDR+i*3+1); 
				fbdata8_2 =*((guchar *)fbSkyeyeADDR+i*3+2); 							
				*(rgbbuf+i*6+0)=(fbdata8_0 & 0x0f)<<4;
                                *(rgbbuf+i*6+1)=(fbdata8_0 & 0xf0);
                                *(rgbbuf+i*6+2)=(fbdata8_1 & 0x0f)<<4;
                                *(rgbbuf+i*6+3)=(fbdata8_1 & 0xf0);
                                *(rgbbuf+i*6+4)=(fbdata8_2 & 0x0f)<<4;
                                *(rgbbuf+i*6+5)=(fbdata8_2 & 0xf0);
		
			}
			
			gdk_draw_rgb_image (widget->window,
						widget->style->fg_gc[GTK_STATE_NORMAL],
	                       			0 , 0,lcd_width ,lcd_height,
	                      			GDK_RGB_DITHER_MAX, rgbbuf,lcd_width*3);
			
			break;
		case 16:
			//gettimeofday(&scan16start,NULL);
			for(i=0;i<wordnum;i++){
				fbdata=*(fbSkyeyeADDR+i);
				
				*(rgbbuf+i*6+0)=(guchar)((fbdata & 0x0000f800)>>8);
				*(rgbbuf+i*6+1)=(guchar)((fbdata & 0x000007e0)>>3);
				*(rgbbuf+i*6+2)=(guchar)((fbdata & 0x0000001f)<<3);
				*(rgbbuf+i*6+3)=(guchar)((fbdata & 0xf8000000)>>24);
				*(rgbbuf+i*6+4)=(guchar)((fbdata & 0x07e00000)>>19);
				*(rgbbuf+i*6+5)=(guchar)((fbdata & 0x001f0000)>>13);	
									
			}
			gdk_draw_rgb_image (widget->window,
						widget->style->fg_gc[GTK_STATE_NORMAL],
	                       			0 , 0,lcd_width ,lcd_height,
	                      			GDK_RGB_DITHER_MAX, rgbbuf,lcd_width*3);
	                      			//GDK_RGB_DITHER_MAX, &buf[0],lcd_width*3);
	                      			//GDK_RGB_DITHER_NONE, rgbbuf,lcd_width*3);
	                      			//GDK_RGB_DITHER_NORMAL, rgbbuf,lcd_width*3);
                        //gettimeofday(&scan16stop,NULL);
                	//scan16sdiff=scan16stop.tv_sec-scan16start.tv_sec;
	                //scan16msdiff=(scan16stop.tv_usec-scan16start.tv_usec)/(double)1000000;
        	        //scan16sum+=scan16sdiff+scan16msdiff;
                	//scan16counter++;
			//if(!(scan16counter%10)){
			//	printf("\n%d,%d",scan16start.tv_usec,scan16stop.tv_usec);
			//	printf("\ncounter=%d,sum=%f,average=%f",scan16counter,scan16sum,scan16sum/scan16counter);
			//	printf("\n");
			//}
			break;
		case 24:
			gdk_draw_rgb_image (widget->window,
						widget->style->fg_gc[GTK_STATE_NORMAL],
	                       			0 , 0,lcd_width ,lcd_height,
	                      			GDK_RGB_DITHER_NORMAL, rgbbuf,lcd_width*3);
			break;
		case 32:
			gdk_draw_rgb_32_image (widget->window,
						widget->style->fg_gc[GTK_STATE_NORMAL],
	                       			0 , 0,lcd_width ,lcd_height,
	                      			GDK_RGB_DITHER_NORMAL, rgbbuf,lcd_width*4);
                        break;
		default:
			break;
	}
	
	return TRUE;
}

static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
	/* event->x, event->y, event->button */
	return TRUE;
}

static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
	/* event->x, event->y */
	return TRUE;
}

//********** touch srceen event callback funtion by ywc ************
unsigned long Pen_buffer[8];
static void skPenEvent(int *buffer,int eventType,int stateType,int x,int y)
{
//	printf("\nSkyEye: skPenEvent():event type=%d\n(x=%d,y=%d)\n",down,x,y);
	buffer[0]=x;
	buffer[1]=y;
	buffer[2]=0;		// dx
	buffer[3]=0;		// dy
	buffer[4]=eventType;    // event from pen (DOWN,UP,CLICK,MOVE)
	buffer[5]=stateType;	// state of pen (DOWN,UP,ERROR)
        buffer[6]=1; 		// no of the event
	buffer[7]=0;		// time of the event (ms) since ts_open
}

void callback_button_press(GtkWidget *w, GdkEventButton *event)
{
	skPenEvent(Pen_buffer,0,1,event->x,event->y);
//	g_print("button pressed , Skyeye get it !!!\n");
	
}

void callback_button_release(GtkWidget *w, GdkEventButton *event)
{
	skPenEvent(Pen_buffer,1,0,event->x,event->y);
//	g_print("button released , Skyeye get it !!!\n\n");
}

void callback_motion_notify(GtkWidget *w, GdkEventMotion *event)
{ 
      //when mouse is moving, generate an skyeye pen motion event
      //should changed to "when mouse is pressed and moving"
         if(Pen_buffer[5]==1){
		skPenEvent(Pen_buffer,2,1,event->x,event->y);
         }
//		g_print("and moving , Skyeye get it !!!\n");
	
}
//***********************************************

#ifdef SKYEYE_LCD_TEST
int global_argc;
char **global_argv;
#else
extern int global_argc;
extern char **global_argv;
#endif
//zy 2004-04-02 for redraw
void init_rgbbuf()
{
// by ywc
/*     
	int x,y;
	guchar *pos;
	pos=rgbbuf;
	for(y=0;y<lcd_height;y++)
		for(x=0;x<lcd_width;x++)
		      //  *pos++=1;
		        //*pos++=0;
                 	*pos++=4;
*/
}
gint redraw(gpointer pointer)
{
	GdkRectangle update_rect;
	update_rect.x = 0;
	update_rect.y = 0;
	update_rect.width = lcd_width;
	update_rect.height = lcd_height;
	//if (lcd_changed)  {
	if (1)  {
		gtk_widget_draw((GtkWidget *)pointer,&update_rect);
		lcd_changed= FALSE;
	}
	if (pointer) {
		return TRUE;
	}
	else
		return FALSE;
}
void
lcd_cycle(ARMul_State *state)
{
	if(!skyeye_config.no_lcd){
		gtk_main_iteration_do(FALSE);
	}
}

void
lcd_enable(ARMul_State *state, int width, int height, int depth)
{

        //printf("para:width= %d, height= %d, depth= %d\n",width, height,depth);	
	//int i;
	guint32 i;
	static int once = 0;
	
	char * title;
	char mode[100];
	
	mem_bank_t *mbp;
	
	guint32 colors8b[256];

	//zy 2004-04-02

        guint32 colors1b[]={
                0x000000,0xffffff};

        guint32 colors4b[]={
                0x000000,0x000080,0x008000,0x008080,0x800000,0x800080,0x808000,0x808080,
                0xc0c0c0,0x0000ff,0x00ff00,0x00ffff,0xff0000,0xff00ff,0xffff00,0xffffff};

	//GdkColormap *colormap;
	//printf("SKYEYE: in lcd enable\n");
	/*
	if (getenv("DISPLAY") == NULL) {
		printf("SKYEYE: the environment variable DISPLAY=NULL  !!!!!\n");
		skyeye_config.no_lcd++;
		return;
	}
	*/
	if(skyeye_config.no_lcd)
		return;
		
	if (!once) {
		once++;
		gtk_init(&global_argc, &global_argv);
		gdk_rgb_init();	
	}
	
	/*		
	if (once) {
		return;
	}
		
	once++;
	gtk_init(&global_argc, &global_argv);
	gdk_rgb_init();
	*/
	lcd_width = width;
	lcd_height = height;
	lcd_depth = depth;
        //chy 2004-02-20 should change again
    	*(state->mach_io.lcd_is_enable)=1;
	*(state->mach_io.lcd_addr_end) = *(state->mach_io.lcd_addr_begin) + (width * height * depth / 8) - 1;
	 //zy 2004-4-13
        lcd_base=*(state->mach_io.lcd_addr_begin);
        
     // by ywc 2004-07-24 scan the framebuffer directly and don't use the buf
    	
	mbp = bank_ptr(lcd_base);
       	if(!mbp)
        {
               fprintf(stderr, "No bank at address 0x%x", lcd_base);
               return;
        }
        fbSkyeyeADDR =(guint32 *) &state->mem.rom[mbp - skyeye_config.mem.mem_banks][(lcd_base- mbp->addr)];
	switch (lcd_depth) {
		case 1:
		case 2:
		case 4: 
     			rgbbuf=malloc(lcd_width*lcd_height);
			break;
		case 12:
		case 16:
			rgbbuf=malloc(lcd_width*lcd_height*3);
			//rgbbuf=&buf[0];
			break;
                case 8:
		case 24:
                case 32:
        		rgbbuf = (guchar *)fbSkyeyeADDR;
			break;
		default:
			break;
	}
        printf("\nSKYEYE:  lcd_addr_begin 0x%x,lcd_addr_end 0x%x, width %d, height %d, depth %d\n",*(state->mach_io.lcd_addr_begin), *(state->mach_io.lcd_addr_end),width, height,depth);	

        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);//create top level window
	
	title="SkyEye_LCD-Screen_Touch-Screen ";
        //sprintf(mode,"%s%dx%dx%d",title,lcd_width,lcd_height,lcd_depth);
        sprintf(mode,"%dx%dx%d %s",lcd_width,lcd_height,lcd_depth,title);
        gtk_window_set_title(window, mode);//added by ywc

	gtk_widget_set_usize(window, width, height);
	
	gtk_widget_set_events(window, GDK_EXPOSURE_MASK);

	TouchScreen = gtk_event_box_new ( );	
        gtk_container_add (GTK_CONTAINER (window), TouchScreen);
	gtk_widget_set_events(GTK_OBJECT(TouchScreen), GDK_ENTER_NOTIFY_MASK
				                 | GDK_LEAVE_NOTIFY_MASK
						 | GDK_BUTTON_PRESS_MASK
						 | GDK_BUTTON_RELEASE_MASK
						 | GDK_POINTER_MOTION_MASK);
						 //| GDK_POINTER_MOTION_HINT_MASK);

	gtk_signal_connect (GTK_OBJECT(TouchScreen), "button-press-event",
                    GTK_SIGNAL_FUNC (callback_button_press), NULL);
	gtk_signal_connect (GTK_OBJECT(TouchScreen), "button-release-event",
                    GTK_SIGNAL_FUNC (callback_button_release), NULL);
	gtk_signal_connect (GTK_OBJECT(TouchScreen), "motion-notify-event",
                    GTK_SIGNAL_FUNC (callback_motion_notify), NULL);
     
	gtk_widget_show (TouchScreen);
	
	gtk_widget_realize (TouchScreen);    
        gdk_window_set_cursor (TouchScreen->window,gdk_cursor_new (GDK_HAND2)); //cursor will change!
	
	//zy 2004-4-02 Add Drawing area
	LCD = gtk_drawing_area_new ();
	gtk_container_add (GTK_CONTAINER (TouchScreen), LCD);
	
	gtk_signal_connect (GTK_OBJECT (LCD), "expose-event", GTK_SIGNAL_FUNC (expose_event),NULL );
	
        gtk_widget_show (LCD);
	
	gtk_widget_show_all (window);

	point_colormap = gdk_window_get_colormap(LCD->window);	
	
	//gtk_widget_show(window);//show the top level window
	
	// ywc 2004-07-24 creat the 256 colormap from 8 TRUE_COLOR-8-332
	// should add PSEUDOCOLOR palette for depth==8
	for( i=0; i<256; i++){	
		 colors8b[i]= ((i&0x000000e0)<<16)+((i&0x0000001c)<<11)+((i&0x00000003)<<6);

	}
	switch (lcd_depth) {
                case 1:
			colormap=gdk_rgb_cmap_new(colors1b,(1 <<depth));
			break;
                case 2:			
			break;
                case 4:
			colormap=gdk_rgb_cmap_new(colors4b,(1 <<depth));
			break;
                case 8:
			colormap=gdk_rgb_cmap_new(colors8b,(1 <<depth));
			break;
		case 12:
		case 16:
		case 24:
		case 32:
		default:
			break;
	}
	
	//init_rgbbuf();
//	gtk_timeout_add(500,redraw,window);
	gtk_timeout_add(200,redraw,window);
//	gtk_timeout_add(1000,redraw,window);
}


void
lcd_disable(ARMul_State *state)
{
	int i;
	
	//printf("SKYEYE: in lcd disable\n");
	if (skyeye_config.no_lcd)
		return;

	//chy 2004-03-11
        *(state->mach_io.lcd_is_enable)=0;
        //*(state->mach_io.lcd_addr_end)=*(state->mach_io.lcd_addr_begin);
	//zy 2004-04-02 
	//for (i = 0; i < GREY_LEVELS; i++) {
	//	if (gc[i]) {
	//		gdk_gc_destroy(gc[i]);
	//		gc[i] = NULL;
	//	}
	//}
	//ywc 2004-09-09 for free dynamic memory
	//if(lcd_depth==1||lcd_depth==2||lcd_depth==4||lcd_depth==16){
	if(lcd_depth==1||lcd_depth==2||lcd_depth==4||lcd_depth==12||lcd_depth==16){
		free(rgbbuf);	
	}
	if (window) {
		gtk_widget_destroy(window);
		window = NULL;
	}
	//zy 2004-04-02
	if (colormap) {
		gdk_rgb_cmap_free(colormap);
		colormap=NULL;
	}
}
//by ywc 2004-07-24 lcd_write will no be called in this framebuffer scanning method 
void
lcd_write(ARMul_State *state, ARMword addr, ARMword data)
{
   	ARMword offset;
	int pixnum, bit;
	guchar pix;
	guchar *pos;
	//printf("SKYEYE: in lcd write\n");
	
	ARMword swapdata=0;
	int k;
	
	if (skyeye_config.no_lcd)
		return;

	//offset = (addr & ~3) - LCD_BASE;
	offset = (addr & ~3) - lcd_base;
	pixnum = offset * 8 / lcd_depth;
	//x = pixnum % lcd_width;
	//y = pixnum / lcd_width;
	//zy 2004-04-02
	//assert(y < lcd_height);
/*	
	if (pixnum<lcd_width*lcd_height) {
		pos=rgbbuf+pixnum;
		for (bit = 0; bit < 32; bit += lcd_depth) {
			pix = (data >> bit) % (1 << lcd_depth) ;
        		*pos++=pix;
			lcd_changed=TRUE;
		}
	}
*/
	
	//ywc 2004-06-21
	if (pixnum<lcd_width*lcd_height) {
		pos=rgbbuf+pixnum;
		
		switch(lcd_depth){
			case 1:
				break;
			case 2:
				break;
			case 4:
				for(k=0;k<32;k+=4){
					if((k/4)&0x01){
					swapdata=swapdata | (((data>>(k-4))&0x0f)<<(k));
					}else{
					swapdata=swapdata | (((data>>(k+4))&0x0f)<<(k));
					}
				}
				data=swapdata;
				break;
			default:
				break;
		}
		for(bit = 0; bit < 32; bit += lcd_depth){
			pix = (data >> bit)  % (1 << lcd_depth) ;
			*pos++ = pix;
			lcd_changed = TRUE;
		}

	}
	
}


#ifdef SKYEYE_LCD_TEST
#include <unistd.h>
void main(int argc, char **argv)
{
  ARMul_State armstate;
  ARMul_State *pas;
  ARMword i,j,k;
  global_argc=argc;
  global_argv=argv;
  pas=&armstate;
  lcd_enable(pas,240,320,4);
  for(i=0;i<1000;i++){
     lcd_write(pas,i+LCD_BASE,i);
     lcd_cycle(pas);
  }
  sleep(5);
  for(i=0;i<1000;i++){
     lcd_write(pas,i+1000+LCD_BASE,i+1000);
     lcd_cycle(pas);
  }
  sleep(5);

}

#endif //SKYEYE_LCD_TEST

#endif //NO_LCD

