#!/usr/bin/env python

"""
amnonscript
amnonutils.py

heatsequer
various utility functions
"""

import numpy as np
import scipy as sp

import inspect
import os
import logging
import time

__version__ = "0.2"


def start_log(level=logging.DEBUG, filename='log.hs.log'):
	"""start the logger for the run

	Parameters
	----------
	level : int, optional
		logging.DEBUG, logging.INFO etc. for the log level (between 0-50).
	filename : str, optional
		name of the filename to save the log to or None (default) to print to screen
	"""
	logging.basicConfig(filename=filename, level=level,format='%(asctime)s:%(message)s')
	logger = logging.getLogger(__name__)
	logger.info('*************************')
	logger.info('logging started')


def Debug(dlevel,*args):
	if dlevel>=DebugLevel:
		logger = logging.getLogger(__name__)
		logger.debug(args)
		print (args)


def SetDebugLevel(dlevel):
	"""
	set the debug level for output
	0 - all info (debug)
	5 - warnings / info
	9 - critical

	input:
	dlevel : int (0-10)
		the minimum message level to show
	"""
	global DebugLevel

	DebugLevel = dlevel


def reverse(seq):
	oseq=''
	for a in seq:
		oseq=a+oseq
	return oseq


def complement(seq):
	seq=seq.upper()
	oseq=''
	for a in seq:
		if a=='A':
			oseq+='T'
		elif a=='C':
			oseq+='G'
		elif a=='G':
			oseq+='C'
		elif a=='T':
			oseq+='A'
		else:
			oseq+='N'
	return oseq


def revcomp(seq):
	return reverse(complement(seq))


def iterfastaseqs(filename):
	"""
	iterate a fasta file and return header,sequence
	input:
	filename - the fasta file name

	output:
	seq - the sequence
	header - the header
	"""

	fl=open(filename,"rU")
	cseq=''
	chead=''
	for cline in fl:
		if cline[0]=='>':
			if chead:
				yield(cseq,chead)
			cseq=''
			chead=cline[1:].rstrip()
		else:
			cseq+=cline.strip()
	if cseq:
		yield(cseq,chead)
	fl.close()



def readfastaseqs(filename):
	"""
	read a fasta file and return a list of sequences
	input:
	filename - the fasta file name

	output:
	seqs - a list of sequences
	headers - a list of the headers
	"""
	fl=open(filename,"rU")
	cseq=''
	seqs=[]
	headers=[]
	for cline in fl:
		if cline[0]=='>':
			headers.append(cline[1:].rstrip())
			if cseq:
				seqs.append(cseq)
				cseq=''
		else:
			cseq+=cline.strip()
	if cseq:
		seqs.append(cseq)
	return seqs,headers


def isort(clist,reverse=False):
	"""
	matlab style sort
	returns both sorted list and the indices of the sort
	input:
	clist: a list to sort
	reverse - true to reverse the sort direction
	output:
	(svals,sidx)
	svals - the sorted values
	sidx - the sorted indices
	"""
	res=sorted(enumerate(clist), key=lambda x:x[1],reverse=reverse)
	svals=[i[1] for i in res]
	sidx=[i[0] for i in res]

	return svals,sidx


def tofloat(clist):
	"""
	convert a list of strings to a list of floats
	input:
	clist - list of strings
	output:
	res - list of floats
	"""
	res=[]
	for s in clist:
		try:
			cval=float(s)
		except:
			cval=0
		if np.isnan(cval):
			cval=0
		res.append(cval)
	return res


def reorder(clist,idx):
	""""
	reorder a list according to idx
	"""
	return [clist[i] for i in idx]


def delete(clist,idx):
	"""
	delete elements from list
	"""
	for i in sorted(idx, reverse=True):
		del clist[i]
	return clist


def clipstrings(clist,maxlen,reverse=False):
	"""
	clip all strings in a list to maxlen
	input:
	clist - list of strings
	maxlen - maximal length for each string
	reverse - if true - clip from end (otherwise from beginning)
	"""
	retlist=[]
	for cstr in clist:
		clen=min(maxlen,len(cstr))
		if reverse:
			retlist.append(cstr[-clen:])
		else:
			retlist.append(cstr[0:clen])
	return retlist


def mlhash(cstr,emod=0):
	"""
	do a hash function on the string cstr
	based on the matlab hash function string2hash
	input:
	cstr - the string to hash
	emod - if 0, don't do modulu, otherwise do modulo
	"""
	chash = 5381
	pnum=pow(2,32)-1
	for cc in cstr:
		chash=np.mod(chash*33+ord(cc),pnum)
	if emod>0:
		chash=np.mod(chash,emod)
	return(chash)


def nicenum(num):
	"""
	get a nice string representation of the numnber
	(turn to K/M if big, m/u if small, trim numbers after decimal point)
	input:
	num - the number
	output:
	numstr - the nice string of the number
	"""

	if num==0:
		numstr="0"
	elif abs(num)>1000000:
		numstr="%.1fM" % (float(num)/1000000)
	elif abs(num)>1000:
		numstr="%.1fK" % (float(num)/1000)
	elif abs(num)<0.000001:
		numstr="%.1fu" % (num*1000000)
	elif abs(num)<0.001:
		numstr="%.1fm" % (num*1000)
	else:
		numstr=int(num)
	return numstr



def SeqToArray(seq):
	""" convert a string sequence to a numpy array"""
	seqa=np.zeros(len(seq),dtype=np.int8)
	for ind,base in enumerate(seq):
		if base=='A':
			seqa[ind]=0
		elif base=='a':
			seqa[ind]=0
		elif base=='C':
			seqa[ind]=1
		elif base=='c':
			seqa[ind]=1
		elif base=='G':
			seqa[ind]=2
		elif base=='g':
			seqa[ind]=2
		elif base=='T':
			seqa[ind]=3
		elif base=='t':
			seqa[ind]=3
		elif base=='-':
			seqa[ind]=4
		else:
			seqa[ind]=5
	return(seqa)


def ArrayToSeq(seqa):
	""" convert a numpy array to sequence (upper case)"""
	seq=''
	for cnuc in seqa:
		if cnuc==0:
			seq+='A'
		elif cnuc==1:
			seq+='C'
		elif cnuc==2:
			seq+='G'
		elif cnuc==3: