import os
import time
import pandas as pd
import schedule
import numpy as np


def read_last_OD_values(pe):
    def strtoint(s):
        try:
            return float(s)
        except:
            return None

    df=pd.read_csv(pe.OD_measurement.od_file, index_col=0)
    lastvalues = [0,0,0,0,0,0,0]
    for t in range(7):
        try:
            ODvalues=list(strtoint(s) for s in df.iloc[:, t])
            ODvalues=[od for od in ODvalues if od]
            lastvalues[t]=ODvalues[-1]
        except:
            pass
    return np.array(lastvalues)


def make_dilution(pe, tube, death_volume=0, peace_volume=0, extra_vacuum=5):
    pe.mix(30)   # Mixer speed > 30 while pumping may lead to power failure!
    if death_volume>0:
        pe.Pumps.pump_death(tube, death_volume)
    if peace_volume>0:
        pe.Pumps.pump_peace(tube, peace_volume)
    pe.Pumps.pump_vacuum(tube, death_volume+peace_volume)
    pe.mix(0)
    pe.Pumps.pump_vacuum(tube, extra_vacuum)
    pe.mix(30)


def _write_parameters_drug_doses(pe, drug_doses=(0, 0, 0, 0, 0, 0, 0)):
    assert len(drug_doses) == 7
    with open(os.path.join(pe.name, "parameters_dose.csv"), "w+") as f:
        f.write(",".join([str(d) for d in drug_doses]))


def _read_parameters_drug_doses(pe):
    df = pd.read_csv(os.path.join(pe.name, "parameters_dose.csv"), header=None)
    drug_doses = np.array(df.values.ravel()).astype(float)
    pe.drug_doses = drug_doses
    return drug_doses


def _write_parameters_OD_thresholds(pe, OD_thresholds=(0, 0, 0, 0, 0, 0, 0)):
    OD_thresholds = np.array(OD_thresholds).astype(float)
    assert len(OD_thresholds) == 7
    with open(os.path.join(pe.name, "parameters_OD_thresholds.csv"), "w+") as f:
        f.write(",".join([str(thr) for thr in OD_thresholds]))


def _read_parameters_OD_thresholds(pe):
    OD_thresholds = pd.read_csv(os.path.join(pe.name, "parameters_OD_thresholds.csv"), header=None).values[0]
    OD_thresholds = np.array(OD_thresholds.ravel()).astype(float)
    pe.OD_thresholds = OD_thresholds
    return OD_thresholds


def dilute_if_necessary(pe, total_dilution_volume=10):
    OD_thresholds = pe.parameters_OD_thresholds
    drug_doses = pe.parameters_drug_doses

    death_volumes = total_dilution_volume * drug_doses/100
    peace_volumes = total_dilution_volume - death_volumes
    for tube in range(7):
        if OD_thresholds[tube] > 0.15 and pe.tubes_status_active[tube]:
            if pe.last_OD_values[tube] > OD_thresholds[tube]:
                make_dilution(pe, tube,
                              death_volume=death_volumes[tube],
                              peace_volume=peace_volumes[tube],
                              extra_vacuum=5)


def pause_mixers_and_measure_OD(pe, mixer_speed):
    pe.mix(mixer_speed)
    time.sleep(2)
    pe.mix(0)
    time.sleep(3)
    for t in range(7):
        pe.last_OD_values[t] = pe.OD_measurement.measure_OD(t)
    pe.mix(mixer_speed)

class color:
   PURPLE = '\033[95m'
   CYAN = '\033[96m'
   DARKCYAN = '\033[36m'
   BLUE = '\033[94m'
   GREEN = '\033[92m'
   YELLOW = '\033[93m'
   RED = '\033[91m'
   BOLD = '\033[1m'
   UNDERLINE = '\033[4m'
   END = '\033[0m'


def start_experiment(pe):

    print("Starting Experiment loop")
    pe.tubes_status_active = [i in pe.active_tubes for i in range(7)]
    if len(pe.active_tubes) == 0:
        print("####### NO ACTIVE TUBES! #######")
    print("Active tubes: ", dict(zip(range(7), pe.tubes_status_active)))
    pe.mix(pe.mixer_speed)
    try:
        pe.last_OD_values = read_last_OD_values(pe)
    except FileNotFoundError:
        pe.last_OD_values = [0]*7
    print("Last OD values: ", dict(zip(range(7), pe.last_OD_values)))

    try:
        pe.drug_doses = read_parameters_drug_doses(pe)
    except FileNotFoundError:
        write_parameters_drug_doses(pe)
        read_parameters_drug_doses(pe)
    print("Death doses:", dict(zip(range(7), pe.drug_doses)))

    try:
        pe.OD_thresholds = read_parameters_OD_thresholds(pe)
    except FileNotFoundError:
        write_parameters_OD_thresholds(pe)
        read_parameters_OD_thresholds(pe)
    print("OD thresholds:", dict(zip(range(7), pe.OD_thresholds)))

    schedule.clear()
    schedule.every().minute.at(":00").do(pause_mixers_and_measure_OD, pe, mixer_speed=pe.mixer_speed)
    schedule.every().minute.at(":15").do(dilute_if_necessary, pe, total_dilution_volume=pe.total_dilution_volume)

    stop_file = os.path.join(pe.name, "stop.experiment")
    while not os.path.exists(stop_file):
        schedule.run_pending()
        time.sleep(1)
    os.remove(stop_file)
    print("Experiment stopped")
