# generic version of a list fancy high performance multiple selection handling 	QQGSEL
# so we can use it in other lists such as contacts list etc

# temporary migration of message list specific code to genric code

function do_select(type){
	gs_select(sw.ml,type);
}

# 	//Select row by ID (includes focus etc); but not message download request
function msglist_select(mid,tick,nofocus)
{
	if(isInt(mid)){
		gs_select_bymid(sw.ml,mid,tick,nofocus);
	}else{
		gs_select_multi(sw.ml,mid);	
	}		
}


# integrate with: cm_select_contact


# generic selection handling: use c style functions with data on any JS object / DOM node rather than real the nasty JS real OO syntax

function gs_init(o,sel_prefix,root)
{
# 	// single row fast selection handling
	o.fastselect_hitcount=0;
	o.fastselect_row=0;
# 	// shift key start row 
	o.shift_sel_start=0;
# 	// Message list cursor / primary selection handling
#  	//  note these will not always match!! 
#   //  Ideally I'd only have one but can't decide which would be best for efficiency.
	o.cursor_mid=U;
	o.cursor_row=U;
#	// Selection globally unique prefix
	o.sel_prefix=sel_prefix;
	o.prefix_len=sel_prefix.length;
#   // Stop after cursor move
	o.showsoon_timer=U;
#   // Dom root note this is a list for
	o.list_root=root;	
	o.grid_rows=1;	
}
function gs_callbacks(o,msglist_rows,row_to_sel,sel_to_row,sel_to_mid,find_row,show_soon_cb)
{
	o.msglist_rows = msglist_rows;
	o.row_to_sel = row_to_sel;
	o.sel_to_row = sel_to_row;
	o.sel_to_mid = sel_to_mid;
	o.find_row = find_row;
	o.show_soon_cb = show_soon_cb;
}

function gs_find_row(o,node)
{
	return o.find_row(o,node)
}
function gs_row_to_sel(o,node)
{
	return o.row_to_sel(node)
}
function gs_list_rows(o)
{
	return o.msglist_rows(o.list_root)
}

# === utility functions
function gs_shift_start(o)
{
	return o.shift_sel_start;
}

function gs_fastselect_reset(o)
{
	o.fastselect_hitcount=0;
}
function gs_shift_start_set(o,val)
{
	o.shift_sel_start = val;
}
function gs_set_b4sel(o,val)
{	
	o.b4select=val;
}

# === DOM structural definitions
function gs_find_td(o,node)
{ 
	return ancestor_by_tag(node,'td');
}

# === SELECTION UPDATES
function gs_row_unselect(o,node)
{
	if(!node)return;
	node.sel=false;
	class_remove(node,'selected');
#	Additional checkbox handling	
	var el = o.row_to_sel(node);
	if (el) el.checked=false;
}

function gs_none_space(o)
{
	var row = o.fastselect_row;
	var el = o.row_to_sel(row);
	if (!el || el.checked) return;
	gs_row_unselect(o,row);	
}
# Applying multiple selection / unselection conditions
function gs_none_fast(o)
{
	if (o.fastselect_hitcount==1){
		o.fastselect_hitcount=0;
		var txt=o.fastselect_row.innerHTML;
		if(txt.length==0) return;
		gs_row_unselect(o,o.fastselect_row);	
	}else{
		gs_select(o,'none');
	}
}
function gs_row_select(o,node,tick)
{
	o.fastselect_hitcount++;
	o.fastselect_row=node;
	node.sel=true;
	class_add(node,'selected');
#	Additional checkbox handling	
	if(tick){
		checkbox_set(o.row_to_sel(node),true);
	}else{
		o.cursor_row=node;
	}
}

# select many in list 
function gs_select(o,type)
{
	var nodes=o.msglist_rows(o.list_root);
	if (type=='all'){
		for(var i=0; i < nodes.length; i++){
			var node = nodes[i];
			if (class_contains(node,'hidden')) continue;
			if (class_contains(node,'nonselect_row')) continue;
			gs_row_select(o,node,true);
		}
	}else if(type=='none'){
		for(var i=0; i < nodes.length; i++){
			var node = nodes[i];
			if (class_contains(node,'nonselect_row')) continue;
			gs_row_unselect(o,node);
		}
		o.fastselect_hitcount=0;
		o.space_mode=false;
	}else if(type=='new'){
		for(var i=0; i < nodes.length; i++){
			var node = nodes[i];
			if (class_contains(node,'nonselect_row')) continue;
			if (!class_contains(node,'hidden')){
				if (class_contains(node,'msg_unread'))
					gs_row_select(o,node,true);
				else
					gs_row_unselect(o,node);
 			}
		}
	}else if(type=='read'){
		for(var i=0; i < nodes.length; i++){
			var node = nodes[i];
			if (class_contains(node,'nonselect_row')) continue;
			if (!class_contains(node,'hidden'))
				if (class_contains(node,'msg_unread'))
					gs_row_unselect(o,node);
				else
					gs_row_select(o,node,true);
		}
	}else if(type=='replied'){
		for(var i=0; i < nodes.length; i++){
			var node = nodes[i];
			if (class_contains(node,'nonselect_row')) continue;
			if (!class_contains(node,'hidden'))
				if (class_contains(node,'msg_replied'))
					gs_row_select(o,node,true);
				else
					gs_row_unselect(o,node);
		}
	}else if(type=='flagged'){
		for(var i=0; i < nodes.length; i++){
			var node = nodes[i];
			if (class_contains(node,'nonselect_row')) continue;
			if (!class_contains(node,'hidden'))
				if (!class_contains(row_to_star(node),'star_sel'))
					gs_row_unselect(o,node);
				else
					gs_row_select(o,node,true);
		}
	}

	select_mailbox_all(type);
}

# Select row by ID (includes focus etc)
function gs_select_bymid(o,mid,tick,nofocus)
{
	var row;
	if(o.sel_prefix=='sel'){
		node = dge(o.sel_prefix+'_'+mid);
		if (!node) return;	
		row=o.sel_to_row(node);
		if(!row) return;
	}else{
		row=row_by_scan(o,mid);
	}
	if(!row) return;
	
#	Old selection state of the row we are about to select
#	allows quick and easy reversing of kbd based shift selections 
	gs_set_b4sel(o,row.sel);
	gs_row_select(o,row,tick);
	
#	Setup tickbox based secondary selection handling
	if(class_contains(sw.active,'fldmsg') && !nofocus || o==sw.cl || o==sw.cld_list){		// NOT GENERIC
		node.focus();
	}
	gs_shift_start_set(o,row);
	gs_cursor_init(o,mid,row);
}

# function operational but still experimental, and not fully tested
function gs_select_multi(o, multi)
{
	var row;
	if(!o || o.sel_prefix!='sel') return;
	for(var i=0; i<multi.length; i++)
	{
		var mid = multi[i];
		node = dge(o.sel_prefix+'_'+mid);
		if (!node) continue;	
		row=o.sel_to_row(node);
		if(!row) continue;
		gs_set_b4sel(o,row.sel);
		gs_row_select(o,row,true);
	}	
	
#	Setup tickbox based secondary selection handling
#	if(class_contains(sw.active,'fldmsg') && !nofocus || o==sw.cl || o==sw.cld_list){		// NOT GENERIC
#		node.focus();
#	}
#	gs_shift_start_set(o,row);
#	gs_cursor_init(o,mid,row);
}


# Complete the shift selection
function gs_complete_selection(o,row,new_state)
{
	var any=false;
	//also add last_click to here to the selection
	var increment=+1;
	var nodes=o.msglist_rows(o.list_root);
	for(var i=0; i < nodes.length; i++){
		node=nodes[i];
		any=true;
		if (node==o.shift_sel_start) increment=-1;
		if (node==row) break;
	};
	if(!any) return;
	for(; i>=0 && i < nodes.length; i+=increment){
		node=nodes[i];
		if (!class_contains(node,'hidden') && !class_contains(node,'nonselect_row')){
			if (new_state)
				gs_row_select(o,node,true);
			else
				gs_row_unselect(o,node,true);
		}
		if (node==o.shift_sel_start) break;
	}
}


function gs_cursor_row_valid(o)
{
	if(!o.cursor_row) return false;
	if(!o.cursor_row.sel) return false;
	if(o.cursor_row.childNodes.length==0) return false;
	return true;
}
function gs_cursor_init(o,mid,row)
{
	o.cursor_mid=mid;
	o.cursor_row=row;
}
function gs_cursor_mid(o)
{
	return o.cursor_mid;
}
function gs_cursor_row(o)
{
	return o.cursor_row;
}
function gs_set_cursor_mid(o,mid)
{
	o.cursor_mid=mid;
}

function gs_find_sel(o)
{
	var sel=[],node,row;
	var nodes=o.list_root.getElementsByTagName('input');
	for (var i = 0; i < nodes.length; i++) {
	  node = nodes[i];
	  row=o.sel_to_row(node);
	  if (row.sel!=true) continue;
	  if (class_contains(row,'hidden')) continue;
	  if (class_contains(row,'multi_row')){
	  	sel.multi_row=true;
	  	var extra=node.getAttribute('extra_ids').split(',');
	 	for(var i=0; i<extra.length; i++){
	 		debugger
	 		sel.push(extra[i]);
	 	}
	  }
	  sel.push(o.sel_to_mid(node));
	}
	return sel;
}


# === LIST INPUT INTEGRATION
# NEEDS: kbd keydown for arrows, dnd drag start, mousedown, click, doubleclick, variety of select all/none calls

function gs_keydown(o,e)	// cf msglist_keydown also  global_keydown
{
	var ret=true;
#	var last_mid, next_mid;
	switch (e.keyCode){
	case vkLeftArrow:
			if(o.grid_rows>1){
				ret=gs_move_by_arrow(o,e,'prev',1);
			}
			break;
	case vkUpArrow:
			ret=gs_move_by_arrow(o,e,'prev',o.grid_rows);
			break;

	case vkRightArrow:
			if(o.grid_rows>1) {
				ret=gs_move_by_arrow(o,e,'next',1);
			}
			break;
	case vkDownArrow:
			ret=gs_move_by_arrow(o,e,'next',o.grid_rows);
			break;

#	ctl A - disable default select all 
	case vkA: 
			 if (ctl_cmd_key(e)) {
				 if (e.shiftKey)
					 gs_select(o,'none');
				 else
					 gs_select(o,'all');
				return false;
			 }
			 break;
	case vkEsc:
			 gs_select(o,'none');
			 if(o.cursor_row) gs_row_select(o,o.cursor_row);
			 break;
			 
#	row by row keyboard selection
	case vkSpace:
			o.space_mode=true;
			break;			 
	}
	
	return ret;
}

function gs_move_by_arrow(o,e,type,step)	//	type='prev'|'next'
{
	 var last_mid,next_mid, ret=true;
	 if (e.ctrlKey&&(e.altKey||e.metaKey)){
		pref.message_done_action=type; //'prev';
		return ret;
	 }
	 if (e.shiftKey) {
		 if(o.cursor_row) gs_row_select(o,o.cursor_row,true);				 	 
		 o.b4select=false;
		 last_mid=o.cursor_mid;
	 }else if(o.space_mode){
		 gs_none_space(o);
	 }else{
		 gs_none_fast(o);
	 }
	 if(!o.cursor_mid) step=1;
	 next_mid=o.cursor_mid;
	 for(var i=0;i<step;i++){
		 if(type=='prev'){
			 next_mid=gs_show_prev_real(o,next_mid,false);
		 }else{
			 next_mid=gs_show_next_real(o,next_mid,false);
		 }
	 }
	 var multi=(step>1 && e.shiftKey);
	 if(multi){
	 	var sel=dge(o.sel_prefix+'_'+next_mid);
	 	var row=o.sel_to_row(sel);
 	 	gs_complete_selection(o,row,sel.checked?false:true);
 	 }
	 gs_show_soon(o,next_mid,e.shiftKey);
	 if(e.shiftKey && o.b4select && next_mid!=last_mid && !multi){
		gs_row_unselect(o,o.sel_to_row(dge(o.sel_prefix+'_'+last_mid)));	
	 }
	 ret=false;
	 return ret;
}


function gs_mousedown(o,e)
{
	var t=target(e);
	var row=o.find_row(o,t);
	if(!class_contains(row,'selected')) {
		gs_none_fast(o);
		gs_row_select(o,row,true);
	}
}


#// msg_list_click (not yet refactored...)
function gs_list_click(o,e)
{
#	// select more than one if we have to...
	var t=target(e);
	var tag=t.tagName.toLowerCase();
	var row=o.find_row(o,t);
	if(!row || class_contains(row,'nonselect_row')) return;
	var web = pref.mode=='web' && tag=="a";
	var mid;

#	// Handle the clicking of select boxes...
	if (tag=="input"){
		var sel=t;
		if (e.shiftKey && gs_shift_start(o)){
			gs_complete_selection(o,row,sel.checked?true:false)
		}else{
			if(gs_cursor_row_valid(o) && !o.row_to_sel(o.cursor_row).checked) gs_row_unselect(o,o.cursor_row);
			node=o.sel_to_row(t);
			if (sel.checked){
				gs_row_select(o,node,true);
			}else{
				gs_row_unselect(o,node);
			}
		}
		var new_cursor_mid = o.sel_to_mid(sel);
		if(new_cursor_mid!=gs_cursor_mid(o)){
			gs_set_cursor_mid(o,new_cursor_mid);
			no_msg_nopreview();		// NOT GENERIC
		}
	} else {
#		// click anywhere in a row
		var valid_sel=o.row_to_sel(row);
		var unselect;
		if (!(e.shiftKey || ctl_cmd_key(e)) || web)
			gs_none_fast(o);
		unselect=valid_sel.checked;
		if (!web){
			if (e.shiftKey && gs_shift_start(o)){
				gs_complete_selection(o,row,row.sel==true?false:true)
			}else if (ctl_cmd_key(e)){
				var c_row = gs_cursor_row(o);
				if (unselect){
					if(c_row) gs_row_unselect(o,c_row);
					gs_row_unselect(o,row);
				}else{
					if(c_row) gs_row_select(o,c_row,true);
					gs_row_select(o,row,true);
				}
				if(keys_2horiz(e)){
					gs_select(o,'none');
				}
			}
		}
		mid=o.sel_to_mid(valid_sel);
		if (!unselect){
			gs_select_bymid(o,mid,true);	// NOT GENERIC
		}
	}
	gs_shift_start_set(o,row);
}
function gs_list_dblclick(o,e)
{
	if (e.shiftKey || e.ctrlKey){
		gs_none_fast(o);
	}
}

function gs_show_soon(o,mid,tick,nofocus,nostatus)	// cf show_soon
{
	if(mid==null) {
		no_msg_sel();
		return false;
	}
	gs_select_bymid(o,mid,tick,nofocus);	
	if(o.showsoon_timer) clearTimeout(o.showsoon_timer);	

	if(!o.show_soon_cb) return;
	o.showsoon_timer=setTimeout(function(){ 
		if(o.show_soon_cb){
			o.show_soon_cb(mid,nofocus,nostatus);
		}
		o.showsoon_timer=null; 
	},150);
}




# === DOM based Prev / Next handling

function gs_find_msgs(o,limit)
{
	var sel=[];
	if(!o.list_root) o.list_root=sw.msgs_panelist;
 	var nodes=o.list_root.getElementsByTagName('tr');
	var n=nodes.length;
	if(limit && n>limit) n=limit;
	for (var i = 0; i < n; i++) {
		var row=nodes[i];
		if(!class_contains(row,'msgs_line')) continue;
		if(class_contains(row,'hidden')) continue;
		if(class_contains(row,'nonselect_row')) continue;
		sel.push(row_to_mid(row));	// o. removed!!
	}
	return sel;
}

function gs_msg_previous(o,msgs,mid)
{
	var last=null;
	for (var i=0;i<msgs.length; i++) {
	  if (msgs[i]==mid)
	  	break;
	  last=msgs[i];
	}
	if(i==msgs.length && msgs[i]!=mid)
		last=msgs[0];
	return last;
}
function gs_msg_next(o,msgs,mid)
{
	var last=null;
	for (var i=msgs.length-1;i>=0; i--) {
	  if (msgs[i]==mid)
	  	break;
	  if (class_contains(o.sel_to_row(dge(o.sel_prefix+'_'+msgs[i])),'action_pending'))
	  	continue;
	  last=msgs[i];
	}
	return last
}
function gs_msg_prev_valid(o,msgs,mid)
{
	var last=null;
	for (var i=0;i<msgs.length; i++) {
	  if (class_contains(o.sel_to_row(dge(o.sel_prefix+'_'+msgs[i])),'action_pending'))
	  	continue;
	  if (msgs[i]==mid)
	  	break;
	  last=msgs[i];
	}
	return last;
}
function gs_msg_next_valid(o,msgs,mid)
{
	var last=null;
	for (var i=msgs.length-1;i; i--) {
	  if (class_contains(o.sel_to_row(dge(o.sel_prefix+'_'+msgs[i])),'action_pending'))
	  	continue;
	  if (msgs[i]==mid)
	  	break;
	  last=msgs[i];
	}
	return last
}

function gs_show_prev_real(o,mid,do_end,eol_swap)
{
	var msgs=gs_find_msgs(o);
	var xnew=gs_msg_previous(o,msgs,mid);
	dbg("show_prev msg="+mid+" prevmsg="+xnew);
	if (xnew==null){
		if(!eol_swap) return mid;
		xnew=mid;
	}
	var row=null;//o.sel_to_row(dge(o.sel_prefix+'_'+xnew));
	if(!row) row=row_by_scan(o,xnew);
	if(class_contains(row,'action_pending')){
		if(eol_swap){
#			Preview based selection of deleted end of list item	
			xnew=gs_msg_next_valid(o,msgs,mid);
		}else if(!pref.no_pending_hide){
#			Keyboard based selection of "hidden" pending message
			xnew=gs_msg_prev_valid(o,msgs,mid);
		}
	}
	return xnew;
}

function gs_show_next_real(o,mid,do_end,eol_swap)
{
	var msgs=gs_find_msgs(o);
	var xnew=gs_msg_next(o,msgs,mid);
	dbg("show_next msg="+mid+" nextmsg="+xnew);
	if (xnew==null){
		if(!eol_swap) return mid;
		xnew=mid;
	}
	var row=null;//o.sel_to_row(dge(o.sel_prefix+'_'+xnew));
	if(!row) row=row_by_scan(o,xnew);
	if(class_contains(row,'action_pending')){
		if(eol_swap){
#			Preview based selection of deleted end of list item	
			xnew=gs_msg_prev_valid(o,msgs,mid);
		}else if(!pref.no_pending_hide){
#			Keyboard based selection of "hidden" pending message
			xnew=gs_msg_next_valid(o,msgs,mid);
		}
	}
	return xnew;
}


# === drag & drop integration

function gs_dnd_start(o, row)
{
	if(class_contains(row,'selected')) return;
	if(!ctl_cmd_key(event))
		gs_none_fast(o);
	if (event.shiftKey && gs_shift_start(o)){
		gs_complete_selection(o,row,true);
	}else{
		gs_row_select(o,row,true);		
	}
}

function row_by_scan(o,msg_id)
{
	var i;
	var rows=o.list_root.getElementsByTagName('tr');
	for(i=0;i<rows.length;i++){
		var el = rows[i];
		if(el.getAttribute('msg_id')==msg_id){
			return el;
		}
	}
	return null;
}
