import numpy as np
from astropy.io import fits
import pickle
import batman
import radvel
# Try to import catwoman:
try:
    import catwoman
    have_catwoman = True
except:
    have_catwoman = False


def init_batman(t, ld_law, nresampling=None, etresampling=None):
     """
     This function initializes the batman code.
     """

     params = batman.TransitParams()
     params.t0 = 0. 
     params.per = 1. 
     params.rp = 0.1
     params.a = 15.
     params.inc = 87.
     params.ecc = 0. 
     params.w = 90.

     if ld_law == 'linear':
         params.u = [0.5]
     else:
         params.u = [0.1,0.3]

     if ld_law == 'none':
         params.limb_dark = 'quadratic'
     else:
         params.limb_dark = ld_law

     params.ac = 0.001
     params.fp = 0.001

     params.t_secondary = params.t0 + (params.per/2) + params.ac

     if nresampling is None or etresampling is None:

         m = [batman.TransitModel(params, t), batman.TransitModel(params, t, transittype='secondary')]

     else:

         m = [batman.TransitModel(params, t, supersample_factor=nresampling, exp_time=etresampling),\
             batman.TransitModel(params, t, transittype='secondary', supersample_factor=nresampling, exp_time=etresampling)]

     return params,m

def init_catwoman(t, ld_law, nresampling = None, etresampling = None):
    """  
    This function initializes the catwoman code.
    """

    params = batman.TransitParams()
    params.t0 = 0.
    params.per = 1.
    params.rp = 0.1
    params.rp2 = 0.1
    params.a = 15.
    params.inc = 87.
    params.ecc = 0.
    params.w = 90.
    params.phi = 90.
    if ld_law == 'linear':
        params.u = [0.5]
    else:
        params.u = [0.1, 0.3]
    params.limb_dark = ld_law
    if nresampling is None or etresampling is None:
        m = catwoman.TransitModel(params, t)
    else:
        m = catwoman.TransitModel(params,
                                  t,
                                  supersample_factor=nresampling,
                                  exp_time=etresampling)
    return params, m


def init_radvel(nplanets=1):
    return radvel.model.Parameters(nplanets, basis='per tc e w k')


def mag_to_flux(m, merr):
    """
    Convert magnitude to relative fluxes.
    """
    fluxes = np.zeros(len(m))
    fluxes_err = np.zeros(len(m))
    for i in range(len(m)):
        dist = 10**(-np.random.normal(m[i], merr[i], 1000) / 2.51)
        fluxes[i] = np.mean(dist)
        fluxes_err[i] = np.sqrt(np.var(dist))
    return fluxes, fluxes_err


def get_TESS_data(filename, fluxtype='PDCSAP_FLUX'):
    """
    Given a filename, this function returns an array of times,
    fluxes and errors on those fluxes.
    """
    # Manipulate the fits file:
    data = fits.getdata(filename)

    # Identify zero-flux values to take them out of the data arrays:
    idx = np.where((data[fluxtype] != 0.) & (~np.isnan(data[fluxtype])))[0]

    # Return median-normalized flux:
    return data['TIME'][idx],data[fluxtype][idx]/np.median(data[fluxtype][idx]), \
           data[fluxtype+'_ERR'][idx]/np.median(data[fluxtype][idx])


import os
try:
    has_astroquery = True
    from astroquery.mast import Observations
except:
    has_astroquery = False

def get_all_TESS_data(object_name, radius = ".02 deg", get_PDC = True, get_all = False, get_lightcurves_only = True):
    """
    Given a planet name, this function returns a dictionary of times, fluxes and
    errors on fluxes in a juliet-friendly format for usage. The function does an
    astroquery to MAST using a default radius of .02 deg around the target name. If get_PDC is True,
    this function returns PDC fluxes. False returns SAP fluxes. If get_all is true, this function
    returns a dictionary that in addition to the times, fluxes and errors, returns other
    metadata.
    """
    if not has_astroquery:
        print(
            "Error on using juliet function `get_all_TESS_data`: astroquery.mast not found."
        )
    obs_table = Observations.query_object(object_name, radius=radius)
    out_dict = {}
    times = {}
    fluxes = {}
    fluxes_errors = {}
    for i in range(len(obs_table['dataURL'])):
        if 's_lc.fits' in obs_table['dataURL'][i]:
            fname = obs_table['dataURL'][i].split('/')[-1]
            metadata = fname.split('-')
            if len(metadata) == 5:
                # Extract metadata:
                sector = int(metadata[1].split('s')[-1])
                ticid = int(metadata[2])
                # Download files:
                data_products = Observations.get_product_list(obs_table[i])
                if get_lightcurves_only:
                    want = data_products['description'] == "Light curves"
                else:
                    want = (data_products['description'] == "Light curves") or (data_products['description'] == "Target pixel files")
                manifest = Observations.download_products(data_products[want])
                # Read lightcurve file:
                d, h = fits.getdata('mastDownload/TESS/' + fname[:-8] + '/' +
                                    fname,
                                    header=True)
                t,fs,fserr,f,ferr = d['TIME']+h['BJDREFI'],d['SAP_FLUX'],d['SAP_FLUX_ERR'],\
                                    d['PDCSAP_FLUX'],d['PDCSAP_FLUX_ERR']
                idx_goodpdc = np.where((f != 0.) & (~np.isnan(f)))[0]
                idx_goodsap = np.where((fs != 0.) & (~np.isnan(fs)))[0]
                # Save to output dictionary:
                if 'TIC' not in out_dict.keys():
                    out_dict['TIC'] = ticid
                out_dict[sector] = {}
                out_dict[sector]['TIME_PDCSAP_FLUX'] = t[idx_goodpdc]
                out_dict[sector]['PDCSAP_FLUX'] = f[idx_goodpdc]
                out_dict[sector]['PDCSAP_FLUX_ERR'] = ferr[idx_goodpdc]
                out_dict[sector]['TIME_SAP_FLUX'] = t[idx_goodsap]
                out_dict[sector]['SAP_FLUX'] = fs[idx_goodsap]
                out_dict[sector]['SAP_FLUX_ERR'] = fserr[idx_goodsap]
                if get_PDC:
                    times['TESS' + str(sector)] = t[idx_goodpdc]
                    med = np.median(f[idx_goodpdc])
                    fluxes['TESS' + str(sector)] = f[idx_goodpdc] / med
                    fluxes_errors['TESS' +
                                  str(sector)] = ferr[idx_goodpdc] / med
                else:
                    times['TESS' + str(sector)] = t[idx_goodsap]
                    med = np.median(fs[idx_goodsap])
                    fluxes['TESS' + str(sector)] = fs[idx_goodsap] / med
                    fluxes_errors['TESS' +
                                  str(sector)] = fserr[idx_goodsap] / med
                # Remove downloaded folder:
                os.system('rm -r mastDownload')
    if get_all:
        return out_dict, times, fluxes, fluxes_errors
    else:
        return times, fluxes, fluxes_errors


def reverse_ld_coeffs(ld_law, q1, q2):
    if ld_law == 'quadratic':
        coeff1 = 2. * np.sqrt(q1) * q2
        coeff2 = np.sqrt(q1) * (1. - 2. * q2)
    elif ld_law == 'squareroot':
        coeff1 = np.sqrt(q1) * (1. - 2. * q2)
        coeff2 = 2. * np.sqrt(q1) * q2
    elif ld_law == 'logarithmic':
        coeff1 = 1. - np.sqrt(q1) * q2
        coeff2 = 1. - np.sqrt(q1)
    elif ld_law == 'linear':
        return q1, q2
    return coeff1, coeff2


def reverse_q_coeffs(ld_law, u1, u2):
    if ld_law == 'quadratic':
        q1 = (u1 + u2)**2
        q2 = (u1 / 2.) / (u1 + u2)
    elif ld_law == 'squareroot':
        q1 = (u1 + u2)**2
        q2 = (u2 / 2.) / (u1 + u2)
    elif ld_law == 'logarithmic':
        q1 = (1. - u2)**2
        q2 = (1. - u1) / (1. - u2)
    return q1, q2


def convert_ld_coeffs(ld_law, coeff1, coeff2):
    if ld_law == 'quadratic':
        q1 = (coeff1 + coeff2)**2
        q2 = coeff1 / (2. * (coeff1 + coeff2))
    elif ld_law == 'squareroot':
        q1 = (coeff1 + coeff2)**2
        q2 = coeff2 / (2. * (coeff1 + coeff2))
    elif ld_law == 'logarithmic':
        q1 = (1 - coeff2)**2
        q2 = (1. - coeff1) / (1. - coeff2)
    return q1, q2


def reverse_bp(r1, r2, pl, pu):
    Ar = (pu - pl) / (2. + pl + pu)
    nsamples = len(r1)
    p = np.zeros(nsamples)
    b = np.zeros(nsamples)
    for i in range(nsamples):
        if r1[i] > Ar:
            b[i],p[i] = (1+pl)*(1. + (r1[i]-1.)/(1.-Ar)),\
                        (1-r2[i])*pl + r2[i]*pu
        else:
            b[i],p[i] = (1. + pl) + np.sqrt(r1[i]/Ar)*r2[i]*(pu-pl),\
                        pu + (pl-pu)*np.sqrt(r1[i]/Ar)*(1.-r2[i])
    return b, p


from scipy.stats import gamma, norm, beta, truncnorm, lognorm
import numpy as np

# Prior transforms for nested samplers:
def transform_uniform(x, hyperparameters):
    a, b = hyperparameters
    return a + (b-a)*x

def transform_loguniform(x, hyperparameters):
    a, b = hyperparameters
    la = np.log(a)
    lb = np.log(b)
    return np.exp(la + x * (lb - la))

def transform_normal(x, hyperparameters):
    mu, sigma = hyperparameters
    return norm.ppf(x, loc=mu, scale=sigma)

def transform_beta(x, hyperparameters):
    a, b = hyperparameters
    return beta.ppf(x, a, b)

def transform_exponential(x, hyperparameters):
    a = hyperparameters
    return gamma.ppf(x, a)

def transform_truncated_normal(x, hyperparameters):
    mu, sigma, a, b = hyperparameters
    ar, br = (a - mu) / sigma, (b - mu) / sigma
    return truncnorm.ppf(x, ar, br, loc=mu, scale=sigma)

def transform_modifiedjeffreys(x, hyperparameters):
    turn, hi = hyperparameters
    return turn * (np.exp((x + 1e-10) * np.log(hi / turn + 1)) - 1)


# Log-prior evaluations for MCMCs:
def evaluate_uniform(x, hyperparameters):
    a, b = hyperparameters
    if x > a and x < b:
        return np.log(1./(b - a))
    else:
        return -np.inf

def evaluate_loguniform(x, hyperparameters):
    a, b = hyperparameters
    if x > a and x < b:
        la = np.log(a)
        lb = np.log(b)
        return np.log(1./(x * (lb - la)))
    else:
        return -np.inf

def evaluate_normal(x, hyperparameters):
    mu, sigma = hyperparameters
    return norm.logpdf(x, loc=mu, scale=sigma)

def evaluate_beta(x, hyperparameters):
    a, b = hyperparameters
    if a > 0 and b < 1:
        return beta.logpdf(x, a, b)
    else:
        return -np.inf

def evaluate_exponential(x, hyperparameters):

    a = hyperparameters

    if x > 0:

        return gamma.logpdf(x, a)

    else:

        return -np.inf

def evaluate_truncated_normal(x, hyperparameters):
    mu, sigma, a, b = hyperparameters
    if x > a and x < b:
        ar, br = (a - mu) / sigma, (b - mu) / sigma
        return truncnorm.logpdf(x, ar, br, loc=mu, scale=sigma)
    else:
        return -np.inf

def evaluate_modifiedjeffreys(x, hyperparameters):
    turn, hi = hyperparameters
    if x > 0 and x < hi:
        return np.log(1.) - np.log(x + turn) + np.log(1.) - np.log( np.log( (turn + hi) / turn ) )
    else:
        return -np.inf

def input_error_catcher(t,y,yerr,datatype):
    if datatype == 'lightcurve':
        dname = 'lc'
    else:
        dname = 'rv'
    if (y is None):
        raise Exception('INPUT ERROR: No '+datatype+' data was fed to juliet. \n'+\
                        ' Make sure to pass data (y_'+dname+') and errors (yerr_'+dname+').')
    if (yerr is None):
        raise Exception('INPUT ERROR: No ERRORS (yerr_'+dname+') on the '+datatype+' data were fed to juliet. \n'+\
                        ' Make sure to pass data (y_'+dname+') and errors (yerr_'+dname+')..')


def read_data(fname):
    fin = open(fname, 'r')
    ts = np.array([])
    fs = np.array([])
    ferrs = np.array([])
    instruments = np.array([])
    # Arguments of an optional linear model. This will save the regression matrix "X" in a model of the form X*theta = y, where theta
    # are the coefficients:
    lm_arguments = {}
    # This will save a True or False for each instrument --- True if there are
    # inputs and therefore we want a linear model, False if not:
    lm_boolean = {}
    instrument_names = []
    while True:
        line = fin.readline()
        if line != '':
            all_vals = line.split()
            t, f, ferr, instrument = all_vals[0:4]
            lm_variables = all_vals[4:]
            ts = np.append(ts, np.double(t))
            fs = np.append(fs, np.double(f))
            ferrs = np.append(ferrs, np.double(ferr))
            instruments = np.append(instruments, instrument.split()[0])
            if instrument.split()[0] not in instrument_names:
                instrument_names.append(instrument.split()[0])
                if len(lm_variables) > 0:
                    lm_arguments[instrument.split()[0]] = np.array([])
                    lm_boolean[instrument.split()[0]] = True
                else:
                    lm_boolean[instrument.split()[0]] = False
            if lm_boolean[instrument.split()[0]]:
                if len(lm_arguments[instrument.split()[0]]) == 0:
                    lm_arguments[instrument.split()[0]] = np.array(
                        lm_variables).astype(np.double)
                else:
                    lm_arguments[instrument.split()[0]] = np.vstack((lm_arguments[instrument.split()[0]],\
                                                               np.array(lm_variables).astype(np.double)))
        else:
            break
    # Identify instrument indeces:
    indexes = {}
    for instrument in instrument_names:
        indexes[instrument] = np.where(instruments == instrument)[0]
    return ts, fs, ferrs, instruments, indexes, len(
        instrument_names), instrument_names, lm_boolean, lm_arguments


def readGPeparams(fname):
    fin = open(fname, 'r')
    GPDictionary = {}
    ftime = True
    global_model = False
    while True:
        line = fin.readline()
        if line != '':
            if line[0] != '#':
                vector = line.split()
                variables, instrument = vector[:-1], vector[-1].split()[0]
                if ftime:
                    if instrument == 'rv' or instrument == 'lc':
                        global_model = True
                    else:
                        global_model = False
                    appended_vector = np.double(np.array(variables))
                else:
                    appended_vector = np.double(np.array(variables))

                if ftime:
                    ftime = False
                if instrument in GPDictionary.keys():
                    GPDictionary[instrument] = np.vstack(
                        (GPDictionary[instrument], appended_vector))
                else:
                    GPDictionary[instrument] = {}
                    GPDictionary[instrument] = appended_vector
        else:
            break
    return GPDictionary, global_model


def readpriors(priorname):
    """
    This function takes either a string or a dict and spits out information about the prior. If a string, it
    reads a prior file. If a dict, it assumes the input dictionary has already defined all the variables and
    distributions and simply spits out information about the system (e.g., number of transiting planets, RV
    planets, etc.)
    """
    input_dict = False

    if type(priorname) == str:

        fin = open(priorname)
        priors = {}
        starting_point = {}

    else:

        counter = -1
        priors = priorname
        input_dict = True
        all_parameters = list(priors.keys())
        n_allkeys = len(all_parameters)

    n_transit = 0
    n_rv = 0
    n_params = 0
    numbering_transit = np.array([])
    numbering_rv = np.array([])

    while True:

        if not input_dict:

            line = fin.readline()

        else:

            # Dummy variable so we enter the while:
            line = 'nc'
            counter += 1

        if line != '':

            if line[0] != '#':

                if not input_dict:

                    prior_vector = line.split()

                    if len(prior_vector) == 3:

                        parameter, prior_name, vals = prior_vector
                        has_sp = False

                    else:

                        parameter, prior_name, vals, sp = prior_vector
                        has_sp = True

                    parameter = parameter.split()[0]

                    # For retro-compatibility, if parameter is of the form sigma_w_rv_instrument change to
                    # sigma_w_instrument:
                    if parameter[:10] == 'sigma_w_rv':

                        instrument = parameter.split('_')[-1]
                        parameter = 'sigma_w_' + instrument

                    prior_name = prior_name.split()[0]
                    vals = vals.split()[0]
                    priors[parameter] = {}

                    if has_sp:

                        starting_point[parameter] = np.double(sp)

                else:

                    param = all_parameters[counter]
                    parameter, prior_name = param, priors[param]['distribution']

                pvector = parameter.split('_')

                # Check if parameter/planet is from a transiting planet:
                if pvector[0] == 'r1' or pvector[0] == 'p' or pvector[
                        0] == 'phi':

                    pnumber = int(pvector[1][1:])
                    numbering_transit = np.append(numbering_transit, pnumber)
                    n_transit += 1

                # Check if parameter/planet is from a RV planet:
                if pvector[0] == 'K':

                    pnumber = int(pvector[1][1:])
                    numbering_rv = np.append(numbering_rv, pnumber)
                    n_rv += 1

                #if parameter == 'r1_p'+str(n_transit+1) or parameter == 'p_p'+str(n_transit+1):
                #    numbering_transit = np.append(numbering_transit,n_transit+1)
                #    n_transit += 1
                #if parameter == 'K_p'+str(n_rv+1):
                #    numbering_rv = np.append(numbering_rv,n_rv+1)
                #    n_rv += 1

                if prior_name.lower() == 'fixed':

                    if not input_dict:

                        priors[parameter]['distribution'] = prior_name.lower()
                        priors[parameter]['hyperparameters'] = np.double(vals)
                        priors[parameter]['cvalue'] = np.double(vals)

                else:

                    n_params += 1

                    if not input_dict:

                        priors[parameter]['distribution'] = prior_name.lower()

                        if priors[parameter]['distribution'] != 'truncatednormal':

                            if priors[parameter]['distribution'] == 'exponential':

                                priors[parameter]['hyperparameters'] = [ np.double(vals) ]

                            else:

                                v1, v2 = vals.split(',')

                                priors[parameter]['hyperparameters'] = [
                                    np.double(v1), np.double(v2)
                                ]

                        else:

                            v1, v2, v3, v4 = vals.split(',')
                            priors[parameter]['hyperparameters'] = [
                                np.double(v1),
                                np.double(v2),
                                np.double(v3),
                                np.double(v4)

                            ]
                        priors[parameter]['cvalue'] = 0.

        else:

            break

        if input_dict:

            if counter == n_allkeys - 1:

                break

    if not input_dict:

        if len(starting_point.keys()) == 0:

            starting_point = None

        return priors, n_transit, n_rv, numbering_transit.astype('int'), numbering_rv.astype('int'), n_params, starting_point

    else:

        return n_transit, n_rv, numbering_transit.astype('int'), numbering_rv.astype('int'), n_params

def get_phases(t, P, t0, phmin=0.5):
    """
    Given input times, a period (or posterior dist of periods)
    and time of transit center (or posterior), returns the
    phase at each time t.
    """
    if type(t) is not float:

        phase = ( (t - np.median(t0)) / np.median(P) ) % 1
        ii = np.where(phase >= phmin)[0]
        phase[ii] = phase[ii] - 1.0
      
    else:
      
        phase = ((t - np.median(t0)) / np.median(P)) % 1
        
        if phase >= 0.5:
            phase = phase - 1.0
            
    return phase


def get_quantiles(dist, alpha=0.68, method='median'):
    """
    get_quantiles function
    DESCRIPTION
        This function returns, in the default case, the parameter median and the error%
        credibility around it. This assumes you give a non-ordered
        distribution of parameters.
    OUTPUTS
        Median of the parameter,upper credibility bound, lower credibility bound
    """
    ordered_dist = dist[np.argsort(dist)]
    param = 0.0
    # Define the number of samples from posterior
    nsamples = len(dist)
    nsamples_at_each_side = int(nsamples * (alpha / 2.) + 1)
    if (method == 'median'):
        med_idx = 0
        if (nsamples % 2 == 0.0):  # Number of points is even
            med_idx_up = int(nsamples / 2.) + 1
            med_idx_down = med_idx_up - 1
            param = (ordered_dist[med_idx_up] + ordered_dist[med_idx_down]) / 2.
            return param,ordered_dist[med_idx_up+nsamples_at_each_side],\
                   ordered_dist[med_idx_down-nsamples_at_each_side]
        else:
            med_idx = int(nsamples / 2.)
            param = ordered_dist[med_idx]
            return param,ordered_dist[med_idx+nsamples_at_each_side],\
                   ordered_dist[med_idx-nsamples_at_each_side]


def bin_data(x, y, n_bin):
    x_bins = []
    y_bins = []
    y_err_bins = []
    for i in range(0, len(x), n_bin):
        x_bins.append(np.median(x[i:i + n_bin - 1]))
        y_bins.append(np.median(y[i:i + n_bin - 1]))
        y_err_bins.append(
            np.sqrt(np.var(y[i:i + n_bin - 1])) /
            np.sqrt(len(y[i:i + n_bin - 1])))
    return np.array(x_bins), np.array(y_bins), np.array(y_err_bins)


def writepp(fout, posteriors, priors):
    if 'pu' in posteriors:
        pu = posteriors['pu']
        pl = posteriors['pl']
        Ar = (pu - pl) / (2. + pl + pu)

    fout.write('# {0:18} \t \t {1:12} \t \t {2:12} \t \t {3:12}\n'.format(
        'Parameter Name', 'Median', 'Upper 68 CI', 'Lower 68 CI'))
    for pname in posteriors['posterior_samples'].keys():
        if pname != 'unnamed' and pname != 'loglike':
            val, valup, valdown = get_quantiles(
                posteriors['posterior_samples'][pname])
            usigma = valup - val
            dsigma = val - valdown
            fout.write(
                '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.format(
                    pname, val, usigma, dsigma))
            if pname.split('_')[0] == 'r2':
                par, planet = pname.split('_')
                r1 = posteriors['posterior_samples']['r1_' + planet]
                r2 = posteriors['posterior_samples']['r2_' + planet]
                b, p = np.zeros(len(r1)), np.zeros(len(r1))
                for i in range(len(r1)):
                    if r1[i] > Ar:
                        b[i],p[i] = (1+pl)*(1. + (r1[i]-1.)/(1.-Ar)),\
                                    (1-r2[i])*pl + r2[i]*pu
                    else:
                        b[i],p[i] = (1. + pl) + np.sqrt(r1[i]/Ar)*r2[i]*(pu-pl),\
                                    pu + (pl-pu)*np.sqrt(r1[i]/Ar)*(1.-r2[i])
                val, valup, valdown = get_quantiles(p)
                usigma = valup - val
                dsigma = val - valdown
                fout.write(
                    '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                    format('p_' + planet, val, usigma, dsigma))
                val, valup, valdown = get_quantiles(b)
                usigma = valup - val
                dsigma = val - valdown
                fout.write(
                    '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                    format('b_' + planet, val, usigma, dsigma))
                # Calculate inclination:
                if 'ecosomega_' + planet in posteriors['posterior_samples']:
                    iplanet = planet[1:]
                    ecc = np.sqrt(
                        posteriors['posterior_samples']['ecosomega_p' +
                                                        str(iplanet)]**2 +
                        posteriors['posterior_samples']['esinomega_p' +
                                                        str(iplanet)]**2)
                    omega = np.arctan2(posteriors['posterior_samples']['esinomega_p'+str(iplanet)],\
                                   posteriors['posterior_samples']['ecosomega_p'+str(iplanet)])
                elif 'secosomega_' + planet in posteriors['posterior_samples']:
                    iplanet = planet[1:]
                    ecc = posteriors['posterior_samples']['secosomega_p' + str(
                        iplanet)]**2 + posteriors['posterior_samples'][
                            'sesinomega_p' + str(iplanet)]**2
                    omega = np.arctan2(posteriors['posterior_samples']['sesinomega_p'+str(iplanet)],\
                                       posteriors['posterior_samples']['secosomega_p'+str(iplanet)])
                elif 'ecc_' + planet in posteriors['posterior_samples']:
                    ecc = posteriors['posterior_samples']['ecc_' + planet]
                    omega = posteriors['posterior_samples'][
                        'omega_' + planet] * np.pi / 180.
                else:
                    ecc = 0.
                    omega = 90.
                ecc_factor = (1. + ecc * np.sin(omega)) / (1. - ecc**2)
                if 'rho' in posteriors['posterior_samples']:
                    G = 6.67408e-11
                    if 'P_' + planet in posteriors['posterior_samples']:
                        a = ((posteriors['posterior_samples']['rho'] * G *
                              ((posteriors['posterior_samples']['P_' + planet] *
                                24. * 3600.)**2)) / (3. * np.pi))**(1. / 3.)
                    else:
                        a = ((posteriors['posterior_samples']['rho'] * G *
                              ((priors['P_' + planet]['hyperparameters'] * 24. *
                                3600.)**2)) / (3. * np.pi))**(1. / 3.)
                else:
                    if 'a_' + planet in posteriors['posterior_samples']:
                        a = posteriors['posterior_samples']['a_' + planet]
                    else:
                        a = priors['a_' + planet]['hyperparameters']
                inc_inv_factor = (b / a) * ecc_factor
                inc = np.arccos(inc_inv_factor) * 180. / np.pi
                val, valup, valdown = get_quantiles(inc)
                usigma = valup - val
                dsigma = val - valdown
                fout.write(
                    '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                    format('inc_' + planet, val, usigma, dsigma))

            if pname.split('_')[0] == 'P':
                if 'rho' in posteriors['posterior_samples']:
                    par, planet = pname.split('_')
                    G = 6.67408e-11
                    if 'P_' + planet in posteriors['posterior_samples']:
                        a = ((posteriors['posterior_samples']['rho'] * G *
                              ((posteriors['posterior_samples']['P_' + planet] *
                                24. * 3600.)**2)) / (3. * np.pi))**(1. / 3.)
                    else:
                        a = ((posteriors['posterior_samples']['rho'] * G *
                              ((priors['P_' + planet]['hyperparameters'] * 24. *
                                3600.)**2)) / (3. * np.pi))**(1. / 3.)
                    val, valup, valdown = get_quantiles(a)
                    usigma = valup - val
                    dsigma = val - valdown
                    fout.write(
                        '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                        format('a_' + planet, val, usigma, dsigma))
            if pname.split('_')[0] == 'ecosomega':
                par, planet = pname.split('_')
                iplanet = planet[1:]
                ecc = np.sqrt(posteriors['posterior_samples']['ecosomega_p' +
                                                              str(iplanet)]**2 +
                              posteriors['posterior_samples']['esinomega_p' +
                                                              str(iplanet)]**2)
                omega = np.arctan2(posteriors['posterior_samples']['esinomega_p'+str(iplanet)],\
                                   posteriors['posterior_samples']['ecosomega_p'+str(iplanet)])*(180/np.pi)

                val, valup, valdown = get_quantiles(ecc)
                usigma = valup - val
                dsigma = val - valdown
                fout.write(
                    '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                    format('ecc_' + planet, val, usigma, dsigma))

                idx = np.where(omega > 0.)[0]
                val, valup, valdown = get_quantiles(omega[idx])
                usigma = valup - val
                dsigma = val - valdown
                fout.write(
                    '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                    format('omega_' + planet, val, usigma, dsigma))

            if pname.split('_')[0] == 'secosomega':
                par, planet = pname.split('_')
                iplanet = planet[1:]
                ecc = posteriors['posterior_samples']['secosomega_p' + str(
                    iplanet)]**2 + posteriors['posterior_samples'][
                        'sesinomega_p' + str(iplanet)]**2
                omega = np.arctan2(posteriors['posterior_samples']['sesinomega_p'+str(iplanet)],\
                                   posteriors['posterior_samples']['secosomega_p'+str(iplanet)])*(180/np.pi)

                val, valup, valdown = get_quantiles(ecc)
                usigma = valup - val
                dsigma = val - valdown
                fout.write(
                    '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                    format('ecc_' + planet, val, usigma, dsigma))

                idx = np.where(omega > 0.)[0]
                val, valup, valdown = get_quantiles(omega[idx])
                usigma = valup - val
                dsigma = val - valdown
                fout.write(
                    '{0:18} \t \t {1:.10f} \t \t {2:.10f} \t \t {3:.10f}\n'.
                    format('omega_' + planet, val, usigma, dsigma))
    fout.close()


from astropy.time import Time as APYTime


def convert_time(conv_string, t):
    input_t, output_t = conv_string.split('->')
    if input_t != output_t:
        tobj = APYTime(t, format='jd', scale=input_t)
        # print('new_t = tobj.'+output_t+'.jd')
        # exec('new_t = tobj.'+output_t+'.jd')
        if output_t == 'utc':
            return tobj.utc.jd
        else:
            return t
        # return new_t
    else:
        return t


def generate_priors(params, dists, hyperps):
    priors = {}
    for param, dist, hyperp in zip(params, dists, hyperps):
        priors[param] = {}
        priors[param]['distribution'], priors[param][
            'hyperparameters'] = dist, hyperp
    return priors


def read_AIJ_tbl(fname):
    """
    This function reads in an AstroAIJ table and returns a dictionary with all the parameters in numpy arrays.
    """
    fin = open(fname, 'r')
    firstime = True
    out_dict = {}
    while True:
        line = fin.readline()
        if line != '':
            vec = line.split()
            if firstime:
                out_dict['index'] = np.array([])
                for i in range(len(vec)):
                    out_dict[vec[i]] = np.array([])
                firstime = False
                parameter_vector = ['index'] + vec
            else:
                for i in range(len(vec)):
                    try:
                        out_dict[parameter_vector[i]] = np.append(
                            out_dict[parameter_vector[i]], np.double(vec[i]))
                    except:
                        out_dict[parameter_vector[i]] = np.append(
                            out_dict[parameter_vector[i]], np.nan)
        else:
            break
    return out_dict
