#!/usr/bin/env python

import sys, os, cgi, re, string, types, sets #, tempfile

import cgitb; cgitb.enable(display=0, logdir='/tmp')

from cgitools import *
#home_dir = '/home/xxia/public_html' # user data will based on this dir
#users_relative_dir = "users"
#users_dir = os.path.join(home_dir, users_relative_dir)
#R_code_dir = 'R_code' # under cgi-bin

# Retrieve data from user
form = cgi.FieldStorage()

monoVars = {'search_type':1, 'user_equ':1, 'dbname':1, 'obj_tb':1, 'fixcols':1, 'return_value':1}
requiredVars = {} # these key names should be offered at least once, values are used for error msg

op_dict = {'=':'%s.%s = %s', '>':'%s.%s > %s', '<':'%s.%s < %s', '>=':'%s.%s >= %s', '<=':'%s.%s <= %s', '!=':'%s.%s != %s',
	'equal to':'%s.%s = "%s"', 'start with':'%s.%s like "%s%%"', 'end with':'%s.%s like "%%%s"', 'contain':'%s.%s like "%%%s%%"',
	'is':'%s.%s = "%s"', 'before':'%s.%s < "%s"', 'after':'%s.%s > "%s"', 'no later than':'%s.%s <= "%s"', 'no earlier than':'%s.%s >= "%s"'
	}

pages = {'project':'dbs_browse_prj.pih', 'array':'dbs_browse_array.pih', 'sample':'dbs_browse_samp.pih', 'protocol':'dbs_browse_prot.pih', 'platform':'dbs_browse_pf.pih', 'probe':'dbs_browse_probe.pih'}

# put all data into a dict
page_values = {}
for k in form.keys():
	v = form.getvalue(k) #form[k] 
	if type(v) == types.ListType or monoVars.has_key(k): page_values[k] = v
	else: page_values[k] = [v.strip()]
obj_tb = page_values['obj_tb']

tbs_used = {'array':['project', 'users', 'platform'], 'sample':['project', 'users'], 'platform':['project', 'users'], 'project':['users'], 'protocol':['project', 'users'], 'probe':['project', 'platform']}

tbalias_dict = {'array':'a', 'project':'p', 'platform':'pf', 'protocol':'pr', 'sample':'s', 'dyncol':'d', 'dyncoldef':'dcf', 'keyword':'k', 'probe':'pb', 'dbkw':'dk', 'dbxref':'dx', 'sampxref':'sx'}
tbs_connections = {
	'array':{
		'sample':{
			'related_tbs':['sampxref sxs'], 
			'cond_str':'(s.id=sxs.sample_id AND sxs.array_id=a.id)'},
		'protocol':{
			'related_tbs':['sampxref sxp'],
			'cond_str':'(a.prot_hyb_id=pr.id OR a.prot_img_id=pr.id OR a.prot_data_id=pr.id OR (sxp.array_id=a.id AND (sxp.prot_proc_id=pr.id OR sxp.prot_tech_id=pr.id OR sxp.prot_label_id=pr.id)) )'},
		'project':{'cond_str':'a.project_id=p.id'},
		'platform':{'cond_str':'a.platform_id=pf.id'},
		'probe':{'cond_str':'pb.platform_id=a.platform_id'}
		}, 
	'project':{
		'sample':{'cond_str':'s.project_id=p.id'},
		'protocol':{'cond_str':'pr.project_id=p.id'},
		'array':{'cond_str':'a.project_id=p.id'},
		'platform':{'cond_str':'pf.project_id=p.id'},
		'probe':{
			'related_tbs':['platform pfpb'],
			'cond_str':'(pfpb.project_id=p.id AND pfpb.id=pb.platform_id)'},
		},
	'platform':{
		'project':{'cond_str':'p.id=pf.project_id'},
		'array':{'cond_str':'a.platform_id=pf.id'},
		'sample':{
			'related_tbs':['array asm', 'sampxref sxpf'],
			'cond_str':'(pf.id=asm.platform_id AND asm.id=sxpf.array_id AND s.id=sxpf.sample_id)'},
		'protocol':{
			'related_tbs':['array apr', 'sampxref sxpr'],
			'cond_str':'(pf.id=apr.platform_id AND (apr.prot_hyb_id=pr.id OR apr.prot_img_id=pr.id OR apr.prot_data_id=pr.id OR (sxpr.array_id=apr.id AND (sxpr.prot_proc_id=pr.id OR sxpr.prot_tech_id=pr.id OR sxpr.prot_label_id=pr.id)) ) )'},
		'probe':{'cond_str':'pb.platform_id=pf.id'}
		}, 
	'sample':{
		'project':{'cond_str':'p.id=s.project_id'},
		'array':{
			'related_tbs':['sampxref sxa'],
			'cond_str':'(s.id=sxa.sample_id AND sxa.array_id=a.id)'},
		'protocol':{
			'related_tbs':['sampxref sxpr', 'array apr'],
			'cond_str':'(s.id=sxpr.sample_id AND (sxpr.prot_proc_id=pr.id OR sxpr.prot_tech_id=pr.id OR sxpr.prot_label_id=pr.id OR (sxpr.array_id=apr.id AND (apr.prot_hyb_id=pr.id OR apr.prot_img_id=pr.id OR apr.prot_data_id=pr.id) ) ) )'},
		'platform':{
			'related_tbs':['sampxref sxpf', 'array apf'],
			'cond_str':'(pf.id=apf.platform_id AND apf.id=sxpf.array_id AND sxpf.sample_id=s.id)'},
		'probe':{
			'related_tbs':['sampxref sxpb', 'array apb', 'platform pfpb'],
			'cond_str':'(pb.platform_id=pfpb.id AND pfpb.id=apb.platform_id AND apb.id=sxpb.array_id AND sxpb.sample_id=s.id)'}
		},
	'protocol':{
		'project':{'cond_str':'p.id=pr.project_id'},
		'sample':{
			'related_tbs':['sampxref sxs', 'array asm'],
			'cond_str':'(s.id=sxs.sample_id AND (sxs.prot_proc_id=pr.id OR sxs.prot_tech_id=pr.id OR sxs.prot_label_id=pr.id OR (sxs.array_id=asm.id AND (asm.prot_hyb_id=pr.id OR asm.prot_img_id=pr.id OR asm.prot_data_id=pr.id) ) ) )'},
		'array':{
			'related_tbs':['sampxref sxa'],
			'cond_str':'(a.prot_hyb_id=pr.id OR a.prot_img_id=pr.id OR a.prot_data_id=pr.id OR (sxa.array_id=a.id AND (sxa.prot_proc_id=pr.id OR sxa.prot_tech_id=pr.id OR sxa.prot_label_id=pr.id) ) )'},
		'platform':{
			'related_tbs':['sampxref sxpf', 'array apf'],
			'cond_str':'(pf.id=apf.platform_id AND (apf.prot_hyb_id=pr.id OR apf.prot_img_id=pr.id OR apf.prot_data_id=pr.id OR (sxpf.array_id=apf.id AND (sxpf.prot_proc_id=pr.id OR sxpf.prot_tech_id=pr.id OR sxpf.prot_label_id=pr.id) ) ) )'},
		'probe':{
			'related_tbs':['sampxref sxpb', 'array apb', 'platform pfpb'],
			'cond_str':'(pb.platform_id=pfpb.id AND pfpb.id=apb.platform_id AND (apb.prot_hyb_id=pr.id OR apb.prot_img_id=pr.id OR apb.prot_data_id=pr.id OR (sxpb.array_id=apb.id AND (sxpb.prot_proc_id=pr.id OR sxpb.prot_tech_id=pr.id OR sxpb.prot_label_id=pr.id) ) ) )'}
		},
	'probe':{
		'platform':{'cond_str':'pf.id=pb.platform_id'},
		'project':{'related_tbs':['platform pfpb'], 'cond_str':'pb.platform_id=pfpb.id AND pfpb.project_id=p.id'},
		'array':{'cond_str':'a.platform_id=pb.platform_id'},
		'sample':{
			'related_tbs':['array asm', 'sampxref sxs'],
			'cond_str':'(pb.platform_id=asm.platform_id AND asm.id=sxs.array_id AND sxs.sample_id=s.id)'},
		'protocol':{
			'related_tbs':['array apr', 'sampxref sxp'],
			'cond_str':'( pb.platform_id=apr.platform_id AND ( apr.prot_hyb_id=pr.id OR apr.prot_img_id=pr.id OR apr.prot_data_id=pr.id OR ( apr.id=sxp.array_id AND ( sxp.prot_proc_id=pr.id OR sxp.prot_tech_id=pr.id OR sxp.prot_label_id=pr.id ) ) ) )'}
		}
	}[obj_tb] # todo
#tbs_related = {'array':{'sample':'sampxref'}, } # todo for "FROM", sx1 for sample, sx2 for protocol????????

################ validation ##################
err_msg = []

def goBack(s=None):
	if s: err_msg.append(s)
	err_msg.append('<p><input type="button" value="Go back" onClick="javascript:history.go(-1)"/></p>')
	exitWithInfo('<br>'.join(err_msg))

# check required named first
for k,webname in requiredVars.items():
	if not page_values[k]: err_msg.append('%s should be offered!' % webname)

if 'colname' not in page_values or 'op' not in page_values or 'val' not in page_values: goBack('No criteria offered, please correct it!')
tbs, cols, ops, vals = page_values['tbname'], page_values['colname'], page_values['op'], page_values['val']
search_type, equ = page_values['search_type'], page_values['user_equ']
cur_db = page_values['dbname'] #cur_db, cur_tb = page_values['dbname'], page_values['tbname']
fixcols = eval(decodeStr(page_values['fixcols']))
# convert tuple to string
for k, v in fixcols.items(): 
	v = map(lambda a:((type(a) is types.StringType) and [a] or a)[0], v)
	fixcols[k] = dict(zip(v, [1]*len(v)))

#tbalias_dict = {'array':'a', 'project':'p', 'platform':'pf', 'protocol':'pr', 'sample':'s', 'dyncol':'d', 'keyword':'k', 'probe':'pb', 'dbkw':'dk', 'dbxref':'dx'}
tbd, tbdf, tbk, tbdk, tbdx = tbalias_dict['dyncol'], tbalias_dict['dyncoldef'], tbalias_dict['keyword'], tbalias_dict['dbkw'], tbalias_dict['dbxref']
pih_vars = {'cur_db':cur_db, 'cur_tb':obj_tb, 'tbalias':tbalias_dict}

if search_type != 'user_mode': # in ('any', 'all'). 
	# remove rows with empty items
	for i in range(len(ops)-1, -1, -1):
		if not cols[i].strip() or not ops[i].strip() or not vals[i].strip():
			cols.pop(i)
			ops.pop(i)
			vals.pop(i)
if not cols: goBack('No valid criteria offered!')

col_type = inquireDB('SELECT tb_name, col_name, col_type FROM %s.dyncoldef' % cur_db, fetch=True)
tb_col_type = {}
map(lambda a:tb_col_type.setdefault(a[0], {}).__setitem__(a[1], a[2]), col_type) # {tb:{col:type,...},...}
tp_col = {'int':'value_int', 'float':'value_float', 'string':'value_str'}
DYNCOL = False
KEYWORD = False
OTHERDB = False
dyncol_dic = {'tbd.col_id=tbdf.id':True}
keycol_dic = {}
othcol_dic = {}
#i_key = i_dyn = 0 # record the times of the table "keyword" and "dyncol, dyncoldef" being used.
tbkey, tbdyn = {}, {} # record which table use keyword, dyncol
def mapCond(i, f=fixcols, t=tbs, c=cols, v=vals, op=ops, od=op_dict, tbdic=tbalias_dict, tbd=tbd, tbk=tbk, tbdk=tbdk, tbdx=tbdx, dd=dyncol_dic, dk=keycol_dic, dother=othcol_dic, tbdf=tbdf, tp_col=tp_col, col_type=tb_col_type):
	if not t[i] or not c[i] or not op[i] or not v[i]: return ''
	cur_tb = t[i]
	tb = tbdic[cur_tb]
	f = f[cur_tb]
	if f.has_key(c[i]): # search in cur_tb
		return od[op[i]] % (tb, c[i], v[i])
	elif c[i] == 'keyword': # search in keyword
		global KEYWORD, tbkey #i_key
		if not KEYWORD: KEYWORD = True
		tbkey[cur_tb] = tbki = '%s_%s' % (tbk, cur_tb) #i_key = i_key + 1
		#tbki = tbk + str(i_key)
		return '%s AND %s' % ('%s.tb_name="%s" AND %s.rec_id=%s.id' % (tbki, cur_tb, tbki, tb), od[op[i]] % (tbki, 'kw', v[i]))
		#dk['%s.tb_name="%s" AND %s.rec_id=%s.id' % (tbk, cur_tb, tbk, tb)] = True
		#return od[op[i]] % (tbk, 'kw', v[i])
	elif cur_tb == 'probe': # search in dbkw
		global OTHERDB
		if not OTHERDB: OTHERDB = True
		return '%s AND %s' % ('%s.id=%s.dbkw_id AND %s.probe_id=%s.id' % (tbdk, tbdx, tbdx, tb), od[op[i]] % (tbdk, 'kw', v[i]))
		#dother['%s.id=%s.dbkw_id AND %s.probe_id=%s.id' % (tbdk, tbdx, tbdx, tb)] = True
		#return od[op[i]] % (tbdk, 'kw', v[i])
	else: # should search in dyncols
		global DYNCOL, tbdyn #i_dyn
		if not DYNCOL: DYNCOL = True
		tbdyn[cur_tb] = tbdi, tbdfi = '%s_%s' % (tbd, cur_tb), '%s_%s' % (tbdf, cur_tb) #i_dyn = i_dyn + 1
		#tbdi, tbdfi = tbd + str(i_dyn), tbdf + str(i_dyn)
		cnd = od[op[i]] % (tbdi, tp_col[col_type[cur_tb][c[i]]], v[i])
		return '%s AND %s' % ('%s.tb_name="%s" AND %s.rec_id=%s.id' % (tbdfi, cur_tb, tbdi, tb), '%s.col_name="%s" AND %s' % (tbdfi, c[i], cnd))
		#cnd = '( %s.col_name="%s" AND %s )' % (tbdf, c[i], cnd)
		#dd['%s.tb_name="%s" AND %s.rec_id=%s.id' % (tbdf, cur_tb, tbd, tb)] = True
		#return cnd

if search_type != 'user_mode': 
	#conds = map(lambda i:op_dict[ops[i]] % (tbalias, cols[i], vals[i]), range(len(cols)))
	conds = map(mapCond, range(len(cols)))
	conds = filter(lambda a:a, conds) # remove empty items
	if conds:
		if search_type == 'all': conds = ' AND '.join(conds)
		else: conds = ' OR '.join(conds) # search_type should be 'any'
	else: goBack('No valid criteria offered!')
else: # for user defined mode
	if not equ: goBack('No search mode offered!')
	items = map(lambda a:int(a), re.findall(r'\d+', equ) )
	if not items: goBack('Mode is invalid!')
	#items.sort()
	#if items[-1] > len(cols): goBack('Mode in invalid!')
	if max(items) > len(cols): goBack('Mode is invalid!')
	# make conditions
	item_set = sets.Set(items)
	conds = {}
	err_conds = []
	for i in item_set:
		if not cols[i-1].strip() or not ops[i-1].strip() or not vals[i-1].strip(): goBack('No valid criteria offered for user-defined mode!')
		#conds[i] = op_dict[ops[i-1]] % (tbalias, cols[i-1], vals[i-1])
		cond = mapCond(i-1)
		if not cond: err_conds.append(str(i))
		else: conds[i] = cond
	if err_conds: goBack('Criteria (%s) are invalid!' % ','.join(err_conds))
	conds = tuple(map(lambda a:conds[a], items))
	conds = re.sub(r'\d+', '%s', equ) % conds

if err_msg: goBack()

tbs_set = sets.Set(filter(lambda a:a, tbs)) # filter out empty items
# discard master table name
tbs_set.discard(obj_tb)
# remove other used table names
for tb in tbs_used[obj_tb]: tbs_set.discard(tb)
# make tb list for 'FROM' clause
pih_vars['dyn_tb'] = ''.join(map(lambda a, tba=tbalias_dict:', %s.%s %s' % (cur_db, a, tba[a]), tbs_set))

# find and add necessary connection tables like sampxref
for tb in tbs_set: #sets.Set(tbs):
	if not tbs_connections.has_key(tb): continue # only add connection tables for used entity tables
	rtbs, cndstr = tbs_connections[tb].get('related_tbs', []), tbs_connections[tb].get('cond_str', '')
	if cndstr: conds = conds and '(%s) AND %s' % (conds, cndstr) or cndstr
	if rtbs:
		#rtbs = ', '.join(map(lambda a:'%s.%s %s' % (cur_db, a[0], a[1]), rtbs.items() ) )
		rtbs = ', '.join(map(lambda a:'%s.%s' % (cur_db, a), rtbs ) )
		pih_vars['dyn_tb'] = '%s, %s' % ( pih_vars.get('dyn_tb', ''), rtbs )

pih_vars['conditions'] = ['( %s )' % conds]

if DYNCOL:
	#pih_vars['conditions'].insert(0, '%s.tb_name="%s" AND %s.rec_id=%s.id' % (tbd, cur_tb, tbd, tbalias) )
	#pih_vars['conditions'].insert(0, ' AND '.join(dyncol_dic.keys()) ) #, ', '.join(dyncol_dic.keys()) )
	#pih_vars['dyn_tb'] = '%s, %s.dyncoldef %s, %s.dyncol %s' % (pih_vars.get('dyn_tb', ''), cur_db, tbdf, cur_db, tbd)
	#pih_vars['dyn_tb'] = '%s, %s' % (pih_vars.get('dyn_tb', ''), ', '.join(map(lambda a:'%s.dyncoldef %s, %s.dyncol %s' % (cur_db, tbdf, a, cur_db, tbd, a), range(1, i_dyn+1))) )
	pih_vars['dyn_tb'] = '%s, %s' % (pih_vars.get('dyn_tb', ''), ', '.join(map(lambda a:'%s.dyncoldef %s, %s.dyncol %s' % (cur_db, a[1], cur_db, a[0]), tbdyn.values() )) )
if KEYWORD:
	#pih_vars['conditions'].insert(0, '%s.tb_name="%s" AND %s.rec_id=%s.id' % (tbk, cur_tb, tbk, tbalias) )
	#pih_vars['conditions'].insert(0, ' AND '.join(keycol_dic.keys()) ) #, ', '.join(keycol_dic.keys()) )
	#pih_vars['dyn_tb'] = '%s, %s.keyword %s' % (pih_vars.get('dyn_tb', ''), cur_db, tbk)
	#pih_vars['dyn_tb'] = '%s, %s' % (pih_vars.get('dyn_tb', ''), ', '.join(map(lambda a:'%s.keyword %s%d' % (cur_db, tbk, a), range(1, i_key+1))) )
	pih_vars['dyn_tb'] = '%s, %s' % (pih_vars.get('dyn_tb', ''), ', '.join(map(lambda a:'%s.keyword %s' % (cur_db, a), tbkey.values() )) )
if OTHERDB:
	#pih_vars['conditions'].insert(0, '%s.id=%s.dbkw_id AND %s.probe_id=%s.id' % (tbdk, tbdx, tbdx, tbalias) )
	#pih_vars['conditions'].insert(0, ' AND '.join(othcol_dic.keys()) ) #, ', '.join(othcol_dic.keys()) )
	pih_vars['dyn_tb'] = '%s, %s.dbxref %s, %s.dbkw %s' % (pih_vars.get('dyn_tb', ''), cur_db, tbdx, cur_db, tbdk)

if False and (DYNCOL or KEYWORD or OTHERDB): pih_vars['conditions'] = '( %s )' % ' AND '.join(pih_vars['conditions'])
else: pih_vars['conditions'] = pih_vars['conditions'][0]

# for array searching may return data to window opener
if page_values.has_key('return_value'): pih_vars['return_value'] = page_values['return_value']

saveEvent(user_id=username, ev_catcode="search", ev_valstr='%s::%s' % (cur_db, obj_tb))

print cgi_token

from PythonInsideHTML import PIH
exec PIH('%s/pages/%s' % (script_path_file, pages[obj_tb])).pythonCode()
#page = '%s/pages/%s' % (script_path_file, pages[obj_tb])
#open('tmpfile.py', 'w').write(PIH(page).pythonCode())
#execfile('tmpfile.py')

#tellDaemon('New requests')

