#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "maqview.h"
#include "gl_gui.h"

void** vps    = NULL;
int *vp_types = NULL;
int vps_size  = 0;
static ViewPanel *last_vp = NULL;
static char acgt[4] = {'a', 'c', 'g', 't'};
static char ACGT[4] = {'A', 'C', 'G', 'T'};

void register_window(int win_id, void *obj, int win_type){
	if(vps_size < win_id){
		vps = (void**)realloc(vps, sizeof(void*) * win_id);
		vp_types = (int*)realloc(vp_types, sizeof(int) * win_id);
		memset(vp_types + vps_size, 0, sizeof(int) * (win_id - vps_size));
		memset(vps + vps_size, 0, sizeof(void*) * (win_id - vps_size));
		vps_size = win_id;
	}
	vps[win_id-1] = obj;
	vp_types[win_id-1] = win_type;
	if(win_type == WINDOW_TYPE_VIEWPANEL) last_vp = (ViewPanel*)obj;
}

ViewPanel* getViewPanel(int win_id){
	if(win_id > vps_size) return NULL;
	if(vp_types[win_id-1] != WINDOW_TYPE_VIEWPANEL) return NULL;
	return (ViewPanel*)vps[win_id-1];
}

int getMaxWindow_id(){
	return vps_size;
}

ViewPanel* getCurrentViewPanel(){
	ViewPanel *vp;
	int id = glutGetWindow();
	vp =  getViewPanel(id);
	if(vp == NULL){
		vp = last_vp;
	} else {
		last_vp = vp;
	}
	return vp;
}

void hideSubWindows(){
	int i, pid;
	pid = 0;
	for(i=0;i<vps_size;i++){
		if(vp_types[i] == WINDOW_TYPE_VIEWPANEL && vps[i]){
			glutSetWindow(((ViewPanel*)vps[i])->win_id);
			glutHideWindow();
			pid = ((ViewPanel*)vps[i])->parent_win_id;
		}
	}
	if(pid) glutSetWindow(pid);
}

void showSubWindows(){
	int i;
	for(i=0;i<vps_size;i++){
		if(vp_types[i] == WINDOW_TYPE_VIEWPANEL && vps[i]){
			glutSetWindow(((ViewPanel*)vps[i])->win_id);
			glutShowWindow();
		}
	}
}

int execute_cmd(ViewPanel *vp, char *cmd){
	int cmd_len;
	int off, next_pos;
	char argv[6][80];
	int argc;
	if(cmd == NULL) return 0;
	if(vp == NULL) return 0;
	cmd_len = strlen(cmd);
	/* Parse command */
	off = 0;
	argc = 0;
	while(off<cmd_len){
		while(off<cmd_len&&cmd[off]==' ') off++;
		next_pos = off + 1;
		while(next_pos<cmd_len&&cmd[next_pos]!=' ') next_pos ++;
		memcpy(argv[argc], cmd + off, next_pos - off);
		argv[argc][next_pos - off] = '\0';
		argc ++;
		off = next_pos;
	}
	if(argc == 1){
		if(strcmp(argv[0], "q") == 0 || strcmp(argv[0], "exit") == 0){
			removeViewPanel(vp);
			return 1;
		} else if(strcmp(argv[0], "c") == 0 || strcmp(argv[0], "clone") == 0){
			if(vp->view->map_file){
				addViewPanel(NULL, vp->view->map_file, vp->view->cns_file, vp->view->style);
				glutSetWindow(vp->win_id);
				return 1;
			}
		}
	} else if(argc == 2){
		if(strcmp(argv[0], "b") == 0 || strcmp(argv[0], "bind") == 0){
			bindViewPanel(vp, atoi(argv[1]) - 1);
			return 1;
		} else if(strcmp(argv[0], "B") == 0 || strcmp(argv[0], "unbind") == 0){
			removeObserverView(vp->owner->view, vp);
			vp->owner = NULL;
			return 1;
		} else if(strcmp(argv[0], "g") == 0 || strcmp(argv[0], "goto") == 0){
			view_goto(vp->view, vp->view->refs.show_id, str2num(argv[1]) - 1);
			glutPostRedisplay();
			return 1;
		} else if(strcmp(argv[0], "r") == 0 || strcmp(argv[0], "ref") == 0){
			if(view_locate(vp->view, argv[1], 0)){
				glutPostRedisplay();
				return 1;
			} else return 0;
		} 
	} else if(argc == 3){
		if(strcmp(argv[0], "s") == 0 || strcmp(argv[0], "set") == 0){
			if(strcmp(argv[1], "i") == 0 || strcmp(argv[1], "interval") == 0){
				off = atoi(argv[2]);
				if(off <= 0) return 0;
				vp->interval = off;
			} else if(strcmp(argv[1], "s") == 0 || strcmp(argv[1], "step") == 0){
				vp->view->step = atoi(argv[2]);
			}
		}
	}
	return 0;
}

void synchronize(void *observer, View *view, int event, int64_t last_x, int off_y){
	ViewPanel *vp;
	Read *read;
	int win_id, i;
	vp = (ViewPanel*)observer;
	win_id = glutGetWindow();
	switch(event){
		case VIEW_EVENT_GOTO:
			if(view->refs.show_id != vp->view->refs.show_id){
				view_goto(vp->view, view->refs.show_id, view->refs.show_start);
				glutSetWindow(vp->win_id);
				glutPostRedisplay();
				break;
			}
		case VIEW_EVENT_MOVE:
			if(last_x != view->refs.show_start){
				view_goto(vp->view, view->refs.show_id, vp->view->refs.show_start + view->refs.show_start - last_x);
				glutSetWindow(vp->win_id);
				glutPostRedisplay();
			}
			break;
		case VIEW_EVENT_MARK_COL:
			last_x -= vp->view->refs.show_start;
			if(last_x >=  0 && last_x <= vp->view->ch_width){
				if(off_y){
					vp->view->refs.tooltips[(int)last_x] |= 0x02;
				} else {
					vp->view->refs.tooltips[(int)last_x] &= ~0x02;
				}
				glutSetWindow(vp->win_id);
				glutPostRedisplay();
			}
			break;
		case VIEW_EVENT_MARK_REF:
			last_x -= vp->view->refs.show_start;
			if(last_x >=  0 && last_x <= vp->view->ch_width){
				if(off_y){
					vp->view->refs.tooltips[(int)last_x] |= 0x01;
				} else {
					vp->view->refs.tooltips[(int)last_x] &= ~0x01;
				}
				glutSetWindow(vp->win_id);
				glutPostRedisplay();
			}
			break;
		case VIEW_EVENT_SELECT_READ:
			view_goto(vp->view, view->refs.show_id, last_x + off_y - (int)(vp->view->ch_width) / 2);
			for(i=0;i<vp->view->refs.cache->size;i++){
				read = vp->view->refs.cache->reads + vp->view->refs.cache->offset + i;
				if(read_pos(read->read.pos) + read->read.size < last_x + off_y) continue;
				if(read_pos(read->read.pos) + read->read.size > last_x + off_y) break;
				if(read->read.dist + off_y == 0){
					read->flag |= READ_SHOWTIP;
					vp->view->layer_offset = - read->y;
					printf("offset = %d %s:%d\n", read->y, __FILE__, __LINE__);
					break;
				}
			}
			glutSetWindow(vp->win_id);
			glutPostRedisplay();
			break;
		case VIEW_EVENT_CLOSE:
			if(vp->owner && vp->owner->view == view){
				vp->owner = NULL;
			}
			break;
	}
	if(win_id) glutSetWindow(win_id);
}

void reshape(int w, int h){
	ViewPanel *vp = getCurrentViewPanel();
	vp->resize(vp, w, h);
}

void display(){
	ViewPanel *vp = getCurrentViewPanel();
	vp->paint(vp);
}

void display_bar(){
	ViewPanel *vp = getCurrentViewPanel();
	vp->paintStatusBar(vp);
}

void timer(int win_id){
	ViewPanel *vp = getViewPanel(win_id);
	if(vp->timer) vp->timer(vp, win_id);
}

void mouse(int button, int state, int x, int y){
	ViewPanel *vp = getCurrentViewPanel();
	if(state == GLUT_DOWN){
		if(vp->mousePressed) vp->mousePressed(vp, button, x, y);
	} else {
		if(vp->mouseReleased) vp->mouseReleased(vp, button, x, y);
	}
}

void motion(int x, int y){
	ViewPanel *vp = getCurrentViewPanel();
	if(vp->mouseDragged) vp->mouseDragged(vp, x, y);
}

void mouse_move(int x, int y){
	ViewPanel *vp = getCurrentViewPanel();
	if(vp->mouseMoved) vp->mouseMoved(vp, x, y);
}

void mouse_wheel(int wheel, int direction, int x, int y){
	ViewPanel *vp = getCurrentViewPanel();
	if(vp->mouseWheel) vp->mouseWheel(vp, wheel, direction, x, y);
}

void keyboard(unsigned char key, int x, int y){
	ViewPanel *vp = getCurrentViewPanel();
	if(vp->keyTyped) vp->keyTyped(vp, key, x, y);
}

void special_key(int code, int x, int y){
	ViewPanel *vp = getCurrentViewPanel();
	if(vp->specialKey) vp->specialKey(vp, code, x, y);
}

void resetMatrixs(ViewPanel *vp){
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glOrtho(0, vp->width, 0, vp->height, -vp->width, vp->width);
}

void resetPickMatrixs(ViewPanel *vp, GLdouble x, GLdouble y, float size, GLint *viewport){
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPickMatrix((GLdouble)x, (GLdouble) y, size, size, viewport);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glOrtho(0, vp->width, 0, vp->height, -vp->width, vp->width);
}

void bar_resize(int w, int h){
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glOrtho(0, w, 0, h, -w, w);
}

void default_resize(ViewPanel *vp, int w, int h){
	vp->width        = w;
	vp->height       = h - vp->status_height;
	vp->status_width = w;
	view_resize(vp->view, vp->width, vp->height);
	glutSetWindow(vp->win_id);
	//glutReshapeWindow(w, h);
	glViewport(0, 0, w, h);
	resetMatrixs(vp);
	glutPostRedisplay();
	glutSetWindow(vp->status_id);
	glutPositionWindow(0, vp->height);
	glutReshapeWindow(vp->status_width, vp->status_height);
	bar_resize(vp->status_width, vp->status_height);
	glutSetWindow(vp->win_id);
}

void default_timer(ViewPanel *vp, int win_id){
	int next_pos;
	if(vp->view->nav.state){
		glutTimerFunc(vp->interval, timer, win_id);
		next_pos = vp->view->refs.show_start + vp->view->nav.arrow_x - vp->view->nav.center_x;
		if(next_pos < 0 || next_pos >= vp->view->refs.cache->length){
		} else {
			view_goto(vp->view, vp->view->refs.show_id, next_pos);
			vp->paint(vp);
		}
	} else if(vp->view->rolling){
		next_pos = vp->view->refs.show_start + vp->view->rolling;
		if(next_pos < 0 || next_pos > vp->view->refs.cache->length){
			vp->view->rolling = 0;
		} else {
			glutTimerFunc(vp->interval, timer, win_id);
			view_goto(vp->view, vp->view->refs.show_id, next_pos);
			vp->paint(vp);
		}
	}
}

void drawString(float x, float y, char *str){
	char *c;
	glRasterPos3f(x, y, 0);
	c = str;
	for(;*c != '\0'; c++){
		if(*c == '\n' || *c == '\r'){
			y -= 15;
			glRasterPos3f(x, y, 0);
		} else {
			glutBitmapCharacter(GLUT_BITMAP_8_BY_13, *c);
		}
	}
}

/*
void drawString(float x, float y, char *str){
	char *c;
	float font_width = 8, font_height = 13, off_x;
	glPushMatrix();
	glTranslatef(x, y, 0);
	glScalef(font_width / 104.76, font_height / 152.38, 1);
	c = str;
	off_x = 0;
	for(;*c != '\0'; c++){
		if(*c == '\n' || *c == '\r'){
			glTranslatef(off_x, -152.38, 0);
			off_x = 0;
		} else {
			off_x -= 104.76;
			glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, *c);
		}
	}
	glPopMatrix();
}
*/

void drawToolTip(ViewPanel *vp, float x, float y, char *str){
	int i, size;
	View *view;
	float z;
	view = vp->view;
	z = 2;
	size = strlen(str);
	glColor3fv(view->select_color.rgb);
	glBegin(GL_POLYGON);
		glVertex3f(x, y, z);
		glVertex3f(x + size * view->font_width / 2 + 1, y + view->font_line_height - view->font_height - 2, z);
		glVertex3f(x + size * view->font_width / 2 + 1, y + view->font_line_height, z);
		glVertex3f(x - size * view->font_width / 2 - 1, y + view->font_line_height, z);
		glVertex3f(x - size * view->font_width / 2 - 1, y + view->font_line_height - view->font_height - 2, z);
	glEnd();
	glColor3fv(view->tip_color.rgb);
	z += 0.1;
	glBegin(GL_LINE_LOOP);
		glVertex3f(x, y - 5, z);
		glVertex3f(x + 3, y, z);
		glVertex3f(x + size * view->font_width / 2 + 1, y, z);
		glVertex3f(x + size * view->font_width / 2 + 1, y + view->font_line_height, z);
		glVertex3f(x - size * view->font_width / 2 - 1, y + view->font_line_height, z);
		glVertex3f(x - size * view->font_width / 2 - 1, y, z);
		glVertex3f(x - 3, y, z);
	glEnd();
	if(x - size * view->font_width / 2 < 0){
		i = ((- x + size * view->font_width / 2) / view->font_width);
		if(i * view->font_width != (- x + size * view->font_width / 2)) i++;
	} else {
		i = 0;
	}
	glRasterPos3f(x - size * view->font_width / 2 + i * view->font_width, y + view->font_line_height - view->font_height, z + 1);
	for(;i<size;i++) glutBitmapCharacter(view->font, str[i]);
}

void drawRect(float x1, float y1, float x2, float y2, float z){
	glBegin(GL_POLYGON);
		glVertex3f(x1, y1, z);
		glVertex3f(x2, y1, z);
		glVertex3f(x2, y2, z);
		glVertex3f(x1, y2, z);
	glEnd();
}

void setBaseColor(View *view, char ch, int q){
	float rgb[3], tmp;
	switch(ch){
		case 'A':
		case 'a':
			memcpy(rgb, view->base_color_table[0].rgb, 3 * sizeof(float));
			break;
		case 'T':
		case 't':
			memcpy(rgb, view->base_color_table[1].rgb, 3 * sizeof(float));
			break;
		case 'G':
		case 'g':
			memcpy(rgb, view->base_color_table[2].rgb, 3 * sizeof(float));
			break;
		case 'C':
		case 'c':
			memcpy(rgb, view->base_color_table[3].rgb, 3 * sizeof(float));
			break;
		default:
			memcpy(rgb, view->base_color_table[4].rgb, 3 * sizeof(float));
	}
	tmp = 1.0 * q / MAX_BASE_Q;
	if (tmp > 1.0) tmp = 1.0;
	rgb[0] = rgb[0] * (0.8 * tmp + 0.2);
	rgb[1] = rgb[1] * (0.8 * tmp + 0.2);
	rgb[2] = rgb[2] * (0.8 * tmp + 0.2);
	glColor3fv(rgb);
}

void drawRefSeq_ch(ViewPanel *vp, float x, float y, int size){
	int i;
	char ch, ch2;
	char str[81];
	View *view;
	RefSeq *refs;
	
	view = vp->view;
	refs = &view->refs;

	for(i=0;i<size;i++){
		if(refs->show_start + i > refs->cns->end ||
			refs->show_start + i < 0 ||
			refs->show_start + i < refs->cns->start){
			ch = view->default_ref_char;
			ch2= view->default_ref_char;
		} else {
			ch  = cns_get_ref(refs->cns->seqs[refs->cns->offset + refs->show_start + i - refs->cns->start]);
			ch2 = cns_get_cns(refs->cns->seqs[refs->cns->offset + refs->show_start + i - refs->cns->start]);
		}
		if(refs->tooltips[i] & 1){
			glColor3fv(view->select_color.rgb);
			drawRect(x + i * view->font_width, y, x + (i+1) * view->font_width, y + view->font_height, -1);
			num2str(i + refs->show_start + 1, str);
			drawToolTip(vp, x + (i + 0.5f) * view->font_width, y + view->font_height, str);
		}
		if(refs->tooltips[i] & 2){
			glColor3fv(view->select_color.rgb);
			drawRect(x + i * view->font_width, y, x + (i+1) * view->font_width, 0, -1);
		}
		glColor3fv(view->ref_color.rgb);
		glRasterPos3f(x + i * view->font_width, y, 0);
		glutBitmapCharacter(view->font, ch);
		glRasterPos3f(x + i * view->font_width, y - view->font_height, 0);
		glutBitmapCharacter(view->font, ch2);
	}
}

void drawRefSeq_sq(ViewPanel *vp, float x, float y, int size){
	int i;
	char ch, ch2;
	char str[81];
	View *view;
	RefSeq *refs;
	
	view = vp->view;
	refs = &view->refs;

	for(i=0;i<size;i++){
		if(refs->show_start + i > refs->cns->end ||
			refs->show_start + i < 0 ||
			refs->show_start + i < refs->cns->start){
			ch = view->default_ref_char;
			ch2= view->default_ref_char;
		} else {
			ch = cns_get_ref(refs->cns->seqs[refs->cns->offset + refs->show_start + i - refs->cns->start]);
			ch2= cns_get_cns(refs->cns->seqs[refs->cns->offset + refs->show_start + i - refs->cns->start]);
		}
		if(refs->tooltips[i] & 1){
			glColor3fv(view->select_color.rgb);
			drawRect(x + i * view->base_width * view->scale_x, y, x + (i+1) * view->base_width * view->scale_x,
				y + view->base_height * view->scale_y, -1);
			sprintf(str, "%ld[%c]", i + refs->show_start + 1, ch);
			drawToolTip(vp, x + (i + 0.5f) * view->base_width * view->scale_x, y + view->base_height * view->scale_y, str);
		}
		if(refs->tooltips[i] & 2){
			glColor3fv(view->select_color.rgb);
			drawRect(x + i * view->line_width * view->scale_x, y, x + (i+1) * view->line_width * view->scale_x, 0, -1);
		}
		setBaseColor(view, ch, MAX_BASE_Q);
		glRectf(x + i * view->line_width * view->scale_x + view->base_margin * view->scale_x, y, 
			x + (i + 1) * view->line_width * view->scale_x - view->base_margin * view->scale_x, y + view->base_height * view->scale_y);
		if(ch2 != ch) setBaseColor(view, ch2, MAX_BASE_Q);
		glRectf(x + i * view->line_width * view->scale_x + view->base_margin * view->scale_x, y - view->line_height * view->scale_y, 
			x + (i + 1) * view->line_width * view->scale_x - view->base_margin * view->scale_x,
				y - view->line_height * view->scale_y  + view->base_height * view->scale_y);
	}
}

void drawRefSeq_li(ViewPanel *vp, float x, float y, int size){
	int i;
	char ch, ch2;
	char str[81];
	View *view;
	RefSeq *refs;
	
	view = vp->view;
	refs = &view->refs;

	for(i=0;i<size;i++){
		if(refs->show_start + i > refs->cns->end ||
			refs->show_start + i < 0 ||
			refs->show_start + i < refs->cns->start){
			ch = view->default_ref_char;
			ch2= view->default_ref_char;
		} else {
			ch = cns_get_ref(refs->cns->seqs[refs->cns->offset + refs->show_start + i - refs->cns->start]);
			ch2= cns_get_cns(refs->cns->seqs[refs->cns->offset + refs->show_start + i - refs->cns->start]);
		}
		if(refs->tooltips[i] & 1){
			glColor3fv(view->select_color.rgb);
			drawRect(x + i * view->line_width * view->scale_x, y, x + (i+1) * view->line_width * view->scale_x,
				y + view->line_height * view->scale_y, -1);
			sprintf(str, "%ld[%c]", i + refs->show_start + 1, ch);
			drawToolTip(vp, x + (i + 0.5f) * view->line_width * view->scale_x, y + view->line_height * view->scale_y, str);
		}
		if(refs->tooltips[i] & 2){
			glColor3fv(view->select_color.rgb);
			drawRect(x + i * view->line_width * view->scale_x, y, x + (i+1) * view->line_width * view->scale_x, 0, -1);
		}
		setBaseColor(view, ch, MAX_BASE_Q);
		glRectf(x + i * view->line_width * view->scale_x, y, 
			x + (i + 1) * view->line_width * view->scale_x, y + view->line_height * view->scale_y);
		if(ch != ch2) setBaseColor(view, ch2, MAX_BASE_Q);
		glRectf(x + i * view->line_width * view->scale_x, y - view->line_height * view->scale_y, 
			x + (i + 1) * view->line_width * view->scale_x, y - view->line_height * view->scale_y + view->line_height * view->scale_y);
	}
}

void drawRefSeq(ViewPanel *vp, float x, float y, int size){
	switch(vp->view->mode){
		case VIEW_MODE_CHARACTER:
			return drawRefSeq_ch(vp, x, y, size);
		case VIEW_MODE_SQUARE:
			return drawRefSeq_sq(vp, x, y, size);
		case VIEW_MODE_LINE:
			return drawRefSeq_li(vp, x, y, size);
	}
}

int selectRefSeq_ch(ViewPanel *vp, float x, float y, int size, int id){
	int i;
	i = 0;
	for(;i<size;i++){
		glLoadName(id + i);
		glRectf(x + i * vp->view->font_width, y + vp->view->font_height, x + (i+1) * vp->view->font_width, y);
		glRectf(x + i * vp->view->font_width, y - vp->view->font_line_height + vp->view->font_height,
				x + (i+1) * vp->view->font_width, y - vp->view->font_line_height);
	}
	return i;
}

int selectRefSeq_sq(ViewPanel *vp, float x, float y, int size, int id){
	int i;
	float y2;
	View *view;
	view = vp->view;
	i = 0;
	y2 = y - view->line_height * view->scale_y;
	for(;i<size;i++){
		glLoadName(id + i);
		glRectf(x + i * view->base_width * view->scale_x, y + view->base_height * view->scale_y,
			x + (i+1) * view->base_width * view->scale_x, y);
		glRectf(x + i * view->base_width * view->scale_x, y2 + view->base_height * view->scale_y,
			x + (i+1) * view->base_width * view->scale_x, y2);
	}
	return i;
}

int selectRefSeq_li(ViewPanel *vp, float x, float y, int size, int id){
	int i;
	float y2;
	View *view;
	view = vp->view;
	i = 0;
	y2 = y - view->line_height * view->scale_y;
	for(;i<size;i++){
		glLoadName(id + i);
		glRectf(x + i * view->line_width * view->scale_x, y + view->line_height * view->scale_y,
			x + (i+1) * view->line_width * view->scale_x, y);
		glRectf(x + i * view->base_width * view->scale_x, y2 + view->base_height * view->scale_y,
			x + (i+1) * view->base_width * view->scale_x, y2);
	}
	return i;
}

int selectRefSeq(ViewPanel *vp, float x, float y, int size, int id){
	switch(vp->view->mode){
		case VIEW_MODE_CHARACTER:
			return selectRefSeq_ch(vp, x, y, size, id);
		case VIEW_MODE_SQUARE:
			return selectRefSeq_sq(vp, x, y, size, id);
		case VIEW_MODE_LINE:
			return selectRefSeq_li(vp, x, y, size, id);
	}
	return 0;
}

void drawRead_ch(ViewPanel *vp, Read *read, float x, float y){
	int i, q;
	char ch, ref;
	float r, g, b;
	View *view;
	view = vp->view;
	if((read->flag & READ_SELECT && view->selecting) || read->flag & READ_SHOWTIP){
		r = 0.7;
		g = 0.0;
		b = 0.3;
		for(i=0;i<read->read.size;i++){
			r /= 1.03;
			b *= 1.03;
			glColor3f(r, g, b);
			if(read_strand(read->read.pos))
				drawRect(x + (read->read.size - i)* view->font_width, y, 
					x + (read->read.size - i - 1) * view->font_width, y + view->font_height, -2);
			else 
				drawRect(x + i * view->font_width, y, x + (i + 1) * view->font_width, y + view->font_height, -2);
		}
	} else {
		read->flag &= ~READ_SELECT;
	}
	for(i=0;i<read->read.size;i++){
		q = read->read.seq[i] & 0x3f;
		if (read->read.seq[i] == 0) ch = 'n';
		else if (q < 27) ch = acgt[read->read.seq[i]>>6&3];
		else ch = ACGT[read->read.seq[i]>>6&3];
		if(x + i * view->font_width < 0) continue;
		if(x + i * view->font_width >= vp->width) continue;
		if(view->refs.cns->src_type == CNS_SRC_FILE){
			ref = cns_get_ref(view->refs.cns->seqs[view->refs.cns->offset + read_pos(read->read.pos) + i - view->refs.cns->start]);
		} else {
			ref = cns_get_cns(view->refs.cns->seqs[view->refs.cns->offset + read_pos(read->read.pos) + i - view->refs.cns->start]);
		}
		if((ch>='a'? ch-'a'+'A':ch) != ref){
			glColor3fv(view->mismatch_color.rgb);
		} else {
			glColor3fv(view->qual_color_table[q].rgb);
		}
		glRasterPos3f(x + i * view->font_width, y, 0);
		glutBitmapCharacter(view->font, ch);
	}
}

void drawRead_sq(ViewPanel *vp, Read *read, float x, float y){
	int i, q, h, map_qual;
	char ch;
	float r, g, b;
	View *view;
	view = vp->view;
	map_qual = (vp->global_flag & GF_IS_SINGLEQ)? read->read.seq[MAX_READLEN-1] : read->read.map_qual;
	if((view->selecting && read->flag & READ_SELECT) || (read->flag & READ_SHOWTIP)){
		r = 0.7;
		g = 0.3;
		b = 0.3;
		for(i=0;i<read->read.size;i++){
			r /= 1.03;
			g /= 1.03;
			b *= 1.03;
			glColor3f(r, g, b);
			if(read_strand(read->read.pos))
				drawRect(x + (read->read.size - i)* view->base_width * view->scale_x, y, 
					x + (read->read.size - i - 1) * view->base_width * view->scale_x , y + view->base_height * view->scale_y, -2);
			else 
				drawRect(x + i * view->base_width * view->scale_x, y, x + (i + 1) * view->base_width * view->scale_x,
					y + view->base_height * view->scale_y, -2);
		}
	} else {
		read->flag &= ~READ_SELECT;
	}
	for(i=0;i<read->read.size;i++){
		q = read->read.seq[i] & 0x3f;
		if (read->read.seq[i] == 0) ch = 'n';
		else if (q < 27) ch = acgt[read->read.seq[i]>>6&3];
		else ch = ACGT[read->read.seq[i]>>6&3];
		if(x + i * view->base_width * view->scale_x < 0) continue;
		if(x + i * view->base_width * view->scale_x >= vp->width) continue;
		setBaseColor(view, ch, q);
		h = view->base_height * view->scale_y; /* h is the height of the box */
		if (!(vp->global_flag & GF_HIDE_MAPQ)) {
			if (map_qual >= 60) h = view->base_height * view->scale_y;
			else if (map_qual >= 30) h = 3;
			else if (map_qual >= 13) h = 2;
			else h = 1;
		}
		glRectf(x + i * view->base_width * view->scale_x + view->base_margin * view->scale_x, y, 
			x + (i + 1) * view->base_width * view->scale_x - view->base_margin * view->scale_x, y + h);
	}
}

void drawRead_li(ViewPanel *vp, Read *read, float x, float y){
	View *view;
	view = vp->view;
	if((view->selecting && read->flag & READ_SELECT) || (read->flag & READ_SHOWTIP)){
		glColor3fv(view->select_color.rgb);
	} else {
		read->flag &= ~READ_SELECT;
		if(read_strand(read->read.pos))
			glColor3fv(view->line_backward_color.rgb);
		else
			glColor3fv(view->line_forward_color.rgb);
	}
	drawRect(x, y + view->line_height * view->scale_y / 3, 
		x + read->read.size * view->line_width * view->scale_x, y + view->line_height * view->scale_y * 2 / 3, 0);
	if(read->read.dist > 0){
		glBegin(GL_POLYGON);
			glVertex3f(x + (read->read.size - 1) * view->line_width * view->scale_x, y + view->line_height * view->scale_y, 0);
			glVertex3f(x + (read->read.size - 1) * view->line_width * view->scale_x, y, 0);
			glVertex3f(x + (read->read.size) * view->line_width * view->scale_x, y + view->line_height * view->scale_y / 3, 0);
			glVertex3f(x + (read->read.size) * view->line_width * view->scale_x, y + view->line_height * view->scale_y * 2 / 3, 0);
		glEnd();
	} else if(read->read.dist < 0){
		glBegin(GL_POLYGON);
			glVertex3f(x + (1) * view->line_width * view->scale_x, y + view->line_height * view->scale_y, 0);
			glVertex3f(x + (1) * view->line_width * view->scale_x, y, 0);
			glVertex3f(x, y + view->line_height * view->scale_y / 3, 0);
			glVertex3f(x, y + view->line_height * view->scale_y * 2 / 3, 0);
		glEnd();
	}
}

void drawRead(ViewPanel *vp, Read *read, float x, float y){
	switch(vp->view->mode){
		case VIEW_MODE_CHARACTER:
			return drawRead_ch(vp, read, x, y);
		case VIEW_MODE_SQUARE:
			return drawRead_sq(vp, read, x, y);
		case VIEW_MODE_LINE:
			return drawRead_li(vp, read, x, y);
	}
}

int selectRead_ch(ViewPanel *vp, Read *read, float x, float y, int id){
	int i;
	for(i=0;i<read->read.size;i++){
		glLoadName(id+i);
		glRectf(x + i * vp->view->font_width, y + vp->view->font_height, x + (i+1) * vp->view->font_width, y);
	}
	return i;
}

int selectRead_sq(ViewPanel *vp, Read *read, float x, float y, int id){
	int i;
	for(i=0;i<read->read.size;i++){
		glLoadName(id+i);
		glRectf(x + i * vp->view->base_width * vp->view->scale_x, y + vp->view->base_height * vp->view->scale_y,
			x + (i+1) * vp->view->base_width * vp->view->scale_x, y);
	}
	return i;
}

int selectRead_li(ViewPanel *vp, Read *read, float x, float y, int id){
	glLoadName(id);
	glRectf(x , y,
			x + (read->read.size) * vp->view->line_width * vp->view->scale_x, y  + vp->view->line_height * vp->view->scale_y);
	return 1;
}

int selectRead(ViewPanel *vp, Read *read, float x, float y, int id){
	switch(vp->view->mode){
		case VIEW_MODE_CHARACTER:
			return selectRead_ch(vp, read, x, y, id);
		case VIEW_MODE_SQUARE:
			return selectRead_sq(vp, read, x, y, id);
		case VIEW_MODE_LINE:
			return selectRead_li(vp, read, x, y, id);
		default:
			return 0;
	}
}

void default_paint(ViewPanel *vp){
	int i, f;
	float x, y;
	int view_layer;
	float layer_offset;
	float font_width, ch_width, line_height;
	View *view;
	Read *read;
	RefSeq *refs;

	glutSetWindow(vp->win_id);
	resetMatrixs(vp);
	view = vp->view;
	glClearColor(0, 0, 0, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	switch(view->mode){
		case VIEW_MODE_CHARACTER:
			font_width  = view->font_width;
			ch_width    = view->ch_width;
			line_height = view->font_line_height;
			break;
		case VIEW_MODE_SQUARE:
			font_width  = view->base_width * view->scale_x;
			ch_width    = view->ch_width;
			line_height = view->base_line_height * view->scale_y;
			break;
		case VIEW_MODE_LINE:
			font_width  = view->line_width * view->scale_x;
			ch_width    = view->ch_width;
			line_height = view->line_height * view->scale_y;
			break;
		default:
			return;
	}

	layer_offset = view->font_line_height + line_height * 2;

	view_layer = (vp->height - layer_offset) / (line_height);

	refs = &(view->refs);
	drawRefSeq(vp, (float)0, (vp->height  - line_height - view->font_line_height), ch_width);
	f = 0;
	if(vp->show_help){
		drawToolTip(vp, vp->width/2 - 100, (vp->height - line_height), "Reference Sequences(maybe null)");
		drawToolTip(vp, vp->width/2 + 100, (vp->height  - line_height - line_height), "Consensus Sequences");
		drawToolTip(vp, 160, vp->status_height + 5, "It is Status Bar");
		f = 0;
	}
	for(i=0;i<refs->cache->size;i++){
		read = refs->cache->reads + refs->cache->offset + i;
		if(read_pos(read->read.pos) > refs->show_start + ch_width) break;
		if(read->y + view->layer_offset < 0) continue;
		if(read->y + view->layer_offset > view_layer) continue;
		x = (read_pos(read->read.pos) - refs->show_start) * font_width;
		y = vp->height - layer_offset - ((read->y + 1 + view->layer_offset)) * line_height;
		if(read_pos(read->read.pos) + read->read.size < refs->show_start) continue;
		drawRead(vp, read, x, y);
		if(!f && x > 0){
			f = 1;
			if(vp->show_help){
				drawToolTip(vp, x + 60, y + line_height, "This is a Read");
			}
		}
	}
	glutSwapBuffers();
	for(i=0;i<vp->child_size;i++){
		if(vp->childs[i*2] && vp->childs[i*2+1]){
			((CHILD_REPAINT)vp->childs[i*2+1])(vp->childs[i*2]);
		}
	}
	vp->paintStatusBar(vp);
}

void default_paintStatusBar(ViewPanel *vp){
	char str[120];
	glutSetWindow(vp->status_id);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glColor3f(1.0, 1.0, 1.0);
	if(vp->view_number){
		num2str(vp->view_number, str);
		sprintf(vp->msg_string, "Input:[%s_]", str);
		drawString(10, 5, vp->msg_string);
	} else if(vp->string_f && vp->string_n){
		sprintf(vp->msg_string, "%s:[%s_]", vp->err_string, vp->view_string);
		drawString(10, 5, vp->msg_string);
	} else if(vp->err){
		drawString(10, 5, vp->err_string);
	} else {
		sprintf(vp->msg_string, "Ref[%s] Position[%ld-%ld] Caching[%ld-%ld] Cacheing seqs[%ld-%ld]", 
			vp->view->refs.cache->index->mm->ref_name[vp->view->refs.show_id],
			vp->view->refs.show_start + 1, 
			vp->view->refs.show_start + (int64_t)vp->view->ch_width + 1, 
			vp->view->refs.cache->start + 1,
			vp->view->refs.cache->end + 1,
			vp->view->refs.cns->start + 1,
			vp->view->refs.cns->end + 1
			);
		drawString(10, 5, vp->msg_string);
	}
	if(vp->show_help && !vp->err) vp->show_help = 0;
	glutSwapBuffers();
	glutSetWindow(vp->win_id);
}

#define BUFSIZE 24

int default_pick(ViewPanel *vp, float x, float y, float size, void (*handle)(ViewPanel *vp, SelectObject *obj, int id)){
	GLuint selectBuf[BUFSIZE];
	GLint hits, viewport[4];
	int i, j, id, count;
	int view_layer;
	float font_width, ch_width, line_height;
	float rect[4];
	View *view;
	Read *read;
	RefSeq *refs;
	SelectObject *obj, *root;

	glutSetWindow(vp->win_id);
	view = vp->view;

	switch(view->mode){
		case VIEW_MODE_CHARACTER:
			font_width  = view->font_width;
			ch_width    = view->ch_width;
			line_height = view->font_line_height;
			break;
		case VIEW_MODE_SQUARE:
			font_width  = view->base_width * view->scale_x;
			ch_width    = view->ch_width;
			line_height = view->base_line_height * view->scale_y;
			break;
		case VIEW_MODE_LINE:
			font_width  = view->line_width * view->scale_x;
			ch_width    = view->ch_width;
			line_height = view->line_height * view->scale_y;
			break;
		default:
			return 0;
	}

	root = NULL;

	glGetIntegerv(GL_VIEWPORT, viewport);
	glSelectBuffer(BUFSIZE, selectBuf);
	glRenderMode(GL_SELECT);

	glInitNames();
	glPushName(0);

	resetPickMatrixs(vp, (GLdouble)x, (GLdouble)(viewport[3] - y), size, viewport);

	rect[0] = x - size/2;
	rect[1] = (float)(vp->height - y) - size/2;
	rect[2] = x + size/2;
	rect[3] = (float)(vp->height + vp->status_height - y) + size/2;

	id = 1;

	view_layer = vp->height / (line_height) - 2;
	if(view_layer < 1) goto END;

	refs = &(view->refs);

	obj = (SelectObject*)malloc(sizeof(SelectObject));
	obj->id_start = id;
	obj->ptr = refs;
	obj->type = SELECT_TYPE_REFSEQ;
	obj->next = NULL;
	root = obj;

	count = selectRefSeq(vp, (float)0, (float)(vp->height - line_height - view->font_line_height),
		ch_width, id);
	id += count;
	obj->id_end = id;

	for(i=0;i<refs->cache->size;i++){
		read = refs->cache->reads + refs->cache->offset + i;
		if(read_pos(read->read.pos) > refs->show_start + ch_width) break;
		if(read->y + view->layer_offset < 0) continue;
		if(read->y + view->layer_offset > view_layer) continue;
		x = (read_pos(read->read.pos) - refs->show_start) * font_width;
		y = vp->height - (2 + (read->y + 1 + view->layer_offset)) * line_height - view->font_line_height;
		if(read_pos(read->read.pos) + read->read.size < refs->show_start) continue;
		if(x + read->read.size * font_width < rect[0] || x > rect[2]) continue;
		if(y + line_height  < rect[1] || y > rect[3]) continue;
		count = selectRead(vp, read, x, y, id);
		if(count > 0){
			obj->next = (SelectObject*)malloc(sizeof(SelectObject));
			obj = obj->next;
			obj->id_start = id;
			id += count;
			obj->id_end = id;
			obj->ptr = read;
			obj->type = SELECT_TYPE_READ;
			obj->next = NULL;
		}
	}

	END:
	glFlush();

	hits = glRenderMode(GL_RENDER);
	resetMatrixs(vp);
	if(hits > 0){
		count = 0;
		for(i=0;i<hits;i++){
			id = selectBuf[count + 3];
			for(j=1;j<selectBuf[count];j++){
				if(id < selectBuf[count + 3 + j]) id = selectBuf[count + 3 + j];
			}
			count += selectBuf[count] + 3;
		}
		obj = root;
		while(obj != NULL){
			if(obj->id_start <= id && obj->id_end > id){
				if(handle) handle(vp, obj, id);
				break;
			} else {
				obj = obj->next;
			}
		}
	}
	if(root){
		obj = root->next;
		free(root);
		while(obj != NULL){
			root = obj;
			obj = root->next;
			free(root);
		}
	}
	return hits;
}

int print_map(void *obj, maplet *m1){
	int j;
	maqmap_t *m;
	FILE *fpout;
	m = (maqmap_t*)obj;
	fpout = stdout;
	fprintf(fpout, "%s\t%s\t%d\t%c\t%d\t%u\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",
			m1->name, m->ref_name[m1->seqid], (m1->pos>>1) + 1,
			(m1->pos&1)? '-' : '+', m1->dist, m1->flag, m1->map_qual, (signed char)m1->seq[MAX_READLEN-1],
			m1->alt_qual, m1->info1&0xf, m1->info2, m1->c[0], m1->c[1], m1->size);
	for (j = 0; j != m1->size; ++j) {
		if (m1->seq[j] == 0) fputc('n', fpout);
		else if ((m1->seq[j]&0x3f) < 27) fputc("acgt"[m1->seq[j]>>6&3], fpout);
		else fputc("ACGT"[m1->seq[j]>>6&3], fpout);
	}
	fputc('\t', fpout);
	for (j = 0; j != m1->size; ++j)
		fputc((m1->seq[j]&0x3f) + 33, fpout);
	fputc('\n', fpout);
	fflush(fpout);
	return 1;
}


void handle_left_pick(ViewPanel *vp, SelectObject *obj, int id){
	RefSeq * refs;
	Read *read;
	switch(obj->type){
		case SELECT_TYPE_NAVIGATION:
			((Navigation*)(obj->ptr))->state = 1;
			return;
		case SELECT_TYPE_REFSEQ:
			refs = (RefSeq*)obj->ptr;
			id = id - obj->id_start;
			if(refs->tooltips[id] & 1){
				refs->tooltips[id] &= ~0x01;
				notifyView(vp->view, VIEW_EVENT_MARK_REF, refs->show_start + id, 0);
			} else {
				refs->tooltips[id] |= 0x01;
				notifyView(vp->view, VIEW_EVENT_MARK_REF, refs->show_start + id, 1);
			}
			return;
		case SELECT_TYPE_READ:
			read = (Read*)obj->ptr;
			print_map(vp->view->refs.cache->index->mm, &(read->read));
			id = id - obj->id_start;
			(read->flag & READ_SELECT)? (read->flag &= ~READ_SELECT) : (read->flag |= READ_SELECT);
			if(read->read.dist) notifyView(vp->view, VIEW_EVENT_SELECT_READ, read->read.dist>0? read_pos(read->read.pos):read_pos(read->read.pos)+read->read.size, read->read.dist);
			return;
	}
}

void handle_right_pick(ViewPanel *vp, SelectObject *obj, int id){
	RefSeq * refs;
	Read *read;
	switch(obj->type){
		case SELECT_TYPE_REFSEQ:
			refs = (RefSeq*)obj->ptr;
			id = id - obj->id_start;
			if(refs->tooltips[id] & 0x02){
				refs->tooltips[id] &= ~(0x02);
				notifyView(vp->view, VIEW_EVENT_MARK_COL, refs->show_start + id, 0);
			} else {
				refs->tooltips[id] |= 0x02;
				notifyView(vp->view, VIEW_EVENT_MARK_COL, refs->show_start + id, 1);
			}
			return;
		case SELECT_TYPE_READ:
			read = (Read*)obj->ptr;
			id = id - obj->id_start;
			(read->flag & READ_SHOWTIP)? (read->flag &= ~READ_SHOWTIP) : (read->flag |= READ_SHOWTIP);
			return;
	}
}

void handle_mouse_over(ViewPanel *vp, SelectObject *obj, int id){
	RefSeq * refs;
	Read *read;
	Navigation *nav;
	int q;
	char ch;
	switch(obj->type){
		case SELECT_TYPE_NAVIGATION:
			nav = (Navigation*)(obj->ptr);
			sprintf(vp->err_string, "Drag navigation to move left/right");
			return;
		case SELECT_TYPE_REFSEQ:
			refs = (RefSeq*)obj->ptr;
			id = id - obj->id_start;
			sprintf(vp->err_string, "Refs[%s] %ld", refs->cache->index->mm->ref_name[refs->show_id], refs->show_start + id + 1);
			return;
		case SELECT_TYPE_READ:
			read = (Read*)obj->ptr;
			id = id - obj->id_start;
			q = read->read.seq[id] & 0x3f;
			switch(vp->view->mode){
				case VIEW_MODE_CHARACTER:
				case VIEW_MODE_SQUARE:
					if (read->read.seq[id] == 0) ch = 'n';
					else if (q < 27) ch = acgt[read->read.seq[id]>>6&3];
					else ch = ACGT[read->read.seq[id]>>6&3];
					sprintf(vp->err_string, "Read: %ld|%c|%s[%d]|%c %d", read_pos(read->read.pos) + 1, 
						read_strand(read->read.pos)? '-':'+', read->read.name, id + 1, ch, q);
					return;
				case VIEW_MODE_LINE:
					sprintf(vp->err_string, "Read: %ld|%c|%s", read_pos(read->read.pos) + 1, 
						read_strand(read->read.pos)? '-':'+', read->read.name);
					return;
			}
	}
}

void default_mousePressed(ViewPanel *vp, int button, int x, int y){
	glutSetWindow(vp->win_id);
	if(button == GLUT_RIGHT_BUTTON){
		if(vp->pick && vp->handle_right_pick
			&& vp->pick(vp, x, y, 1, vp->handle_right_pick)){
		} else {	
			vp->err = 1;
			sprintf(vp->err_string, "enter '?' to get help");
			vp->show_help = 1;
		}
		glutSetCursor(GLUT_CURSOR_CROSSHAIR);
		glutPostRedisplay();
		return;
	}
	if(button == GLUT_MIDDLE_BUTTON){
		vp->view->state = 1;
		vp->view->last_x = x;
		vp->view->last_y = y;
		vp->view->old_x  = vp->view->refs.show_start;
		vp->view->old_y  = vp->view->layer_offset;
		return;
	}
	vp->view->selecting = 1;
	if(vp->pick && vp->handle_left_pick && vp->pick(vp, x, y, 1, vp->handle_left_pick)){
	} else {	
		vp->err = 1;
		sprintf(vp->err_string, "enter '?' to get help");
		vp->show_help = 1;
	}
	glutSetCursor(GLUT_CURSOR_CROSSHAIR);
	glutPostRedisplay();
}

void default_mouseReleased(ViewPanel *vp, int button, int x, int y){
	if(vp->msg) return;
	glutSetWindow(vp->win_id);
	if(button == GLUT_RIGHT_BUTTON){
		glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
		return;
	}
	if(button == GLUT_MIDDLE_BUTTON){
		vp->view->state = 0;
		vp->view->refs.show_start = vp->view->old_x;
		vp->view->layer_offset = vp->view->old_y;
		glutPostRedisplay();
		return;
	}
	glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
	vp->view->selecting = 0;
	if(vp->view->nav.state){
		vp->view->nav.arrow_x = vp->view->nav.center_x;
		vp->view->nav.arrow_y = vp->view->nav.center_y;
		vp->view->nav.state = 0;
	}
	glutPostRedisplay();
}

void default_mouseDragged(ViewPanel *vp, int x, int y){
	if(vp->view == NULL) return;
	if(vp->msg) return;
	glutSetWindow(vp->win_id);
	if(vp->view->state){
		vp->view->layer_offset = vp->view->old_y + (y - vp->view->last_y) / vp->view->font_line_height;
		vp->view->refs.show_start = vp->view->old_x - (x - vp->view->last_x) / vp->view->font_width;
		glutPostRedisplay();
	}
}

void default_mouseMoved(ViewPanel *vp, int x, int y){
	if(vp->msg) return;
	glutSetWindow(vp->win_id);
	vp->_x = x;
	vp->_y = y;
	if(vp->pick){
		if(vp->handle_mouse_over && vp->pick(vp, x, y, 1, vp->handle_mouse_over)){
			vp->err = 1;
		} else {
			vp->err = 0;
		}
		vp->paintStatusBar(vp);
	}
	glutPostRedisplay();
}

static void select_ref(void *obj, int val){
	ViewPanel *vp;
	vp = ((GList*)obj)->window.parent;
	if(val != vp->view->refs.show_id){
		view_goto(vp->view, val, 1);
		vp->paint(vp);
	}
}

void default_mouseWheel(ViewPanel *vp, int wheel, int direction, int x, int y){
	if(vp->view->mode == VIEW_MODE_CHARACTER) return;
	if(vp->msg) return;
	if(direction > 0){
		vp->view->scale_x *= 1.2;
		vp->view->scale_y *= 1.2;
	} else if(vp->view->scale_x > 0.33){
		vp->view->scale_x /= 1.2;
		vp->view->scale_y /= 1.2;
	}
	vp->view->ch_width = vp->width/(vp->view->base_width * vp->view->scale_x);
	glutPostRedisplay();
}

void default_keyTyped(ViewPanel *vp, unsigned char key, int x, int y){
	View *view;
	int cell_w, cell_h, n_disp;
	int i;
	int64_t t;
	int modifiers, multiple;
	glutSetWindow(vp->win_id);
	modifiers = glutGetModifiers();
	if(modifiers & GLUT_ACTIVE_SHIFT){
		multiple = 100;
	} else if(modifiers & GLUT_ACTIVE_CTRL){
		multiple = 1000;
	} else {
		multiple = 1;
	}
	if(vp->msg){
		if(key != 13 && key != 27) return;
		showSubWindows();
		vp->msg = 0;
		glutPostRedisplay();
		return;
	}
	if(!vp->string_f && key == '?'){
		vp->msg = 1;
		hideSubWindows();
		glutPostRedisplay();
		return;
	}
	if(vp->view == NULL) return;
	if(vp->string_f){
		if(key == 27){
			vp->string_f = 0;
			vp->string_n = 0;
			vp->view_string[0] = '\0';
			vp->err = 0;
		} else if(key == 13){
			switch(vp->command){
				case VP_CMD_OPEN:
					view = createView(vp->view_string, NULL, vp->width, vp->height, 0);
					if(view == NULL){
						vp->err = 1;
						sprintf(vp->err_string, "Cannot open view %s", vp->view_string);
					} else {
						closeView(vp->view);
						vp->view = view;
						vp->err = 1;
						sprintf(vp->err_string, "set view to %s", vp->view_string);
						vp->view_string[0] = '\0';
						vp->string_f = 0;
						vp->string_n = 0;
						goto REFRESH;
					}
					break;
				case VP_CMD_CMD:
					vp->err = 0;
					execute_cmd(vp, vp->view_string);
					break;
			}
			vp->view_string[0] = '\0';
			vp->string_f = 0;
			vp->string_n = 0;
		} else if(key == '\b'){
			if(!vp->string_n) return;
			vp->string_n --;
			vp->view_string[vp->string_n] = '\0';
		} else {
			vp->view_string[vp->string_n++] = key;
			vp->view_string[vp->string_n] = '\0';
		}
		glutPostRedisplay();
		return;
	} else if(key >= '0' && key <= '9'){
		if(key == '0' && vp->view_number == 0){
			vp->view->scale_x = 1;
			vp->view->scale_y = 1;
			if(vp->view->mode != VIEW_MODE_CHARACTER) vp->view->ch_width = vp->width / vp->view->base_width;
		} else {
			vp->view_number = vp->view_number * 10 + key - '0';
		}
		glutPostRedisplay();
		return;
	} else if(key == 13){
		if(vp->view_number){
			vp->view->rolling = 0;
			view_goto(vp->view, vp->view->refs.show_id, vp->view_number - 1);
			vp->err = 1;
			sprintf(vp->err_string, "goto: %ld", vp->view_number);
			vp->view_number = 0;
		} else {
			vp->err = 0;
			if(vp->view->rolling) vp->view->rolling = 0;
			else {
				vp->view->rolling = vp->view->step;
				if(vp->timer) vp->timer(vp, glutGetWindow());
			}
		}
		glutPostRedisplay();
		return;
	} else if(key == 2){
		if(vp->view_number){
			vp->view_number /= 10;
			glutPostRedisplay();
			return;
		}
		if(vp->view->rolling) vp->view->rolling = 0;
		else {
			vp->view->rolling = -vp->view->step;
			if(vp->timer) vp->timer(vp, glutGetWindow());
		}
		return;
	}

//	for(i=0;i<TOOLTIP_SIZE;i++) vp->view->refs.tooltips[i] &= ~(0x02);
	
	if(vp->view_number > 0){
		if(key == 27){
			vp->view_number = 0;
			glutPostRedisplay();
			return;
		} else {
			vp->view->step = vp->view_number;
		}
	}
	vp->view_number = 0;

	switch(key){
		case 'f':
			glutSetWindow(vp->parent_win_id);
			glutFullScreen();
			glutSetWindow(vp->win_id);
			break;
		case 'q':
			if (vp->global_flag & GF_HIDE_MAPQ) {
				vp->global_flag &= ~GF_HIDE_MAPQ;
				vp->err = 1;
				sprintf(vp->err_string, "hide mapping qualities");
			} else {
				vp->global_flag |= GF_HIDE_MAPQ;
				vp->err = 1;
				sprintf(vp->err_string, "show mapping qualities");
			}
			glutPostRedisplay();
			break;
		case 'e':
			vp->global_flag &= ~GF_HIDE_MAPQ;
			if (vp->global_flag & GF_IS_SINGLEQ) {
				vp->global_flag &= ~GF_IS_SINGLEQ;
				vp->err = 1;
				sprintf(vp->err_string, "visualize paired-end mapping qualities");
			} else {
				vp->global_flag |= GF_IS_SINGLEQ;
				vp->err = 1;
				sprintf(vp->err_string, "visualize single-end mapping qualities");
			}
			glutPostRedisplay();
			break;		
		case 27:
			exit(0);
		case 'o':
			vp->command = VP_CMD_OPEN;
			vp->string_f = 1;
			vp->string_n = 0;
			vp->view_string[0] = '\0';
			vp->err = 1;
			sprintf(vp->err_string, "try open new view, please input filename");
			glutPostRedisplay();
			break;
		case 'c':
			vp->command = VP_CMD_CMD;
			vp->string_f = 1;
			vp->string_n = 0;
			vp->view_string[0] = '\0';
			vp->err = 1;
			sprintf(vp->err_string, "input command");
			glutPostRedisplay();
			break;
		case '=':
		case '+':
			if(modifiers & GLUT_ACTIVE_CTRL){
				if(vp->view->map_file){
					addViewPanel(NULL, vp->view->map_file, vp->view->cns_file, vp->view->style);
					glutSetWindow(vp->win_id);
				}
				break;
			}
			if(vp->view->mode == VIEW_MODE_CHARACTER){
				vp->err = 1;
				sprintf(vp->err_string, "Cannot zoom in in character mode");
			} else {
				vp->view->scale_x *= 1.2;
				vp->view->scale_y *= 1.2;
				switch(vp->view->mode){
					case VIEW_MODE_SQUARE:
						vp->view->ch_width = vp->width/(vp->view->base_width * vp->view->scale_x);
						break;
					case VIEW_MODE_LINE:
						vp->view->ch_width = vp->width/(vp->view->line_width * vp->view->scale_x);
						break;
				}
			}
			view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start);
			glutPostRedisplay();
			break;
		case 31: removeViewPanel(vp); break; /* for Mac OS X */
		case '-':
			if(modifiers & GLUT_ACTIVE_CTRL){
				removeViewPanel(vp);
				break;
			}
			if(vp->view->mode == VIEW_MODE_CHARACTER){
				vp->err = 1;
				sprintf(vp->err_string, "Cannot zoom out in character mode");
			} else if(vp->view->scale_x > 0.33){
				vp->view->scale_x /= 1.2;
				vp->view->scale_y /= 1.2;
				switch(vp->view->mode){
					case VIEW_MODE_SQUARE:
						vp->view->ch_width = vp->width/(vp->view->base_width * vp->view->scale_x);
						break;
					case VIEW_MODE_LINE:
						vp->view->ch_width = vp->width/(vp->view->line_width * vp->view->scale_x);
						break;
				}
			}
			view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start);
			glutPostRedisplay();
			break;
		case 'r':
			if(vp->ref_menu){
				freeGList(vp->ref_menu);
				vp->ref_menu = NULL;
			} else {
				cell_w = 100;
				cell_h = 20;
				n_disp = vp->view->refs.cache->index->mm->n_ref;
				if(n_disp * cell_h > vp->height - vp->status_height){
					n_disp = (vp->height - vp->status_height) / cell_h;
				}
				vp->ref_menu = createGList(0, vp, 5, vp->height - (n_disp * cell_h), cell_w, cell_h, n_disp);
				for(i=0;i<vp->view->refs.cache->index->mm->n_ref;i++){
					GList_addOption(vp->ref_menu, vp->view->refs.cache->index->mm->ref_name[i]);
				}
				vp->ref_menu->valueChanged = select_ref;
			}
			glutPostRedisplay();
			break;
			vp->err = 1;
			sprintf(vp->err_string, "Refresh");
			REFRESH:
			vp->view->refs.cache->offset = 0;
			vp->view->refs.cache->size   = 0;
			vp->view->refs.cache->start = 0;
			vp->view->refs.cache->end   = 0;
			vp->view->refs.cns->ref_id  = -1;
			t = vp->view->refs.show_start;
			vp->view->refs.show_start = t + 1;
			view_goto(vp->view, vp->view->refs.show_id, t);
			glutPostRedisplay();
	    case 8:
	    case 'H':
	    case 'h':
			vp->err = 0;
			if (key == 8) multiple = 1000;
			else if (key == 'H') multiple = 100;
			if(vp->view->refs.show_start > 0){
				view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start - vp->view->step * multiple);
				glutPostRedisplay();
			}
			break;
	    case 12:
	    case 'L':
	    case 'l':
			vp->err = 0;
			if (key == 12) multiple = 1000;
			else if (key == 'L') multiple = 100;
			if(vp->view->refs.show_start < vp->view->refs.cache->length){
				view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start + vp->view->step * multiple);
				glutPostRedisplay();
			}
			break;
	    case 'k':
			vp->err = 1;
			vp->view->layer_offset += multiple;
			if(vp->view->layer_offset > 2) vp->view->layer_offset = 2;
			sprintf(vp->err_string, "move view offset to %d line", vp->view->layer_offset);
			glutPostRedisplay();
			break;
	    case 'j':
			vp->err = 1;
			vp->view->layer_offset -= multiple;
			sprintf(vp->err_string, "move view offset to %d line", vp->view->layer_offset);
			glutPostRedisplay();
			break;
		case ' ':
			vp->err = 0;
			if(vp->view->refs.show_start < vp->view->refs.cache->length){
				view_goto(vp->view, vp->view->refs.show_id,
					vp->view->refs.show_start + 
					vp->view->ch_width);
				glutPostRedisplay();
			}
			break;
		case 'u':
			vp->err = 0;
			if(vp->view->refs.show_start > 0){
				view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start - vp->view->ch_width);
				glutPostRedisplay();
			}
			break;
		case 'g':
			view_goto(vp->view, vp->view->refs.show_id, 0);
			vp->err = 1;
			sprintf(vp->err_string, "goto begin");
			glutPostRedisplay();
			break;
		case 'G':
			view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.cache->length);
			vp->err = 1;
			sprintf(vp->err_string, "goto end");
			glutPostRedisplay();
			break;
		case '>':
			vp->err = 0;
			t = vp->view->refs.show_start;
			while(1){
				for(i=0;i<vp->view->refs.cache->size;i++){
					if(read_pos(vp->view->refs.cache->reads[vp->view->refs.cache->offset + i].read.pos) > t){
						view_goto(vp->view, vp->view->refs.show_id, 
							read_pos(vp->view->refs.cache->reads[vp->view->refs.cache->offset + i].read.pos));
						glutPostRedisplay();
						return;
					}
				}
				if(vp->view->refs.show_start + vp->view->ch_width < vp->view->refs.cache->length
						&& view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.cache->end + 1) 
						&& vp->view->refs.cache->size){
				} else {
					vp->err = 1;
					sprintf(vp->err_string, "No more read available");
					glutPostRedisplay();
					return;
				}
			}
			break;
		case '<':
			vp->err = 0;
			t = vp->view->refs.show_start;
			while(1){
				for(i=vp->view->refs.cache->size-1;i>=0;i--){
					if(read_pos(vp->view->refs.cache->reads[vp->view->refs.cache->offset + i].read.pos) < t){
						view_goto(vp->view, vp->view->refs.show_id, 
							read_pos(vp->view->refs.cache->reads[vp->view->refs.cache->offset + i].read.pos));
						glutPostRedisplay();
						return;
					}
				}
				if(vp->view->refs.show_start > 0 && 
						view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.cache->start - vp->view->ch_width)){
				} else {
					vp->err = 1;
					sprintf(vp->err_string, "No more read available");
					glutPostRedisplay();
					return;
				}
			}
			break;
		case 'n':
			vp->err = 1;
			if(vp->view->refs.cache->index->mm->n_ref > 1){
				i = (vp->view->refs.show_id + 1) % vp->view->refs.cache->index->mm->n_ref;
				view_goto(vp->view, i, 0);
				sprintf(vp->err_string, "Switch to ref seq [%s]", vp->view->refs.cache->index->mm->ref_name[i]);
			} else {
				sprintf(vp->err_string, "Cannot switch: only one ref seq [%s]", 
					vp->view->refs.cache->index->mm->ref_name[vp->view->refs.show_id]);
			}
			glutPostRedisplay();
			break;
		case 'p':
			vp->err = 1;
			if(vp->view->refs.cache->index->mm->n_ref > 1){
				i = (vp->view->refs.show_id - 1 + vp->view->refs.cache->index->mm->n_ref) % vp->view->refs.cache->index->mm->n_ref;
				view_goto(vp->view, i, 0);
				sprintf(vp->err_string, "Switch to ref seq [%s]", vp->view->refs.cache->index->mm->ref_name[i]);
//				if(view_goto(vp->view, i, 0)){
//				} else {
//					sprintf(err_string, "Cannot switch to ref seq [%s]", vp->view->refs.index->mm->ref_name[i]);
//				}
			} else {
				sprintf(vp->err_string, "Cannot switch: only one ref seq [%s]",
					vp->view->refs.cache->index->mm->ref_name[vp->view->refs.show_id]);
			}
			glutPostRedisplay();
			break;
		default:
			vp->err = 1;
			sprintf(vp->err_string, "unknown key binding: %d[%c]", key, key);
			glutPostRedisplay();
	}
}

void default_specialKey(ViewPanel *vp, int key, int x, int y){
	if(!vp->view) return;
	glutSetWindow(vp->win_id);
	switch(key){
		case GLUT_KEY_F1:
			vp->view->mode = VIEW_MODE_CHARACTER;
			vp->view->ch_width = vp->width/vp->view->font_width;
			view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start);
			glutPostRedisplay();
			return;
		case GLUT_KEY_F2:
			vp->view->mode = VIEW_MODE_SQUARE;
			vp->view->ch_width = vp->width/(vp->view->base_width * vp->view->scale_x);
			view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start);
			glutPostRedisplay();
			return;
		case GLUT_KEY_F3:
			vp->view->mode = VIEW_MODE_LINE;
			vp->view->ch_width = vp->width/(vp->view->line_width * vp->view->scale_x);
			view_goto(vp->view, vp->view->refs.show_id, vp->view->refs.show_start);
			glutPostRedisplay();
			return;
		case GLUT_KEY_LEFT:
			return default_keyTyped(vp, 'h', x, y);
		case GLUT_KEY_RIGHT:
			return default_keyTyped(vp, 'l', x, y);
		case GLUT_KEY_UP:
			return default_keyTyped(vp, 'k', x, y);
		case GLUT_KEY_DOWN:
			return default_keyTyped(vp, 'j', x, y);
		case GLUT_KEY_PAGE_UP:
			return default_keyTyped(vp, 'u', x, y);
		case GLUT_KEY_PAGE_DOWN:
			return default_keyTyped(vp, ' ', x, y);
		case GLUT_KEY_HOME:
			return default_keyTyped(vp, 'g', x, y);
		case GLUT_KEY_END:
			return default_keyTyped(vp, 'G', x, y);
	}
}
ViewPanel* createViewPanel(int pw_id, View *view, float off_x, float off_y, float width, float height){
	int win_id;
	int status_height;
	ViewPanel *vp;
	status_height = 20;


	win_id = glutCreateSubWindow(pw_id, off_x, off_y, width, height);
	glClearColor(0, 0, 0, 0);
	glEnable(GL_DEPTH_TEST);
	vp = (ViewPanel*)malloc(sizeof(ViewPanel));
	vp->parent_win_id = pw_id;
	vp->win_id = win_id;
	vp->status_id = glutCreateSubWindow(win_id, 0, height - status_height, width, status_height);
	glClearColor(0.2, 0.2, 0.2, 0);
	glEnable(GL_DEPTH_TEST);
	glutDisplayFunc(display_bar);
	glutReshapeFunc(bar_resize);
	vp->off_x  = off_x;
	vp->off_y  = off_y;
	vp->width  = width;
	vp->height = height - status_height;
	vp->status_width  = width;
	vp->status_height = status_height;


	vp->view   = view;

	vp->view_number = 0;
	vp->string_f    = 0;
	vp->string_n    = 0;

	vp->err = 0;
	vp->msg = 0;

	vp->show_help = 0;

	vp->interval = 180;

	vp->command = 0;

	vp->_x  = 0;
	vp->_x1 = 0;
	vp->_x2 = 0;
	vp->_y  = 0;
	vp->_y1 = 0;
	vp->_y2 = 0;

	vp->global_flag = 0;

	vp->resize = (void*)default_resize;
	vp->paint  = (void*)default_paint;
	vp->pick   = (void*)default_pick;
	vp->paintStatusBar    = (void*)default_paintStatusBar;
	vp->handle_left_pick  = (void*)handle_left_pick;
	vp->handle_right_pick = (void*)handle_right_pick;
	vp->handle_mouse_over = (void*)handle_mouse_over;
	vp->mousePressed  = (void*)default_mousePressed;
	vp->mouseReleased = (void*)default_mouseReleased;
	vp->mouseDragged  = (void*)default_mouseDragged;
	vp->mouseMoved    = (void*)default_mouseMoved;
	vp->mouseWheel    = (void*)default_mouseWheel;
	vp->keyTyped      = (void*)default_keyTyped;
	vp->specialKey    = (void*)default_specialKey;
	vp->timer         = (void*)default_timer;
	vp->synchronize   = (void*)synchronize;

	vp->owner = NULL;

	vp->childs = NULL;
	vp->child_size = 0;
	vp->child_capacity = 0;

	vp->ref_menu = NULL;

	register_window(vp->win_id, vp, WINDOW_TYPE_VIEWPANEL);

	glutSetWindow(vp->win_id);
	glutReshapeFunc(reshape);
	glutDisplayFunc(display);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutPassiveMotionFunc(mouse_move);
	glutKeyboardFunc(keyboard);
	glutSpecialFunc(special_key);
#ifdef HAVE_FREEGLUT
	glutMouseWheelFunc(mouse_wheel);
#endif

	vp->resize(vp, width, height);
	view_resize(view, vp->width, vp->height);

	return vp;
}

void closeViewPanel(ViewPanel *vp){
	int i;
	closeView(vp->view);
	glutDestroyWindow(vp->status_id);
	glutDestroyWindow(vp->win_id);
	if(vps_size >= vp->win_id && vps[vp->win_id-1] == vp){
		vps[vp->win_id-1] = NULL;
	}
	for(i=0;i<vps_size;i++){
		if(vps[i] && vp_types[i] == WINDOW_TYPE_VIEWPANEL) removeObserverView(((ViewPanel*)vps[i])->view, vp);
	}
	free(vp);
}

void closeViewPanels(){
	int i;
	for(i=0;i<vps_size;i++){
		if(vps[i] && vp_types[i] == WINDOW_TYPE_VIEWPANEL){
			closeViewPanel((ViewPanel*)vps[i]);
		}
	}
}
