#!/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()

GO_term = ['GO_ID','GO_term']

monoVars = {'search_type':1, 'user_equ':1, 'dbname':1, 'obj_tb':1, 'GO':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']
GO = page_values['GO']

#select distinct d2.* from dbxref d1, dbxref d2, term_dbxref x1, term_dbxref x2 where d1.xref_dbname='PMID' && d1.xref_key='10873824' and d1.id=x1.dbxref_id and x1.term_id=x2.term_id and x2.dbxref_id=d2.id limit 100

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 = {
	'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!')
cols, ops, vals = 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']

#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!')

HAS_TERM = bool(sets.Set(GO_term).intersection(cols))
HAS_DB = bool(sets.Set(cols).difference(GO_term))

def mapCond(i, c=cols, v=vals, op=ops, od=op_dict):
	if not c[i] or not op[i] or not v[i]: return ''
	if c[i] in GO_term:
		tmcol = {'GO_ID':'acc','GO_term':'name'}
		return od[op[i]] % ('tm', tmcol[c[i]], v[i]) 
	else:
		return 'd1.xref_dbname="%s" AND %s' % (c[i], od[op[i]] % ('d1', 'xref_key', v[i]))

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 = [', %s.term_dbxref x2, %s.dbxref d2' % (GO, GO)]
if HAS_TERM: tbs.insert(0, ', %s.term tm' % GO)
if HAS_DB: tbs.insert(0, ', %s.dbxref d1, %s.term_dbxref x1' % (GO, GO))
tbs.insert(0, ', %s.dbxref %s, %s.dbkw %s' % (cur_db, tbdx, cur_db, tbdk))
pih_vars['dyn_tb'] = ''.join(tbs)

pih_vars['conditions'] = ['( %s )' % conds]
if HAS_TERM: pih_vars['conditions'].append('tm.id=x2.term_id')
if HAS_DB: pih_vars['conditions'].append('d1.id=x1.dbxref_id AND x1.term_id=x2.term_id')
pih_vars['conditions'].append('x2.dbxref_id=d2.id AND d2.xref_dbname=%s.dbname AND d2.xref_key=%s.kw AND %s.id=%s.dbkw_id AND %s.probe_id=%s.id' % (tbdk, tbdk, tbdk, tbdx, tbdx, tbalias_dict['probe']))
pih_vars['conditions'] = '( %s )' % ' AND '.join(pih_vars['conditions'])

# 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')

