from mesa import Agent, Model
from mesa.time import RandomActivation
import random
import pandas as pd
import CovidAnalysis2DT_MultivariantRegressionClass as CDT
import numpy
import statistics
from statistics import mode
from scipy import stats as s
from scipy import stats
from collections import Counter
#from mesa.datacollection import DataCollector

#the strategy list is fixed. It means that all agents have all these strategies at each time but with different degrees.
list_strategy = ['Nothing', 'No_Lockdown', 'Social_Distancing', 'Soft_Lockdown', 'Hard_Lockdown']




class ABMAgent(Agent):
    """ An agent with fixed initial wealth."""
    def __init__(self, unique_id, model):
        super().__init__(unique_id, model)
        ##dynamic: variables
        self.cases_for_14_days_per100000 = random.randint(0, 2)      #random.randint(0,5)
        self.cases_for_14_days_per100000_initial = self.cases_for_14_days_per100000
        self.r_initial = 0.2 #random.uniform(1, 4) #when it is pandemic that R0>= 1, for covid we have 2.39 <=R0<=3.44 then I considered the range of 1,4
        self.r = self.r_initial
        self.r_accumulated = self.r_initial
        self.r_avg = self.r_initial

        #self.max_decrease_freedom = random.uniform(0.03, 0.09)
        #self.max_increase_unemployment = random.uniform(0.03, 0.05) #average based on real data for two weeks is 0.04
        #probability of values degrees, high uss (0), high csp(1)
        self.temp_choose_value = numpy.random.choice(numpy.arange(0, 2), p=[self.model.uss_high_prob, 1 - self.model.uss_high_prob])
        #print("self.model.uss_high_prob:", self.temp_choose_value, self.model.uss_high_prob)
        if self.temp_choose_value == 0:
            self.values_USS = random.uniform(0.5, 1)  # Universalism, Self-Direction, Stimulation
            print("self.values_USS:::", self.values_USS)
        if self.temp_choose_value == 1:
            self.values_USS = random.uniform(0, 0.5)  # Universalism, Self-Direction, Stimulation
        self.values_CSP = 1 - (self.values_USS)  # Conformity, Security, Power
        #print("cattttt", self.values_CSP, self.values_USS)
        self.values_USS_initial = self.values_USS
        self.values_CSP_initial = self.values_CSP

        #How many ticks they wait to change their values
        if self.values_USS_initial >= 0.8:
            self.ticks_need_to_change_values = 1
        if 0.5 <= self.values_USS_initial < 0.8:
            self.ticks_need_to_change_values = 2
        if 0.5 < self.values_CSP_initial < 0.8:
            self.ticks_need_to_change_values = 4
        if self.values_CSP_initial >= 0.8:
            self.ticks_need_to_change_values = 8
        #print("self.values_USS_initial", self.values_USS_initial, "self.ticks_need_to_change_values::::", self.ticks_need_to_change_values)


        #Neww
        self.unemployment_rate = random.randint(0, 30)  # based on the min and max of all countries real data
        self.unemployment_rate_initial = self.unemployment_rate
        self.freedom_rate = random.randint(40, 90)
        self.freedom_rate_initial = self.freedom_rate
        self.unemployment_threshold = self.unemployment_rate_initial + random.uniform(0,
                                                                                      10)  # Return a random integer N such that a <= N <= b
        self.unemployment_threshold_initial = self.unemployment_threshold
        self.freedom_threshold = self.freedom_rate - random.uniform(0, 30)
        self.freedom_threshold_initial = self.freedom_threshold

        # New2
        self.max_decrease_freedom = random.uniform(1.4, 4.2)#self.freedom_rate_initial
        self.max_increase_unemployment = random.uniform(1.2, 2)#self.unemployment_rate_initial

        self.need_to_change_strategy = 0
        self.need_to_change_strategy_ini = 0
        self.change_values_flag = 0

        #how much they can change their values
        if self.values_USS_initial >= 0.5:
            self.change_value_degree_max = max(self.values_USS_initial, self.values_CSP_initial)/2
            self.change_value_degree_min = -1
            self.change_value_degree_max_initial = self.change_value_degree_max
            self.change_value_degree_min_initial = self.change_value_degree_min
            #self.change_value_degree_max = random.uniform(0, self.temp_change_value_degree_max)
        if self.values_CSP_initial > 0.5:
            self.change_value_degree_min = (min(self.values_USS_initial, self.values_CSP_initial))/2
            self.change_value_degree_max = -1
            #self.change_value_degree_min = random.uniform(0, self.temp_change_value_degree_min)
            self.change_value_degree_max_initial = self.change_value_degree_max
            self.change_value_degree_min_initial = self.change_value_degree_min

            #how many times he changes their values
        self.counter = 0


        ##fixed: parameters
        #self.innovation_rate_agent = random.uniform(0, 1)  # how much one agent is innovative
        self.cases_for_14_days_per100000_threshold = random.randint(30, 60) # (50, 100)
        #REGION_Africa,REGION_Americas,REGION_Asia,REGION_Europe,REGION_Middle east,REGION_Pacific
        self.region = numpy.random.choice(numpy.arange(0, 6), p=[0.3, 0.2, 0.1, 0.2, 0.1, 0.1]) #random.randint(0, 5), based on acaps number of countries distributed in regions
        #self.max_increase_cases = random.randint(25, 55)    #how many cases increase (max) per 2 weeks per country
        #self.recovery_rate = random.randint(15, 45)     #how many cases the country can recover per 2 weeks

        #self.resistance_to_change = random.uniform(0, 1) #whether they accept to change their values easily or not, use USS instead of this

        self.init_strategy_dict = {'Nothing': 1, 'No_Lockdown': 0, 'Social_Distancing': 0, 'Soft_Lockdown': 0, 'Hard_Lockdown': 0}
        #self.current_dict = {'Nothing': self.rand_act, 'No_Lockdown': self.rand_condtn, 'Social_Distancing': self.rand_act, 'Soft_Lockdown': self.rand_condtn, 'Hard_Lockdown': self.rand_condtn}  # agent's strategy, should have 4 elements


        #ML (0) or Copy (1)
        self.if_ML = numpy.random.choice(numpy.arange(0, 2), p=[self.model.how_much_ML, 1 - self.model.how_much_ML])
        #In coping: how much copy(0), how much changing(1)
        self.confidence = numpy.random.choice(numpy.arange(0, 2), p=[self.model.how_much_copy, 1 - self.model.how_much_copy])
        #probabilities are based on the real data
        self.cluster = numpy.random.choice(numpy.arange(0, 5), p=[0.1, 0.2, 0.2, 0.4, 0.1])
        #clustering: when they start to have strategies
        if self.cluster == 3:
            self.start_week_bin = 1
        if self.cluster == 4:
            self.start_week_bin = 1#2
        if self.cluster == 2:
            self.start_week_bin = 1#4
        if self.cluster == 1:
            self.start_week_bin = 1#5
        if self.cluster == 0:
            self.start_week_bin = 1#3

        self.cases_fail = 0
        self.unemployment_fail = 0
        self.freedom_fail = 0




    def step(self):
        # The agent's step will go here.
        # For demonstration purposes we will print the agent's unique_id
        print ("Hi, I am agent " + str(self.unique_id) + " region " + str(self.region) + ". " + " The tick is: " + str(self.model.stepcounter) + " cases_for_14_days_per100000: " + str(round(self.cases_for_14_days_per100000, 2)) + " R_initial: " + str(round(self.r, 2)) +" unemployment_rate: " + str(round(self.unemployment_rate, 2)) +  " freedom_rate: " + str(round(self.freedom_rate, 2)) + " values_USS: " + str(round(self.values_USS, 2)) + " self.values_CSP: " + str(round(self.values_CSP, 2)) + " cases_for_14_days_per100000_threshold: " + str(round(self.cases_for_14_days_per100000_threshold, 2)) + " unemployment_threshold: " + str(round(self.unemployment_threshold, 2)) + " freedom_threshold: " + str(round(self.freedom_threshold, 2)))
        print("self.counter:", self.counter)
        temp_enter = 0



        # an agent that the agent following it
        if self.model.stepcounter == 0:
            for agent_temp in self.model.schedule.agents:
                # not to choose the own agent (that is running) as the agent to follow
                if self.unique_id != agent_temp.unique_id:
                    if self.model.uss_high_prob <= 0.6:
                        if abs(agent_temp.values_USS - self.values_USS) <= 0.2:  # 0.1
                            self.agent_follow = agent_temp
                        if abs(agent_temp.values_USS - self.values_USS) > 0.2 and abs(agent_temp.values_USS - self.values_USS) <= 0.3:
                            self.agent_follow = agent_temp
                        if abs(agent_temp.values_USS - self.values_USS) > 0.3 and abs(agent_temp.values_USS - self.values_USS) <= 0.4:
                            self.agent_follow = agent_temp
                            #print("I " + str(self.unique_id) + "should follow " + str(self.agent_follow.unique_id))
                            break
                    if self.model.uss_high_prob > 0.6:
                        if abs(agent_temp.values_USS - self.values_USS) <= 0.2:  # 0.1
                            self.agent_follow = agent_temp
                        if abs(agent_temp.values_USS - self.values_USS) > 0.2 and abs(
                                agent_temp.values_USS - self.values_USS) <= 0.3:
                            self.agent_follow = agent_temp
                        if abs(agent_temp.values_USS - self.values_USS) > 0.3 and abs(
                                agent_temp.values_USS - self.values_USS) <= 0.5:
                            self.agent_follow = agent_temp
                            #print("I " + str(self.unique_id) + "should follow " + str(self.agent_follow.unique_id))
                            break
            #if it is null!

        #saving parameters per tick
        if self.model.list_tick_save_parameters[-1] != self.model.stepcounter:
            temp_sum_strategies_0 = self.return_agents_sum_strategies()
            self.model.sum_shared_strategy_per_tick.append(round(temp_sum_strategies_0, 2))  # list of shared strategies
            temp_mod_strategies_0 = self.return_agents_mod_shared_strategies()
            self.model.mod_shared_strategy_per_tick.append(temp_mod_strategies_0)  # list of shared strategies,mod
            temp_USS_change_0 = self.return_agents_count_USS_values_when_emerge_institutions()  # list of (sum of) values
            self.model.sum_USS_per_tick.append(round(temp_USS_change_0, 2))
            temp_CSP_change_0 = self.return_agents_count_CSP_values_when_emerge_institutions()  # list of (sum of) values
            self.model.sum_CSP_per_tick.append(round(temp_CSP_change_0, 2))
            self.model.list_tick_save_parameters.append(self.model.stepcounter)  # each tick
            print("self.model.sum_USS:", self.model.sum_USS_per_tick)
            #per agents
            temp_strategies_0 = self.return_agents_strategies()
            self.model.strategy_per_tick.append(temp_strategies_0)  #list agents' strategies per ticks
            temp_USS_per_tick_0 = self. return_agents_USS_per_ticks() #list of values per agent per tick
            self.model.USS_per_tick.append(temp_USS_per_tick_0)

            temp_cases_per_tick_0 = self.return_agents_cases_per_ticks()  # list of values per agent per tick
            self.model.cases_per_tick.append(temp_cases_per_tick_0)
            temp_unemployment_per_tick_0 = self.return_agents_unemployment_rate_per_ticks()  # list of values per agent per tick
            self.model.unemployment_per_tick.append(temp_unemployment_per_tick_0)
            temp_freedom_per_tick_0 = self.return_agents_freedom_per_ticks()  # list of values per agent per tick
            self.model.freedom_per_tick.append(temp_freedom_per_tick_0)
            temp_cases_threshold_per_tick_0 = self.return_agents_cases_threshold_per_ticks()  # list of values per agent per tick
            self.model.cases_threshold_per_tick.append(temp_cases_threshold_per_tick_0)
            temp_unemployment_threshold_per_tick_0 = self.return_agents_unemployment_threshold_per_ticks()  # list of values per agent per tick
            self.model.unemployment_threshold_per_tick.append(temp_unemployment_threshold_per_tick_0)
            temp_freedom_threshold_per_tick_0 = self.return_agents_freedom_threshold_per_ticks()  # list of values per agent per tick
            self.model.freedom_threshold_per_tick.append(temp_freedom_threshold_per_tick_0)
            temp_r_per_tick_0 = self.return_agents_r_per_ticks()  # list of values per agent per tick
            self.model.r_per_tick.append(temp_r_per_tick_0)
            temp_unhappy_per_tick_0 = self.return_agents_unhappy_per_ticks()  # how many agents are not happy
            self.model.unhappy_per_tick.append(temp_unhappy_per_tick_0)
            temp_unhappy_cases_per_tick_0 = self.return_agents_unhappy_cases_per_ticks()  # how many agents are not happy (cases failed)
            self.model.fail_cases_per_tick.append(temp_unhappy_cases_per_tick_0)
            temp_unhappy_unem_per_tick_0 = self.return_agents_unhappy_unemployment_per_ticks()  # how many agents are not happy (cases failed)
            self.model.fail_unemployment_per_tick.append(temp_unhappy_unem_per_tick_0)
            temp_unhappy_free_per_tick_0 = self.return_agents_unhappy_freedom_per_ticks()  # how many agents are not happy (cases failed)
            self.model.fail_freedom_per_tick.append(temp_unhappy_free_per_tick_0)
            temp_sum_NoL_per_tick_0 = self.return_sum_NoLockdown_per_ticks()  # sum of NOLockdown per tick
            self.model.sum_NoLockdown_per_tick.append(temp_sum_NoL_per_tick_0)
            temp_sum_SD_per_tick_0 = self.return_sum_SocialDistancing_per_ticks()  # sum of SocialDistancing per tick
            self.model.sum_SocialDistancing_per_tick.append(temp_sum_SD_per_tick_0)
            temp_sum_SL_per_tick_0 = self.return_sum_SoftLockdown_per_ticks()  # sum of SoftLockdown per tick
            self.model.sum_SoftLockdown_per_tick.append(temp_sum_SL_per_tick_0)
            temp_sum_HL_per_tick_0 = self.return_sum_HardLockdown_per_ticks()  # sum of HardLockdown per tick
            self.model.sum_HardLockdown_per_tick.append(temp_sum_HL_per_tick_0)
            temp_actual_value_change_per_tick_0 = self.return_sum_actual_value_change_per_ticks()  # sum of HardLockdown per tick
            self.model.number_of_actual_value_changes_tick.append(temp_actual_value_change_per_tick_0)




            #first institution check, then strategy change (it was vise versa first)
        # defining_institution

        if (((self.model.stepcounter + 1) % self.model.institutional_emergence_time) == 0) and (
                self.model.list_tick_emergeinstitution[
                    -1] != self.model.stepcounter):  # then one time for each tick this part will be done (not for each agent)

            Num_act = len(list_strategy)
            Num_con = self.model.num_agents

            count_item2 = [[] for i in range(Num_con)]

            for i in range(Num_con):
                for j in range(Num_act):
                    count_item2[i].append(0)
            # print(count_item2)

            for agent in self.model.schedule.agents:
                # print("agenttt", agent.init_strategy_dict['Nothing'])
                count_item2[int(agent.unique_id)][0] = agent.init_strategy_dict['Nothing']
                count_item2[int(agent.unique_id)][1] = agent.init_strategy_dict['No_Lockdown']
                count_item2[int(agent.unique_id)][2] = agent.init_strategy_dict['Social_Distancing']
                count_item2[int(agent.unique_id)][3] = agent.init_strategy_dict['Soft_Lockdown']
                count_item2[int(agent.unique_id)][4] = agent.init_strategy_dict['Hard_Lockdown']
                # print("count_item2[agent.unique_id]", count_item2[int(agent.unique_id)][0])

            # df = pd.DataFrame([count_item2])
            # df.columns = ['Nothing', 'No_Lockdown', 'Social_Distancing', 'Soft_Lockdown', 'Hard_Lockdown']
            df = pd.DataFrame(count_item2,
                              columns=list('NOSFH'))  # changing list to df, NOSFH insteads of strategies

            # using sum of values for each column
            new_instititution = {'Nothing': df['N'].sum(),
                                 'No_Lockdown': df['O'].sum(),
                                 'Social_Distancing': df['S'].sum(),
                                 'Soft_Lockdown': df['F'].sum(),
                                 'Hard_Lockdown': df['H'].sum()}
            #New
            #self.model.mod_shared_strategy_per_tick.append(df['O'].mod()* 1 + df['S'].mod()* 10 + df['F'].mod()*100 + df['H'].mod()*1000)
            # self.model.institution_stablishment_stepcounter = self.model.stepcounter
            # print("institution_stablishment_stepcounter:", self.model.institution_stablishment_stepcounter)
            self.model.list_institutions.append(new_instititution)  # list of institutions
            self.model.list_tick_emergeinstitution.append(
                self.model.stepcounter)  # #the ticks when institutions define
            print("emerge time:", self.model.stepcounter)
            print("list_institutions:", self.model.list_institutions)
            temp_USS_change = self.return_agents_count_USS_values_when_emerge_institutions()
            temp_CSP_change = self.return_agents_count_CSP_values_when_emerge_institutions()
            self.model.sum_USS.append(temp_USS_change)
            self.model.sum_CSP.append(temp_CSP_change)
            print("self.model.sum_USS:", self.model.sum_USS)
            print("self.model.sum_CSP:", self.model.sum_CSP)



        #call well_being()       #1
        self.need_to_change_strategy = self.well_being(self.values_USS, self.values_CSP, self.cases_for_14_days_per100000, self.unemployment_rate, self.freedom_rate, self.cases_for_14_days_per100000_threshold, self.unemployment_threshold, self.freedom_threshold)
        print("self.need_to_change_strategy " + str(self.need_to_change_strategy) + "self.unemployment_rate " + str(self.unemployment_rate))
        print("agent " + str(self.unique_id) + str(self.need_to_change_strategy))
        if self.need_to_change_strategy == 0:
            self.init_strategy_dict['No_Lockdown'] = self.init_strategy_dict['No_Lockdown'] * random.uniform(0.8, 1)
            self.init_strategy_dict['Social_Distancing'] = self.init_strategy_dict['Social_Distancing'] * random.uniform(0.8, 1)
            self.init_strategy_dict['Soft_Lockdown']= self.init_strategy_dict['Soft_Lockdown'] * random.uniform(0.8, 1)
            self.init_strategy_dict['Hard_Lockdown'] = self.init_strategy_dict['Hard_Lockdown'] * random.uniform(0.8, 1)
            if (self.init_strategy_dict['No_Lockdown'] == 0 and self.init_strategy_dict['Social_Distancing'] == 0 and
                    self.init_strategy_dict['Soft_Lockdown'] == 0 and self.init_strategy_dict['Hard_Lockdown'] == 0):
                self.init_strategy_dict['Nothing'] = 1
                print("agent " + str(self.unique_id) + " new strategy " + str(self.init_strategy_dict))
            else:
                self.init_strategy_dict['Nothing'] = 0
        if self.need_to_change_strategy == 1:
            self.change_values_flag = self.change_values_flag + 1   #it means the well being is not good (it is dependent and doesnt relate to strategy)
            print("changing strategyyyyy!!!")
            # call changing_strategy      #2
            #filling region
            #if self.ticks_need_to_change_values == self.change_values_flag:
            if self.region == 0:
                self.init_strategy_dict['No_Lockdown'], self.init_strategy_dict['Social_Distancing'], self.init_strategy_dict['Soft_Lockdown'], self.init_strategy_dict['Hard_Lockdown'] = self.changing_strategy(self.model.stepcounter, self.cases_for_14_days_per100000, 1, 0, 0, 0, 0, 0, self.confidence)
            if self.region == 1:
                self.init_strategy_dict['No_Lockdown'], self.init_strategy_dict['Social_Distancing'], self.init_strategy_dict['Soft_Lockdown'], self.init_strategy_dict['Hard_Lockdown'] = self.changing_strategy(self.model.stepcounter, self.cases_for_14_days_per100000, 0, 1, 0, 0, 0, 0, self.confidence)
            if self.region == 2:
                self.init_strategy_dict['No_Lockdown'], self.init_strategy_dict['Social_Distancing'], self.init_strategy_dict['Soft_Lockdown'], self.init_strategy_dict['Hard_Lockdown'] = self.changing_strategy(self.model.stepcounter, self.cases_for_14_days_per100000, 0, 0, 1, 0, 0, 0, self.confidence)
            if self.region == 3:
                self.init_strategy_dict['No_Lockdown'], self.init_strategy_dict['Social_Distancing'], self.init_strategy_dict['Soft_Lockdown'], self.init_strategy_dict['Hard_Lockdown'] = self.changing_strategy(self.model.stepcounter, self.cases_for_14_days_per100000, 0, 0, 0, 1, 0, 0, self.confidence)
            if self.region == 4:
                self.init_strategy_dict['No_Lockdown'], self.init_strategy_dict['Social_Distancing'], self.init_strategy_dict['Soft_Lockdown'], self.init_strategy_dict['Hard_Lockdown'] = self.changing_strategy(self.model.stepcounter, self.cases_for_14_days_per100000, 0, 0, 0, 0, 1, 0, self.confidence)
            if self.region == 5:
                self.init_strategy_dict['No_Lockdown'], self.init_strategy_dict['Social_Distancing'], self.init_strategy_dict['Soft_Lockdown'], self.init_strategy_dict['Hard_Lockdown'] = self.changing_strategy(self.model.stepcounter, self.cases_for_14_days_per100000, 0, 0, 0, 0, 0, 1, self.confidence)

            if (self.init_strategy_dict['No_Lockdown'] == 0 and self.init_strategy_dict['Social_Distancing']==0 and self.init_strategy_dict['Soft_Lockdown']==0 and self.init_strategy_dict['Hard_Lockdown']==0):
                self.init_strategy_dict['Nothing'] = 1
                print("agent " + str(self.unique_id) + " new strategy " + str(self.init_strategy_dict))
            else:
                self.init_strategy_dict['Nothing'] = 0
                print("agent " + str(self.unique_id) + " new strategy " + str(self.init_strategy_dict))
            self.need_to_change_strategy_ini = self.need_to_change_strategy
            self.need_to_change_strategy = 0

            # changing_values, using USS as openess to change instead of resistance to change parameter
            if self.ticks_need_to_change_values == self.change_values_flag:
                #if self.change_values_flag == 1: #they can tolerate for 2 ticks (1 month), if the strategy changes for two times in a row, they change their values
                self.counter = 1
                self.values_USS, self.values_CSP, self.change_value_degree_max, self.change_value_degree_min, self.unemployment_threshold, self.freedom_threshold = self.changing_values(self.values_USS, self.values_CSP, self.values_USS_initial, self.values_CSP_initial, self.change_value_degree_max, self.change_value_degree_min, self.change_value_degree_max_initial, self.change_value_degree_min_initial, self.unemployment_threshold, self.freedom_threshold)
                self.change_values_flag = 0 #reset the flag
                temp_enter = 1
            if self.ticks_need_to_change_values != self.change_values_flag and temp_enter == 0:
                self.counter = 0


        #updating parameters (if change strategy or not)
        self.cases_previous_tick = self.cases_for_14_days_per100000
        print("self.cases_previous_tick", self.cases_previous_tick)
        self.cases_for_14_days_per100000, self.unemployment_rate, self.freedom_rate = self.updating_parameters(self.init_strategy_dict['Nothing'], self.init_strategy_dict['No_Lockdown'], self.init_strategy_dict['Social_Distancing'], self.init_strategy_dict['Soft_Lockdown'], self.init_strategy_dict['Hard_Lockdown'], self.cases_for_14_days_per100000, self.unemployment_rate, self.freedom_rate)



    #I think it doesnt need to pass parameters of agents, bcz u passed the self
    def well_being(self, val_USS, val_CSP, cases_14_100000, unemployment_rate, freedom_rate, threshold_cases, threshold_unemployment, threshold_freedom):
        agent_need_to_change_strategy = 0
        if self.start_week_bin <= self.model.stepcounter + 1:
            ##New
            if unemployment_rate >= threshold_unemployment:
                agent_need_to_change_strategy = 1
                self.unemployment_fail = 1
                print("failunem")
            if unemployment_rate < threshold_unemployment:
                self.unemployment_fail = 0
            if freedom_rate <= threshold_freedom:
                agent_need_to_change_strategy = 1
                self.freedom_fail = 1
                print("free man", freedom_rate, "thre man", threshold_freedom, "free initial", self.freedom_rate_initial)
                print("failfre")
            if freedom_rate > threshold_freedom:
                self.freedom_fail = 0
            randomrange_temp = 1 #random.uniform(1, 2)
            if cases_14_100000 >= randomrange_temp * threshold_cases:  # threshold_cases * 2
                agent_need_to_change_strategy = 1
                self.cases_fail = 1
            if cases_14_100000 < randomrange_temp * threshold_cases:
                self.cases_fail = 0

        return agent_need_to_change_strategy

    def changing_strategy(self, step, cases, r1, r2, r3, r4, r5, r6, confidence):
        init_strategy_dict_temp = {'No_Lockdown': 0, 'Social_Distancing': 0, 'Soft_Lockdown': 0,
                              'Hard_Lockdown': 0}
        if self.model.just_ML == 0: #ML or copy
            if self.if_ML == 1: #copy
                init_strategy_dict_temp['No_Lockdown'] = self.agent_follow.init_strategy_dict['No_Lockdown']
                init_strategy_dict_temp['Social_Distancing'] = self.agent_follow.init_strategy_dict['Social_Distancing']
                init_strategy_dict_temp['Soft_Lockdown'] = self.agent_follow.init_strategy_dict['Soft_Lockdown']
                init_strategy_dict_temp['Hard_Lockdown'] = self.agent_follow.init_strategy_dict['Hard_Lockdown']
            if self.if_ML == 0: #ML
                #input_values = [2, 10000, 0, 0, 0, 0, 1, 0]
                input_values = [step+1, cases, r1, r2, r3, r4, r5, r6]
                prediction = CDT.DTRegression.regressor.predict([input_values])
                #print("prediction", prediction, prediction[0])  #prediction: [[0. 0. 1. 0.]], prediction[0]: [0. 0. 1. 0.]
                print("I used ML " + str(self.unique_id))
                prediction = [round(x, 2) for x in prediction[0]]
                #print("prediction::", prediction[2])
                init_strategy_dict_temp['No_Lockdown'] = prediction[0]
                init_strategy_dict_temp['Social_Distancing'] = prediction[1]
                init_strategy_dict_temp['Soft_Lockdown'] = prediction[2]
                init_strategy_dict_temp['Hard_Lockdown'] = prediction[3]
        if self.model.just_ML == 1:     #just ML
            #if confidence == False:
            #input_values = [2, 10000, 0, 0, 0, 0, 1, 0]
            input_values = [step+1, cases, r1, r2, r3, r4, r5, r6]
            prediction = CDT.DTRegression.regressor.predict([input_values])
            #print("prediction", prediction, prediction[0])  #prediction: [[0. 0. 1. 0.]], prediction[0]: [0. 0. 1. 0.]
            print("I used ML " + str(self.unique_id))
            prediction = [round(x, 2) for x in prediction[0]]
            #print("prediction::", prediction[2])
            init_strategy_dict_temp['No_Lockdown'] = prediction[0]
            init_strategy_dict_temp['Social_Distancing'] = prediction[1]
            init_strategy_dict_temp['Soft_Lockdown'] = prediction[2]
            init_strategy_dict_temp['Hard_Lockdown'] = prediction[3]
        if self.model.just_ML == 2: #ML or copy or change previous strategy
            if self.if_ML == 1:
                #Copy
                if self.confidence == 0:  #if temp_prob <= 2:  #50% probability, copy
                    init_strategy_dict_temp['No_Lockdown'] = self.agent_follow.init_strategy_dict['No_Lockdown']
                    init_strategy_dict_temp['Social_Distancing'] = self.agent_follow.init_strategy_dict['Social_Distancing']
                    init_strategy_dict_temp['Soft_Lockdown'] = self.agent_follow.init_strategy_dict['Soft_Lockdown']
                    init_strategy_dict_temp['Hard_Lockdown'] = self.agent_follow.init_strategy_dict['Hard_Lockdown']
                #Changing
                if self.confidence == 1:    #if temp_prob > 2:  #50% probability, increase the weight of one of strategies (randomly select)
                    temp_which_one = numpy.random.choice(numpy.arange(1, 5), p=[0.1, 0.2, 0.3, 0.4])  #hard most probable #random.randint(1,4)
                    #temp_which_one = random.randint(1,4)
                    #first fill with current strategy
                    init_strategy_dict_temp['No_Lockdown'] = self.init_strategy_dict['No_Lockdown']
                    init_strategy_dict_temp['Social_Distancing'] = self.init_strategy_dict['Social_Distancing']
                    init_strategy_dict_temp['Soft_Lockdown'] = self.init_strategy_dict['Soft_Lockdown']
                    init_strategy_dict_temp['Hard_Lockdown'] = self.init_strategy_dict['Hard_Lockdown']
                    #then change one of them to one
                    print("change strategy:", self.unique_id, init_strategy_dict_temp)

                    # New3
                    if (self.cases_for_14_days_per100000 < self.cases_for_14_days_per100000_threshold):
                        init_strategy_dict_temp['No_Lockdown'] = 0
                        init_strategy_dict_temp['Social_Distancing'] = 0
                        init_strategy_dict_temp['Soft_Lockdown'] = 0
                        init_strategy_dict_temp['Hard_Lockdown'] = 0
                    if (self.cases_for_14_days_per100000_threshold <= self.cases_for_14_days_per100000) and (
                            self.cases_for_14_days_per100000 < (1.5 * self.cases_for_14_days_per100000_threshold)):
                        init_strategy_dict_temp['No_Lockdown'] = 1
                    if (1.5 * self.cases_for_14_days_per100000_threshold <= self.cases_for_14_days_per100000) and (
                            self.cases_for_14_days_per100000 < (2 * self.cases_for_14_days_per100000_threshold)):
                        init_strategy_dict_temp['Social_Distancing'] = 1
                    if (2 * self.cases_for_14_days_per100000_threshold <= self.cases_for_14_days_per100000) and (
                            self.cases_for_14_days_per100000 < (2.5 * self.cases_for_14_days_per100000_threshold)):
                        init_strategy_dict_temp['Soft_Lockdown'] = 1
                    if (2.5 * self.cases_for_14_days_per100000_threshold <= self.cases_for_14_days_per100000):
                        init_strategy_dict_temp['Hard_Lockdown'] = 1

            if self.if_ML == 0:
                #input_values = [2, 10000, 0, 0, 0, 0, 1, 0]
                #week bin starts from 1
                input_values = [step+1, cases, r1, r2, r3, r4, r5, r6]
                prediction = CDT.DTRegression.regressor.predict([input_values])
                #print("prediction", prediction, prediction[0])  #prediction: [[0. 0. 1. 0.]], prediction[0]: [0. 0. 1. 0.]
                print("I used ML " + str(self.unique_id))
                prediction = [round(x, 2) for x in prediction[0]]
                #print("prediction::", prediction[2])
                init_strategy_dict_temp['No_Lockdown'] = prediction[0]
                init_strategy_dict_temp['Social_Distancing'] = prediction[1]
                init_strategy_dict_temp['Soft_Lockdown'] = prediction[2]
                init_strategy_dict_temp['Hard_Lockdown'] = prediction[3]
        return init_strategy_dict_temp['No_Lockdown'], init_strategy_dict_temp['Social_Distancing'], init_strategy_dict_temp['Soft_Lockdown'], init_strategy_dict_temp['Hard_Lockdown']


    def updating_parameters(self, degree1, degree2, degree3, degree4, degree5, cases_14_100000, unemployment_rate, free_rate):
        cases_temp = 0
        unemployment_temp = 0
        freedom_temp = 0
        #unemployment_rate = self.unemployment_rate_initial
        #free_rate = self.freedom_rate_initial
        max_increase_cases = 0
        #strategies are independent
        # by implementing rules, cases is decreasing.
        #first compute the cases that are still sick (they are death or recovered then nothing)

        if self.r < 1:
            max_increase_cases_choices = numpy.random.choice(numpy.arange(0, 2), p=[0.2, 0.8])
            if max_increase_cases_choices == 0:
                max_increase_cases = (cases_14_100000 * random.randint(1, 2))
                #since we look in 100000 population
                if max_increase_cases > 100000:
                    max_increase_cases = 100000
            if max_increase_cases_choices == 1:
                max_increase_cases = random.randint(0, 30)


        #self.r_initial = self.r_initial + random.uniform(0, 0.01)
        if self.r >= 1: ##start of the pandemic
            max_increase_cases = (cases_14_100000 * self.r) #probability of the infected people in that 100000
            if max_increase_cases > 100000:
                max_increase_cases = 100000

        #we use max(decreasing rates based on applying measures) to avoid haveing - cases (<0)
        impact_of_strategies_on_cases = ((max_increase_cases * ( - (0.1 * degree2) - (0.25 * degree3) - (0.44 * degree4) - (0.96 * degree5))))
        if abs(impact_of_strategies_on_cases) > max_increase_cases:
            impact_of_strategies_on_cases = - max_increase_cases

        cases_temp = max_increase_cases + ((1- degree1) * impact_of_strategies_on_cases) #to prevent be 0, bcz if cases is zero from the first, it always be 0

        #calculating r for next tick
        self.cases_this_tick = cases_temp

        self.r_temp = self.r
        if self.cases_previous_tick != 0:
            p_temp = random.uniform(0, 1)
            self.r_now = round(self.cases_this_tick / self.cases_previous_tick, 2)
            print("r_now", self.r_now, self.cases_this_tick, self.cases_previous_tick)
            self.r_accumulated = 0.2 * self.r_accumulated + 0.8 * self.r_now
            self.r_avg = self.r_accumulated / 2
            self.r = self.r_avg
            if self.r < 0:
                print("manfiii")
            if self.r > 100000:
                print("ziaaad", self.r_accumulated, self.r_avg)
        else:
            self.r_avg = self.r_accumulated #equals to r_initial
            self.r = self.r_avg


        #change the value to the initial each time, then calculate the new impact (since for strategies also we dont look at the past, each time we come up with totally new one and then forget it for the next strategy)
        unemployment_rate = self.unemployment_rate_initial
        #unemployment_temp = unemployment_rate + (degree1 * self.max_increase_unemployment ) + ((1 - degree1) * (((self.max_increase_unemployment * 0.25) * degree2) + ((self.max_increase_unemployment * 0.5) * degree3) + ((self.max_increase_unemployment * 0.75) * degree4) + ((self.max_increase_unemployment * 1) * degree5)))
        #if nothing implemented (degree1 = 1) it is as before
        unemployment_temp = unemployment_rate + ((1 - degree1) * (
                    ((self.max_increase_unemployment * 0.25) * degree2) + (
                        (self.max_increase_unemployment * 0.5) * degree3) + (
                                (self.max_increase_unemployment * 0.75) * degree4) + (
                                (self.max_increase_unemployment * 1) * degree5)))
        if unemployment_temp > 100:
            unemployment_temp = 100
        #by inplementing rules, it is decreasing. We assume that without implementing rules it is fix.
        # we use max(decreasing rates based on applying measures) to avoid haveing - free rate (<0)
        # change the value to the initial each time, then calculate the new impact (since for strategies also we dont look at the past, each time we come up with totally new one and then forget it for the next strategy)
        free_rate = self.freedom_rate_initial
        print("free_rate_aval", free_rate, "initial", self.freedom_rate_initial)
        #impact_of_strategies_on_freedom = (free_rate * (- (0.25 * degree2) - (0.35 * degree3) - (0.65 * degree4) - (0.90 * degree5)))
        impact_of_strategies_on_freedom = ((1 - degree1) * (
                    ((self.max_decrease_freedom * 0.25) * degree2) + (
                        (self.max_decrease_freedom * 0.35) * degree3) + (
                                (self.max_decrease_freedom * 0.65) * degree4) + (
                                (self.max_decrease_freedom * 0.90) * degree5)))
        if abs(impact_of_strategies_on_freedom) > free_rate:
            impact_of_strategies_on_freedom = free_rate
        freedom_temp = free_rate - ((1 - degree1) * impact_of_strategies_on_freedom)
        print("free_rate_bad", freedom_temp, "thres", self.freedom_threshold_initial)
        return cases_temp, unemployment_temp, freedom_temp

    def changing_values(self, val_USS, val_CSP, val_USS_ini, val_CSP_ini, val_degree_max, val_degree_min, val_degree_max_ini, val_degree_min_ini, threshold_unemployment, threshold_freedom):
        #self.counter = 1

        #it means uss initial high
        if (val_degree_max != -1):
            #New
            self.degree_to_change = random.uniform(0, val_degree_max)
            #till he can tolerate changes
            if val_degree_max >= self.degree_to_change: #0.1:
                val_USS = abs(val_USS - self.degree_to_change)  #0.1
                val_CSP = abs(val_CSP + self.degree_to_change)  #0.1
                threshold_unemployment = threshold_unemployment - 1 #0.1
                threshold_freedom = threshold_freedom + 1 #0.1
                val_degree_max = val_degree_max - self.degree_to_change #0.1
                print("valuedegreemax", val_degree_max)
            if val_degree_max < self.degree_to_change: # 0.1:
                if self.model.roll_back == 1:
                    print("rollbackkkkuss")
                    val_USS = val_USS_ini
                    val_CSP = val_CSP_ini
                    val_degree_max = val_degree_max_ini
                    threshold_unemployment = self.unemployment_threshold_initial
                    threshold_freedom = self.freedom_threshold_initial
        #it means csp initial high
        if (val_degree_min != -1):
            # till he can tolerate changes
            #New
            self.degree_to_change = val_degree_min
            if val_degree_min >= self.degree_to_change: #0.1:
                val_USS = abs(val_USS + self.degree_to_change)   #0.1 #it was 0.05
                val_CSP = abs(val_CSP - self.degree_to_change)   #0.1 #it was 0.05
                threshold_unemployment = threshold_unemployment + 1 #0.1
                threshold_freedom = threshold_freedom - 1 #0.1
                val_degree_min = val_degree_min - self.degree_to_change #0.1
            if val_degree_min < self.degree_to_change: #0.1:
                if self.model.roll_back == 1:
                    print("rollbackcsp")
                    val_CSP = val_CSP_ini
                    val_USS = val_USS_ini
                    val_degree_min = val_degree_min_ini
                    threshold_unemployment = self.unemployment_threshold_initial
                    threshold_freedom = self.freedom_threshold_initial
        return val_USS, val_CSP, val_degree_max, val_degree_min, threshold_unemployment, threshold_freedom

    def return_agents_count_USS_values_when_emerge_institutions(self):
        agent_values_USS = [agent.values_USS for agent in self.model.schedule.agents]

        #count_less_than_half = sum(map(lambda x: x < 0.5, agent_values_USS))
        #percentage_agents_with_half = count_less_than_half / self.model.num_agents
        sum_of_uss = sum(agent_values_USS)
        return sum_of_uss
    def return_agents_count_CSP_values_when_emerge_institutions(self):
        agent_values_CSP = [agent.values_CSP for agent in self.model.schedule.agents]

        #count_less_than_half = sum(map(lambda x: x < 0.5, agent_values_USS))
        #percentage_agents_with_half = count_less_than_half / self.model.num_agents
        sum_of_csp = sum(agent_values_CSP)
        return sum_of_csp
    def return_agents_USS_per_ticks(self):
        agent_values_USS = [round(agent.values_USS, 2) for agent in self.model.schedule.agents]
        return agent_values_USS

    def return_agents_cases_per_ticks(self):
        agent_cases = [round(agent.cases_for_14_days_per100000, 2) for agent in self.model.schedule.agents]
        return agent_cases
    def return_agents_unemployment_rate_per_ticks(self):
        agent_unemployment_rate = [round(agent.unemployment_rate, 2) for agent in self.model.schedule.agents]
        return agent_unemployment_rate
    def return_agents_freedom_per_ticks(self):
        agent_freedom = [round(agent.freedom_rate, 2) for agent in self.model.schedule.agents]
        return agent_freedom
    def return_agents_cases_threshold_per_ticks(self):
        agent_cases_threshold_per_ticks = [round(agent.cases_for_14_days_per100000_threshold, 2) for agent in self.model.schedule.agents]
        return agent_cases_threshold_per_ticks
    def return_agents_unemployment_threshold_per_ticks(self):
        agent_unemployment_threshold_per_ticks = [round(agent.unemployment_threshold, 2) for agent in self.model.schedule.agents]
        return agent_unemployment_threshold_per_ticks
    def return_agents_freedom_threshold_per_ticks(self):
        agent_freedom_threshold_per_ticks = [round(agent.freedom_threshold, 2) for agent in self.model.schedule.agents]
        return agent_freedom_threshold_per_ticks
    def return_agents_r_per_ticks(self):
        agent_r = [round(agent.r, 2) for agent in self.model.schedule.agents]
        return agent_r
    def return_agents_unhappy_per_ticks(self):
        agent_un = [round(agent.need_to_change_strategy_ini, 0) for agent in self.model.schedule.agents]
        agent_unhappy = sum(agent_un)
        print("agent_unhappy::", agent_unhappy)
        return agent_unhappy
    def return_agents_unhappy_cases_per_ticks(self):
        agent_un = [round(agent.cases_fail, 0) for agent in self.model.schedule.agents]
        agent_unhappy_cases = sum(agent_un)
        print("agent_unhappy_cases::", agent_unhappy_cases)
        return agent_unhappy_cases
    def return_agents_unhappy_unemployment_per_ticks(self):
        agent_un = [round(agent.unemployment_fail, 0) for agent in self.model.schedule.agents]
        agent_unhappy_unem = sum(agent_un)
        print("agent_unhappy_unem::", agent_unhappy_unem)
        return agent_unhappy_unem
    def return_agents_unhappy_freedom_per_ticks(self):
        agent_un = [round(agent.freedom_fail, 0) for agent in self.model.schedule.agents]
        agent_unhappy_freedom = sum(agent_un)
        print("agent_unhappy_freedom::", agent_unhappy_freedom)
        return agent_unhappy_freedom
    def return_sum_NoLockdown_per_ticks(self):
        agent_un = [round(agent.init_strategy_dict['No_Lockdown'], 2) for agent in self.model.schedule.agents]
        agent_sum_NoLockdown = sum(agent_un)
        return agent_sum_NoLockdown
    def return_sum_SocialDistancing_per_ticks(self):
        agent_un = [round(agent.init_strategy_dict['Social_Distancing'], 2) for agent in self.model.schedule.agents]
        agent_sum_SocialDistancing = sum(agent_un)
        return agent_sum_SocialDistancing
    def return_sum_SoftLockdown_per_ticks(self):
        agent_un = [round(agent.init_strategy_dict['Soft_Lockdown'], 2) for agent in self.model.schedule.agents]
        agent_sum_SoftLockdown = sum(agent_un)
        return agent_sum_SoftLockdown
    def return_sum_HardLockdown_per_ticks(self):
        agent_un = [round(agent.init_strategy_dict['Hard_Lockdown'], 2) for agent in self.model.schedule.agents]
        agent_sum_HardLockdown = sum(agent_un)
        return agent_sum_HardLockdown
    def return_sum_actual_value_change_per_ticks(self):
        agent_change = [agent.counter for agent in self.model.schedule.agents]
        agent_num_change = sum(agent_change)
        return agent_num_change


    def return_agents_count_CSP_values_when_emerge_institutions(self):
        agent_values_CSP = [agent.values_CSP for agent in self.model.schedule.agents]
        #count_less_than_half = sum(map(lambda x: x < 0.5, agent_values_USS))
        #percentage_agents_with_half = count_less_than_half / self.model.num_agents

        sum_of_csp = sum(agent_values_CSP)
        return sum_of_csp
    def return_agents_sum_strategies(self):
        agnets_NoLockdown = [agent.init_strategy_dict['No_Lockdown'] for agent in self.model.schedule.agents]
        agnets_Social_Distancing = [agent.init_strategy_dict['Social_Distancing'] for agent in self.model.schedule.agents]
        agnets_Soft_Lockdown = [agent.init_strategy_dict['Soft_Lockdown'] for agent in self.model.schedule.agents]
        agnets_Hard_Lockdown = [agent.init_strategy_dict['Hard_Lockdown'] for agent in self.model.schedule.agents]

        agnets_NoLockdown_sum = sum(agnets_NoLockdown)
        agnets_Social_Distancing_sum = sum(agnets_Social_Distancing)
        agnets_Soft_Lockdown_sum = sum(agnets_Soft_Lockdown)
        agnets_Hard_Lockdown_sum = sum(agnets_Hard_Lockdown)
        temp_last_shared_strategy_sum = (agnets_NoLockdown_sum * 1) + (
                                                    agnets_Social_Distancing_sum * 10) + (
                                                    agnets_Soft_Lockdown_sum * 100) + (
                                                    agnets_Hard_Lockdown_sum * 1000)

        return temp_last_shared_strategy_sum

    def return_agents_mod_shared_strategies(self):
        agnets_NoLockdown = [agent.init_strategy_dict['No_Lockdown'] for agent in self.model.schedule.agents]
        agnets_Social_Distancing = [agent.init_strategy_dict['Social_Distancing'] for agent in
                                    self.model.schedule.agents]
        agnets_Soft_Lockdown = [agent.init_strategy_dict['Soft_Lockdown'] for agent in self.model.schedule.agents]
        agnets_Hard_Lockdown = [agent.init_strategy_dict['Hard_Lockdown'] for agent in self.model.schedule.agents]
        #for real mod, do it
        #agnets_NoLockdown_sum = Counter(agnets_NoLockdown).most_common()[0][0]
        agnets_NoLockdown_sum = sum(agnets_NoLockdown)/self.model.num_agents
        agnets_Social_Distancing_sum = sum(agnets_Social_Distancing)/self.model.num_agents
        agnets_Soft_Lockdown_sum = sum(agnets_Soft_Lockdown)/self.model.num_agents
        agnets_Hard_Lockdown_sum = sum(agnets_Hard_Lockdown)/self.model.num_agents
        temp_last_shared_strategy_sum = (agnets_NoLockdown_sum * 1) + (
                agnets_Social_Distancing_sum * 10) + (
                                                agnets_Soft_Lockdown_sum * 100) + (
                                                agnets_Hard_Lockdown_sum * 1000)
        print("modd", temp_last_shared_strategy_sum)
        return temp_last_shared_strategy_sum
    def return_agents_strategies(self):
        temp_stra_sum = [round(((agent.init_strategy_dict['No_Lockdown']* 1) +(agent.init_strategy_dict['Social_Distancing'] * 10)+(agent.init_strategy_dict['Soft_Lockdown']*100)+(agent.init_strategy_dict['Hard_Lockdown']*1000)),2) for agent in self.model.schedule.agents]


        return temp_stra_sum


def return_list_institution(Model):
    return Model.list_institutions

def retunrn_sum_shared_strategy_per_tick(Model):
    return Model.sum_shared_strategy_per_tick
def mod_shared_strategy_per_tick_model(Model):
    return Model.mod_shared_strategy_per_tick

def return_strategy_per_tick(Model):
    return Model.strategy_per_tick

def retunrn_sum_USS_per_tick(Model):
    return Model.sum_USS_per_tick

def retunrn_sum_CSP_per_tick(Model):
    return Model.sum_CSP_per_tick

def return_USS_per_tick(Model):
    return Model.USS_per_tick
def return_cases_per_tick(Model):
    return Model.cases_per_tick
def return_unemployment_per_tick(Model):
    return Model.unemployment_per_tick
def return_freedom_per_tick(Model):
    return Model.freedom_per_tick
def return_ceses_thre_per_tick(Model):
    return Model.cases_threshold_per_tick
def return_unemployment_thre_per_tick(Model):
    return Model.unemployment_threshold_per_tick
def return_freedom_thre_per_tick(Model):
    return Model.freedom_threshold_per_tick
def return_r_per_tick(Model):
    return Model.r_per_tick
def return_unhappy_per_tick(Model):
    return Model.unhappy_per_tick
def return_unhappy_cases_per_tick(Model):
    return Model.fail_cases_per_tick
def return_unhappy_unemployment_per_tick(Model):
    return Model.fail_unemployment_per_tick
def return_unhappy_freedom_per_tick(Model):
    return Model.fail_freedom_per_tick
def return_sum_NoLockdown_per_ticks_model(Model):
    return Model.sum_NoLockdown_per_tick
def return_sum_SocialDistancing_per_ticks_model(Model):
    return Model.sum_SocialDistancing_per_tick
def return_sum_SoftLockdown_per_ticks_model(Model):
    return Model.sum_SoftLockdown_per_tick
def return_sum_HardLockdown_per_ticks_model(Model):
    return Model.sum_HardLockdown_per_tick
def return_number_actual_value_change_per_ticks_model(Model):
    return Model.number_of_actual_value_changes_tick

def return_region(Model):
    r_temp = [agent.region for agent in Model.schedule.agents]
    return r_temp

def return_list_tick_emergeinstitution(Model):
    return Model.list_tick_emergeinstitution

def return_sum_USS_last(Model):
    return Model.sum_USS[-1]

def return_sum_CSP_last(Model):
    return Model.sum_CSP[-1]

def return_sum_USS_half(Model):
    return Model.sum_USS[-2]

def return_sum_CSP_half(Model):
    return Model.sum_CSP[-2]

def sum_shared_strategy_last(Model):
    temp_last_shared_strategy = Model.list_institutions[-1]
    temp_last_shared_strategy_sum = (temp_last_shared_strategy['Nothing'] * 0) + (temp_last_shared_strategy['No_Lockdown'] * 1) + (temp_last_shared_strategy['Social_Distancing'] * 10) + (temp_last_shared_strategy['Soft_Lockdown'] *100) + (temp_last_shared_strategy['Hard_Lockdown']*1000)
    return temp_last_shared_strategy_sum

def sum_shared_strategy_half(Model):
    temp_last_shared_strategy = Model.list_institutions[-2]
    temp_last_shared_strategy_sum = (temp_last_shared_strategy['Nothing'] * 0) + (temp_last_shared_strategy['No_Lockdown'] * 1) + (temp_last_shared_strategy['Social_Distancing'] * 10) + (temp_last_shared_strategy['Soft_Lockdown'] *100) + (temp_last_shared_strategy['Hard_Lockdown']*1000)
    return temp_last_shared_strategy_sum

def shared_strategy_last_Nothing(Model):
    temp_last_shared_strategy = Model.list_institutions[-1]
    temp_last_shared_strategy_sum = temp_last_shared_strategy['Nothing']
    return temp_last_shared_strategy_sum

def shared_strategy_last_No_Lockdown(Model):
    temp_last_shared_strategy = Model.list_institutions[-1]
    temp_last_shared_strategy_sum = temp_last_shared_strategy['No_Lockdown']
    return temp_last_shared_strategy_sum

def shared_strategy_last_Social_Distancing(Model):
    temp_last_shared_strategy = Model.list_institutions[-1]
    temp_last_shared_strategy_sum = temp_last_shared_strategy['Social_Distancing']
    return temp_last_shared_strategy_sum

def shared_strategy_last_Soft_Lockdown(Model):
    temp_last_shared_strategy = Model.list_institutions[-1]
    temp_last_shared_strategy_sum = temp_last_shared_strategy['Soft_Lockdown']
    return temp_last_shared_strategy_sum

def shared_strategy_last_Hard_Lockdown(Model):
    temp_last_shared_strategy = Model.list_institutions[-1]
    temp_last_shared_strategy_sum = temp_last_shared_strategy['Hard_Lockdown']
    return temp_last_shared_strategy_sum

def return_sum_USS_initial(Model):
    agent_values_USS_initial = [agent.values_USS_initial for agent in Model.schedule.agents]
    agent_values_USS_initial_sum = sum(agent_values_USS_initial)
    return agent_values_USS_initial_sum

def return_sum_CSP_initial(Model):
    agent_values_CSP_initial = [agent.values_CSP_initial for agent in Model.schedule.agents]
    agent_values_CSP_initial= sum(agent_values_CSP_initial)
    return agent_values_CSP_initial

def return_scenario(Model):
    return Model.just_ML

def return_agents_specifications(Model):
    agent_values_USS = [agent.values_USS for agent in Model.schedule.agents]
    agent_values_CSP = [agent.values_CSP for agent in Model.schedule.agents]
    agent_values_USS_initial = [agent.values_USS_initial for agent in Model.schedule.agents]
    agent_values_CSP_initial = [agent.values_CSP_initial for agent in Model.schedule.agents]
    agent_cases_for_14_days_per100000 = [agent.cases_for_14_days_per100000 for agent in Model.schedule.agents]
    agent_cases_for_14_days_per100000_initial = [agent.cases_for_14_days_per100000_initial for agent in Model.schedule.agents]
    agent_unemployment_rate = [agent.unemployment_rate for agent in Model.schedule.agents]
    agent_unemployment_rate_initial = [agent.unemployment_rate_initial for agent in Model.schedule.agents]
    agent_freedom_rate = [agent.freedom_rate for agent in Model.schedule.agents]
    agent_freedom_rate_initial = [agent.freedom_rate_initial for agent in Model.schedule.agents]
    agent_recovery_rate =[agent.recovery_rate for agent in Model.schedule.agents]
    agent_innovation_rate_agent = [agent.innovation_rate_agent for agent in Model.schedule.agents]
    agent_cases_for_14_days_per100000_threshold = [agent.cases_for_14_days_per100000_threshold for agent in Model.schedule.agents]
    agent_unemployment_threshold= [agent.unemployment_threshold for agent in Model.schedule.agents]
    agent_unemployment_threshold_initial = [agent.unemployment_threshold_initial for agent in Model.schedule.agents]
    agent_freedom_threshold = [agent.freedom_threshold for agent in Model.schedule.agents]
    agent_freedom_threshold_initial = [agent.freedom_threshold_initial for agent in Model.schedule.agents]
    agent_resistance_to_change = [agent.resistance_to_change for agent in Model.schedule.agents]
    agent_max_increase_cases = [agent.max_increase_cases for agent in Model.schedule.agents]
    agent_region = [agent.region for agent in Model.schedule.agents]


    return agent_values_USS, agent_values_CSP, agent_values_USS_initial, agent_values_CSP_initial, agent_cases_for_14_days_per100000, agent_cases_for_14_days_per100000_initial, agent_unemployment_rate, agent_unemployment_rate_initial, agent_freedom_rate, agent_freedom_rate_initial, agent_recovery_rate, agent_innovation_rate_agent, agent_cases_for_14_days_per100000_threshold, agent_unemployment_threshold, agent_unemployment_threshold_initial, agent_freedom_threshold, agent_freedom_threshold_initial, agent_resistance_to_change, agent_max_increase_cases, agent_region



class ABMModel(Model):
    """A model with some number of agents."""
    def __init__(self, N, usshigh, M, C):
        self.num_agents = N
        self.uss_high_prob = usshigh

        self.stepcounter = 0  # calculate the tick
        self.schedule = RandomActivation(self)
        self.list_institutions = [{'Nothing': -1, 'No_Lockdown': -1, 'Social_Distancing': -1, 'Soft_Lockdown': -1, 'Hard_Lockdown': -1}]
        self.list_tick_emergeinstitution = [-1]
        self.sum_USS = [-1]
        self.sum_CSP = [-1]
        self.sum_shared_strategy = [-1]


        #in each tick
        self.sum_USS_per_tick = []
        self.sum_CSP_per_tick = []
        self.sum_shared_strategy_per_tick = []
        self.list_tick_save_parameters = [-1]
        self.mod_shared_strategy_per_tick = []
        #in each tick for all agents
        self.USS_per_tick = []
        self.strategy_per_tick = []
        self.cases_per_tick = []
        self.unemployment_per_tick = []
        self.freedom_per_tick = []
        self.cases_threshold_per_tick = []
        self.unemployment_threshold_per_tick = []
        self.freedom_threshold_per_tick = []
        self.r_per_tick = []
        self.unhappy_per_tick = []
        self.fail_cases_per_tick = []
        self.fail_unemployment_per_tick = []
        self.fail_freedom_per_tick = []
        self.number_of_actual_value_changes_tick = []
        self.sum_NoLockdown_per_tick = []
        self.sum_SocialDistancing_per_tick = []
        self.sum_SoftLockdown_per_tick = []
        self.sum_HardLockdown_per_tick = []



        self.institutional_emergence_time = 12 #100
        self.just_ML = 2   #0: ML and copy , 1: just ML, 2:ML, copy, change previous one
        self.how_much_ML = M   #how much ML/copy/change
        self.how_much_copy = C
        self.roll_back = 1 #roll back to stablish quo (1), wont roll back (0)

        # Create agents
        for i in range(self.num_agents):
            a = ABMAgent(i, self)
            self.schedule.add(a)
        self.running = True     #####should add this in order to use batch run



    def step(self):
        '''Advance the model by one step.'''
        while (self.stepcounter < 26):
            self.schedule.step()
            self.stepcounter += 1
            #print(self.energy)
            #self.datacollector.collect(self)
