import random
import math
import csv
import shutil
from tempfile import NamedTemporaryFile
import pandas as pd
import numpy as np
from numpy import array
import csv
from random import randint
import random
import math
from copy import deepcopy
import time
import xlrd
import openpyxl




def LoadData(x, name):

    data = pd.read_csv(name + '_train.csv', names=None)

    return data

def SelectDescrip(x, first_des):

    randes = randint(first_des, x - 1)

    return randes

def Init_Weight(weight):

    weight = 0

    weight = random.uniform(-5, 5)

    return weight

def Init_DesMultipleWeight(data,dataShape):

    datacopy = deepcopy(data)

    temp = pd.DataFrame

    des = 0

    w = 0

    des = (SelectDescrip(dataShape, first_des))

    w = Init_Weight(w)

    temp = (datacopy.iloc[:, des])*w

    return temp

def TheChosenDes(data, dataShape, numDes,first_des):

    chosen_list = pd.DataFrame(data.iloc[:, 0:first_des])

    comp_number = 0

    i = 0

    while i < numDes:

        temp = pd.DataFrame(Init_DesMultipleWeight(data, dataShape))

        comp_number = 0

        for j in chosen_list.columns:

            if temp.columns == j:
                comp_number = comp_number + 1

        if comp_number == 0:
            chosen_list = chosen_list.join(temp)

        elif comp_number > 0:
            i = i - 1

        i = i + 1

    return chosen_list

def Init_QSAR_Eq(chosen_list,first_des):

    copylist = deepcopy(chosen_list)

    qsar = []

    for i in range(copylist.shape[0]):
        qsar.append(sum(copylist.iloc[i, first_des:]))

    copylist['QSAR Eq'] = qsar

    copylist = copylist.sort_values(by='QSAR Eq', ascending=False)

    return copylist

def Number_of_Activity(number_of_act, chosen_list_act):

    number_of_act = 0

    for i in chosen_list_act:

        if i == 'a':
            number_of_act = number_of_act + 1

    return number_of_act

def Des_Mult_ChangeWeight(data_des,des):

    descopy = deepcopy(des)

    data_descopy = deepcopy(data_des)

    random_weight = random.uniform(-10, 10)

    descopy = descopy + data_des*random_weight

    return descopy

def Change_DesMultipleWeight(data,dataShape):

    datacopy = deepcopy(data)

    temp = pd.DataFrame

    des = 0

    w = 0

    des = (SelectDescrip(dataShape, first_des))

    w = Init_Weight(w)

    temp = (datacopy.iloc[:, des])*w

    title = datacopy.columns[des]

    return temp, title

def QSAR_Changing(chosen_list, first_des):

    cl_copy = deepcopy(chosen_list)

    col = cl_copy.shape[1]                                                 #number of columns

    for i in range(cl_copy.shape[0]):

        cl_copy.iloc[i, col - 1] = sum(cl_copy.iloc[i, first_des:(col - 1)])       #qsar column <--- sum of descriptors after changing

    cl_copy = cl_copy.sort_values(by='QSAR Eq', ascending=False)

    return cl_copy

def Activity(number_of_act, list):

    # Activity checking
    e = 0

    number_of_act_c = deepcopy(number_of_act)

    list_c = deepcopy(list)

    for i in range(number_of_act_c):
        if list_c.iloc[i] == 'a':
            e = e + 1

    return e

def Lacking_des(chosen_list, des_id):

    copylist = deepcopy(chosen_list)

    copylist.loc[:, "QSAR Eq"] = deepcopy(copylist.loc[:, "QSAR Eq"] - copylist.iloc[:, des_id])

    return copylist

def number_of_des_to_change(tot_des, change_des):

    if change_des > tot_des:
        return print("More than available descriptors were chosen to change")

    if change_des > 1:
        descrip_list = []
        numbers = [i for i in range(tot_des)]

        for i in range(change_des):
            gen_des = random.choice(numbers)
            descrip_list.append(gen_des)
            numbers.remove(gen_des)
    else:
        descrip_list = randint(1, change_des)

    return descrip_list

def Change_des(chosen_list, data, first_des):

    w = 0

    w = Init_Weight(w)

    data_c = deepcopy(data)

    data_titles = data_c.columns

    chosen_list_c = deepcopy(chosen_list)

    chosen_list_titles = chosen_list_c.columns

    chosen_list_titles = chosen_list_titles.drop('QSAR Eq')

    data_titles = data_titles.drop(chosen_list_titles)

    chosen_list_titles = chosen_list_titles.drop(chosen_list_titles[0:first_des])

    del_des = random.choice(chosen_list_titles)

    new_des = random.choice(data_titles)

    chosen_list_c.loc[:, "QSAR Eq"] = chosen_list_c.loc[:, "QSAR Eq"] - chosen_list_c.loc[:, del_des]

    chosen_list_c.loc[:, del_des] = data_c.loc[:, new_des]

    chosen_list_c.loc[:, del_des] = chosen_list_c.loc[:, del_des]*w

    chosen_list_c.rename({del_des: new_des}, axis=1, inplace=True)

    chosen_list_c.loc[:, "QSAR Eq"] = chosen_list_c.loc[:, "QSAR Eq"] + chosen_list_c.loc[:, new_des]

    return chosen_list_c

def Final_Weights(chosen_list_min,data, first_des):

    final_weights = pd.DataFrame()

    temp = pd.DataFrame()

    chosen_list_min_copy = deepcopy(chosen_list_min)

    final_weights = pd.DataFrame(columns=chosen_list_min_copy.columns)

    data_copy = deepcopy(data)

    i = 0

    for title in chosen_list_min_copy.columns:

        for tit in data_copy.columns:

            if title == tit and i >= first_des: # title != 'Title' and title != 'Activity':
                x = chosen_list_min_copy.loc[1, title]/data_copy.loc[1, tit]
                final_weights.loc[0, title] = x

        i+=1

    return final_weights

def test(final_weights, first_des, name):

    data_test = pd.read_csv(name + "_test.csv", names=None)

    final_weights_c = deepcopy(final_weights)

    test = pd.DataFrame(columns=final_weights_c.columns)

    test = test.drop('QSAR Eq', axis=1)

    i = 0                                   # the index of the descriptors. Was added in order to get the first descriptors and skip the 'Title', 'Activity', etc.

    for des in final_weights_c.columns:

        for tit in data_test.columns:

            if (des == tit and i < first_des):
                test.loc[:, des] = data_test.loc[:, tit]

            elif des == tit:# and des != 'Title' and des != 'Activity':
                test.loc[:,des] = data_test.loc[:, tit]*final_weights_c.loc[0, des]

        i+=1

    return test

def valid(final_weights, first_des, name):

    data_valid = pd.read_csv(name + "_validation.csv", names=None)

    final_weights_c = deepcopy(final_weights)

    valid = pd.DataFrame(columns=final_weights_c.columns)

    valid = valid.drop('QSAR Eq', axis=1)

    i = 0                                   # the index of the descriptors. Was added in order to get the first descriptors and skip the 'Title', 'Activity', etc.

    for des in final_weights_c.columns:

        for tit in data_valid.columns:

            if (des == tit and i < first_des):
                valid.loc[:, des] = data_valid.loc[:, tit]

            elif des == tit:
                valid.loc[:,des] = data_valid.loc[:, tit]*final_weights_c.loc[0, des]

        i+=1

    return valid
    
def Update_weights(numDes, des_to_change, chosen_list, data, first_des):

    numDes_c = deepcopy(numDes)

    des_to_change_c = deepcopy(des_to_change)

    chosen_list_c = deepcopy(chosen_list)

    data_c = deepcopy(data)

    list_to_change_c = number_of_des_to_change(numDes_c, des_to_change_c)

    for id_des in list_to_change_c:

        chosen_list_c = deepcopy(Lacking_des(chosen_list_c, id_des + first_des))

        des_name = chosen_list_c.columns[id_des + first_des]

        chosen_list_c.loc[:,des_name] = deepcopy(Des_Mult_ChangeWeight(data_c.loc[:,des_name], chosen_list_c.loc[:,des_name])) # changing the descriptor by adding the changing weight multiple the ORIGINAL descriptor into the current chosen descriptor

        chosen_list_c.loc[:,'QSAR Eq'] = deepcopy(chosen_list_c.loc[:,'QSAR Eq'] + chosen_list_c.loc[:, des_name])

    return chosen_list_c

def norm(indicies_ac, indicies_na):

    act = [i + 1 for i in range(indicies_ac)]
    avg = np.mean(act)
    sd = np.std(act)
    act = [(i - avg)/sd for i in act]

    na = [i + 1 for i in range(indicies_na)]
    avg = np.mean(na)
    sd = np.std(na)
    na = [(i - avg)/sd for i in na]

    temp = act + na
    return temp


def score(data_s, act, na):
    data_scor = pd.DataFrame()
    data_scor = deepcopy(data_s)

    scoring_func = 0
    scoring_func_2 = 0

    for i in range(act):

        if data_scor.loc[i, "Activity"] == 'naa':
            scoring_func -= data_scor.loc[i, "Score"]

    for j in range(act, na + act):

        if data_scor.loc[j, "Activity"] == 'a':
            scoring_func += data_scor.loc[j, "Score"]

    return scoring_func


start_time = time.time()

name = input('Name files: ')

file_name = input('Name files will be saved: ')

first_des = int(input('In which column is the first descriptor: '))

mc_steps = 1000000 #int(input('How many MC steps?: '))

first_temperature = 60 # int(input('The temperature starts at: '))

redu_temp = 10 #int(input('How many steps for reducing temperature? '))

change_des_point = 200 #int(input('How many steps to change descriptors: '))

temperatur = first_temperature

data = []

data = LoadData(data, name)

tot_des_num = data.shape[1]

number_of_act = 0

numDes = input('How many descriptors: ')

des_to_change =  input('How many descriptors to change: ')

chosen_list = TheChosenDes(data, tot_des_num, numDes, first_des)

number_of_act = Number_of_Activity(number_of_act, chosen_list.iloc[:, 1])#Chacking how many actives

chosen_list = Init_QSAR_Eq(chosen_list, first_des)

e = Activity(number_of_act, chosen_list.loc[:,'Activity'])

chosen_list_shape = chosen_list.shape[1]

chosen_list_temp = deepcopy(chosen_list)

e = Activity(number_of_act, chosen_list.loc[:,'Activity'])

e = e*-1

e_test = e

temper_list = []

counter = 0

count = e

min1 = e*-1

temp_counter = 0

change_counter = 0

ar = []

min_set = pd.DataFrame()

steps_to_change_des = 0

solu_list = []

for i in range(mc_steps):

    rand = random.uniform(0, 1)



    if temperatur <  2:
        temperatur = first_temperature
        temp_counter = 0

    count = count + e

    steps_to_change_des += 1

    chosen_list_temp = deepcopy(Update_weights(numDes, des_to_change, chosen_list_temp, data, first_des))

    chosen_list_temp = chosen_list_temp.sort_values(by='QSAR Eq', ascending=False)

    e_test = Activity(number_of_act, chosen_list_temp.loc[:,'Activity'])

    if temp_counter == 10 and temperatur > 1:
        temperatur = temperatur - 1
        if temperatur <=0:
            temperatur = 1
        temp_counter = 0
        temper_list.append(temperatur)

    e_test = e_test*-1

    if e_test <= min1:

        if e_test < min1:
            solu_list[:] = []
            solu_list = [chosen_list_temp]

        elif e_test == min1:
            solu_list.append(chosen_list_temp)

        min1 = deepcopy(e_test)
        chosen_list_min = deepcopy(chosen_list_temp)
        change_counter = 0


    if e_test < e and i > 0:
        e = deepcopy(e_test)
        chosen_list = deepcopy(chosen_list_temp)
        counter += 1
        ar.append(counter / i)


    elif e_test >= e and i > 0:
        exp = (e_test-e)/(temperatur*0.01)
        if rand < math.e**(-exp):
            e = deepcopy(e_test)
            chosen_list = deepcopy(chosen_list_temp)
            counter += 1
            ar.append(counter/i)

    else:
        e_test = deepcopy(e)
        chosen_list_temp = deepcopy(chosen_list)

    temp_counter = temp_counter + 1


    if steps_to_change_des == change_des_point:
        chosen_list_temp = deepcopy(Change_des(chosen_list_min, data, first_des))
        chosen_list = deepcopy(chosen_list_temp)
        e = deepcopy(e_test)
        steps_to_change_des = 0



for i in range(len(solu_list)):
    dat = deepcopy(solu_list[i].reset_index(drop=True))

    data_score = pd.DataFrame()

    data_score["Score"] = norm(number_of_act, len(dat) - number_of_act)

    data_score = data_score.join(dat.loc[:, "Activity"])

    scoring_func = score(data_score, number_of_act, len(dat) - number_of_act)

    solu_list[i].loc[0, "Grade"] = scoring_func

    scoring_func = 0
 
for i in range(len(solu_list)):

    if i == 0:
        min = solu_list[i].loc[0,"Grade"]

        index_min = i
    elif solu_list[i].loc[0,"Grade"] < min:
        min = solu_list[i].loc[0,"Grade"]
        index_min = i


chosen_list_min = deepcopy(solu_list[index_min])


final_weights = pd.DataFrame()

final_weights = Final_Weights(chosen_list_min, data, first_des)

final_weights = final_weights.drop("Grade", axis=1)

data_valid = pd.DataFrame()

data_valid = valid(final_weights, first_des, name)

data_valid = Init_QSAR_Eq(data_valid, first_des)

data_test = pd.DataFrame()

data_test = test(final_weights, first_des, name)

data_test = Init_QSAR_Eq(data_test, first_des)

final_weights['Model'] = min1*-1

number_of_act_valid = 0

number_of_act_valid = Number_of_Activity(number_of_act_valid, data_valid.loc[:, 'Activity'])

number_of_act_test = 0

number_of_act_test = Number_of_Activity(number_of_act_test, data_test.loc[:, 'Activity'])

final_weights['Validation'] = Activity(number_of_act_valid, data_valid.loc[:, 'Activity'])

final_weights['Test'] = Activity(number_of_act_test,data_test.loc[:, 'Activity'])

final_weights['Number of Actives - validation'] = number_of_act_valid

final_weights['Number of Actives - Test'] = number_of_act_test

final_weights['Number of Actives - Model'] = number_of_act

final_weights['Number of descriptors'] = numDes

final_weights['MC steps'] = mc_steps

final_weights['Grade'] = min

final_weights['Temperature'] = first_temperature

final_weights['Time (hr)'] = (time.time() - start_time)/3600

final_weights = final_weights.drop(final_weights.columns[:first_des], axis=1)

final_weights = final_weights.drop("QSAR Eq", axis=1)



"""output files"""

final_weights.to_excel(file_name + "_weights_and_scores.xlsx", index=False)

chosen_list_min.to_excel(file_name + "_min.xlsx")

data.to_excel(file_name + "_data.xlsx")

chosen_list.to_excel(file_name + "_chosen.xlsx")

np.savetxt(file_name + "_AR.csv", ar, delimiter=",", fmt='%s')

data_valid.to_excel(file_name + "_validation.xlsx")

data_test.to_excel(file_name + "_test.xlsx")

