import random
from time import sleep
import sys
from termcolor import colored
from getch import getch
import os

stolen_items = ['back','Thievin Tools']
item_values = [None,10]

possible_colors = ["white","grey","blue","cyan","green","yellow","magenta"]
player_color = "white"
player_attrs = None
health = 100
stamina = 100
wealth = 0

#Random first-time through checks and set-ups
color_change = False
color_change_check = False
first_time = True
Returned = False
Taeris_Talk = True
possible_colors = ["white","grey","blue","cyan","green","yellow","magenta"]

stolen_things = zip(stolen_items,item_values)
stolen_things = list(stolen_things)

#This isn't how you're meant to use classes. Buuuuuuuuuuut, Google is hard & this works well enough for my organisation itch.
class text:
  def basic(content,color,attributes,speed):
    if attributes != None:
      for x in colored(str(content),str(color),attrs=[str(attributes)]):
        sys.stdout.write(x)
        sys.stdout.flush()
        sleep(speed)
    else:
      for x in colored(str(content),str(color)):
        sys.stdout.write(x)
        sys.stdout.flush()
        sleep(speed)

  def taeris(content,speed):
    for x in colored(str(content),"red",attrs=["bold"]):
      sys.stdout.write(x)
      sys.stdout.flush()
      sleep(speed)

  def player(content,speed):
    if player_attrs != None:
      for x in colored(str(content).capitalize(),player_color,attrs=[player_attrs]):
        sys.stdout.write(x)
        sys.stdout.flush()
        sleep(speed)
    else:
      for x in colored(str(content).capitalize(),player_color):
        sys.stdout.write(x)
        sys.stdout.flush()
        sleep(speed)

  def waiting(time):
    for x in range(time):
      text.basic("...","white","bold",0.1)
      print("\r")
      text.delete_last_line()

  def title(content,speed):
    if player_attrs != None:
      for x in colored(str(content).upper(),player_color,attrs=[player_attrs]):
        sys.stdout.write(x)
        sys.stdout.flush()
        sleep(speed)
    else:
      for x in colored(str(content).upper(),player_color):
        sys.stdout.write(x)
        sys.stdout.flush()
        sleep(speed)

  def delete_last_line():
    #"Use this function to delete the last line in the STDOUT"

    #cursor up one line
    sys.stdout.write('\x1b[1A')

    #delete last line
    sys.stdout.write('\x1b[2K')
    sys.stdout.flush()

class items:

  def add(item,value):
    global stolen_things
    global stolen_items
    global stolen_values

    stolen_items.append(item)
    item_values.append(value)

    stolen_things = zip(stolen_items,item_values)
    stolen_things = list(stolen_things)

  def sell(item_number):
    global wealth
    item_number = int(item_number) - 1
    wealth = wealth + item_values[item_number]
    stolen_things.remove(stolen_things[item_number])

  def show():
    for x,res in enumerate(stolen_things,1):
      for x in str("(" + str(x) + ")" + " " + colored(str(res),player_color)):
        sys.stdout.write(x)
        sys.stdout.flush()
        sleep(0.05)
      for x in str(len(stolen_things)):
        print()

class stats:
  #using health as a death check & stamina as an event pass check
  global health
  global stamina
  class health_stat:
    def death_check():
      if health <= 0:
        os.system("clear")
        game_over()
      else:
        return
  class stamina_stat:
    def tire_check():
      if stamina < 25:
        print()
        text.basic("You're too tired to even attempt another robbery. You'll need to rest","white","bold",0.05)
        os.system("clear")
        game_main()
    def rest():
      global health
      global stamina
      os.system("clear")
      text.basic("You can kinda do this infinitely right now soooo... Honor System?","grey","bold",0.05)
      print()
      print()
      text.player("BRB, Sleeping",0.05)
      print()
      text.waiting(5)
      stamina = 100
      if health < 100:
        if health <= 20:
          health = health + 50
        elif health:
          health = health + 25

class tutorials:

  def health():
    global first_time
    text.taeris("Health is quite simply a measure of your physical wellbeing.",0.05)
    print()
    text.taeris("Should it reach 0, or you become damaged beyond your limits,",0.05)
    print()
    text.taeris("You will swiftly be delivered back to me and I shall deal with the fallout.",0.05)
    print()
    print()
    text.taeris("Anything else?",0.05)
    print()
    player_choices("Stamina","Wealth","Encounters","Nope")
    print()
    tutorial_choice = getch()

    if tutorial_choice == "1":
      os.system("clear")
      tutorials.stamina()
    elif tutorial_choice == "2":
      os.system("clear")
      tutorials.wealth()
    elif tutorial_choice == "3":
      os.system("clear")
      tutorials.encounters()
    elif tutorial_choice == "4":
      os.system("clear")
      text.taeris("Good, now get on with the daring robberies.",0.05)
      sleep(1)
      first_time = False
      game_main()

  def stamina():
    global first_time
    text.taeris("Stamina is a measure of how much you can exert yourself before you tire.",0.05)
    print()
    text.taeris("Should it reach 0, or you exert yourself too much,",0.05)
    print()
    text.taeris("You'll be returned back to the hideout\n and you shall have to rest before attempting any more thefts.",0.05)
    print()
    print()
    text.taeris("Anything else?",0.05)
    print()
    player_choices("Health","Wealth","Encounters","Nope")
    print()
    tutorial_choice = getch()

    if tutorial_choice == "1":
      os.system("clear")
      tutorials.health()
    elif tutorial_choice == "2":
      os.system("clear")
      tutorials.wealth()
    elif tutorial_choice == "3":
      os.system("clear")
      tutorials.encounters()
    elif tutorial_choice == "4":
      os.system("clear")
      text.taeris("Good, now get on with the daring robberies.",0.05)
      sleep(1)
      first_time = False
      game_main()

  def wealth():
    global first_time
    text.taeris("Wealth is quite self-explanatory, however,\n",0.05)
    text.taeris("It shouldn't be underestimated as it is the primary evidence of your place in our world.",0.05)
    print()
    text.taeris("Should you fail to incur enough, it will reflect badly on us all.",0.05)
    print()
    sleep(1)
    print()
    text.taeris("Anything else?",0.05)
    print()
    player_choices("Health","Stamina","Encounters","Nope")
    print()
    tutorial_choice = getch()

    if tutorial_choice == "1":
      os.system("clear")
      tutorials.health()
    elif tutorial_choice == "2":
      os.system("clear")
      tutorials.stamina()
    elif tutorial_choice == "3":
      os.system("clear")
      tutorials.encounters()
    elif tutorial_choice == "4":
      os.system("clear")
      text.taeris("Good, now get on with the daring robberies.",0.05)
      sleep(1)
      first_time = False
      game_main()

  def encounters():
    global first_time
    text.taeris("Encounters are your primary source of items to sell",0.05)
    print()
    text.taeris("It generally goes:\nYou find someone(or something) you want to steal from;\nYou choose a method to steal something from them\nand then you see if you can carry out your daring plan.",0.05)
    print()
    print()
    text.taeris("You may fail with your selected plan if you are too tired\n or incur too much damage during the operation",0.05)
    print()
    print()
    text.taeris("It should also be noted that it is often an option of great prevalence to just run. \n",0.05)
    text.taeris("I do not feel the need to explain how this would reflect badly on you and us.",0.05)
    print()
    sleep(3)
    print()
    text.taeris("Anything else?\n",0.05)
    print()
    player_choices("Health","Stamina","Wealth","Nope")
    print()
    tutorial_choice = getch()

    if tutorial_choice == "1":
      os.system("clear")
      tutorials.health()
    elif tutorial_choice == "2":
      os.system("clear")
      tutorials.stamina()
    elif tutorial_choice == "3":
      os.system("clear")
      tutorials.wealth()
    elif tutorial_choice == "4":
      os.system("clear")
      text.taeris("Good, now get on with the daring robberies.",0.05)
      sleep(1)
      first_time = False
      game_main()

class event_setup:

    def event_difficulty_check(difficulty):
      global stamina
      global health
      if str(difficulty).lower() == "easy":
        if stamina >= 25:
          stamina = stamina - 25
          return True
        elif stamina < 25:
          return False

      if str(difficulty).lower() == "medium":
        if stamina >= 50:
          stamina = stamina - 50
          return True
        elif stamina < 50:
          return False

      if str(difficulty).lower() == "hard":
        if stamina >= 75:
          stamina = stamina - 75
          return True
        elif stamina < 75:
          return False

    def event_prompt(content,color,attrs,speed):
      text.basic(content,color,attrs,speed)
      print()
      print()

    def event_choices(*choices):
      event_options = []
      for x in choices:
        event_options.append([str(x)])
      for x,res in enumerate(event_options,1):
        for x in str("(" + str(x) + ")" + " " + colored(str(res),player_color)):
          sys.stdout.write(x)
          sys.stdout.flush()
          sleep(0.05)
        for x in str(len(event_options)):
          print()

    def return_repeat():
      player_choices("continue searching?","return back to your stash?")
      n = getch()
      if n == "1":
        stats.stamina_stat.tire_check()
        events.event(random.randint(0,15))
      else:
        game_main()

    def event_outcome(content,difficulty,minus_health,item,value):
        global stolen_items
        global stolen_values
        global health
        random.seed(random.randint(0,15))
        text.player(content,0.05)
        print()
        text.waiting(3)
        stats.health_stat.death_check()
        print()
        event_pass = bool(event_setup.event_difficulty_check(difficulty))
        if event_pass == True and item != None:
          text.player("You succeed! and along the way you find a/an ",0.05)
          text.player(str(item),0.05)
          text.player("!",0.05)
          print()
          text.player("You estimate it to be worth about ",0.05)
          text.player(str(value),0.05)
          text.player(" universal moneys.",0.05)
          print()
          print()
          items.add(item,value)
          event_setup.return_repeat()
        elif event_setup.event_difficulty_check(difficulty) == True and item == None:
          text.player("You succeed! But,(sadly) can't find any items to grab.",0.05)
          print()
          print()
          event_setup.return_repeat()

        else:
          health = health - minus_health
          text.player("You fail (miserably),lose whatever items may have been gained and take some scuffs along the way.",0.05)
          stats.health_stat.death_check()
          print()
          print()
          event_setup.return_repeat()

class events:
  global health
  global stamina
  def event(event_id):
    os.system("clear")
    HUD()
    print()
    print()
    stats.health_stat.death_check()
    #Probably a much better way of doing this, I know.
    #----------------------------------Event(0)--------------------------------------#
    if event_id == 0:
      event_setup.event_prompt("suddenly, a DOG appears!","white","bold",0.05)

      event_setup.event_choices("attack!","RUN!","Pet it")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You rush forwards and attack the dog!","medium",50,"Dog Collar",5)
      elif event_choice == "2":
        event_setup.event_outcome("You run as fast as you can!","easy",10,None,1)
      elif event_choice == "3":
        event_setup.event_outcome("You approach it carefully in an attempt to russle its ears.","easy",10,"Dogs Collar",20)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(1)--------------------------------------#
    elif event_id == 1:
      event_setup.event_prompt("You find a random rich person walking along the street.","white","bold",0.05)

      event_setup.event_choices("try to pickpocket him","RUN")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You swiftly manoeuvre yourself into an encounter with the mans valuables and...","medium",20,"Smart-Looking Watch",80)
      elif event_choice == "2":
        event_setup.event_outcome("You just start booking it in a straight line away from the stranger!","easy",10,None,1)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(2)--------------------------------------#
    elif event_id == 2:
      event_setup.event_prompt("You're just minding your own business looking for something to steal when,\nsuddenly, some gangstah comes up and starts dissin' ya fly girl. Whatever shall you do?","white","bold",0.05)

      event_setup.event_choices("Give im' one a these: Brandish a firearm","Cower")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("The weapon kicks back hard and you struggle to keep it under control.","hard",50,"Empty Shotgun",50)
      elif event_choice == "2":
        event_setup.event_outcome("You cower pathetically and hope he passes you by","easy",40,None,1)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(3)--------------------------------------#
    elif event_id == 3:
      event_setup.event_prompt("You arrive outside a bank and size it up for a robbery.","white","bold",0.05)

      event_setup.event_choices("Sneak in around the back","befriend the faculty")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You slither around the bank to its rear like a snake and attempt to sneak in unnoticed.","hard",50,"Big Bag of Swag",160)
      elif event_choice == "2":
        event_setup.event_outcome("You swagger into the bank and attempt to charm its staff into looking the other way.","hard",30,"Expensive Necklace",80)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(4)--------------------------------------#
    elif event_id == 4:
      event_setup.event_prompt("You come across a proselytizing man in the street.","white","bold",0.05)

      event_setup.event_choices("Steal his donations","Convince him that you have seen the error of your ways")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You snatch up his cup of donations and begin dashing away!","medium",20,"Full Donation Cup",30)
      elif event_choice == "2":
        event_setup.event_outcome("You meagerly limp over and begin lecturing the man in return as to your own spiritual ascension in hopes that he'll give you something.","hard",10,"Holy Ornament",60)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(5)--------------------------------------#
    elif event_id == 5:
      event_setup.event_prompt("You're walking along a forest trail when you suddenly fall into the ground!\nYou appear to have fallen into a trap!","white","bold",0.05)

      event_setup.event_choices("Scramble out","Rummage around for secrets")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You deftly grasp at a tree branch and try to leverage yourself out of the hole.","easy",50,None,1)
      elif event_choice == "2":
        event_setup.event_outcome("You rumble around like a rodent when you come acroos a skeleton covered in gold!\nYou grab some of its jewellery and attempt to escape.","hard",70,"Various Doubloons",240)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(6)--------------------------------------#
    elif event_id == 6:
      event_setup.event_prompt("You're walking along the road when a glint hits the corner of your eye!\nThere appears to be a magpies stash of valuables hidden in one of the surrounding trees.","white","bold",0.05)

      event_setup.event_choices("Try to clamber up the tree and grab the nest","Leave it be")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You grab branches and haphazardly rush up the tree in hopes of stealing the birds fortune.","hard",50,"Magpies Fortune",140)
      elif event_choice == "2":
        event_setup.event_outcome("You attempt to abstain from the clear fortune awaiting you in the tree.","easy",10,"New-Found Self-Respect",0)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(7)--------------------------------------#
    elif event_id == 7:
      event_setup.event_prompt("You find yourself in a bar when a tough guy walks in and begins flashing his packed wallet to everybody.","white","bold",0.05)

      event_setup.event_choices("try to fight him","Drown your sorrows")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You saunter through the bar and berate his choice of hair gel.\n This enrages him and he launches across the bar towards you.","hard",70,"Bulky Wallet",200)
      elif event_choice == "2":
        event_setup.event_outcome("You attempt to find riches at the bottom of your pint.","easy",10,"Alcholism",0)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(8)--------------------------------------#
    elif event_id == 8:
      event_setup.event_prompt("Along your journey to riches,\nyou find yourself in a prestigous library during a talk being given by a learned man of great years.","white","bold",0.05)

      event_setup.event_choices("Outwit him","Steal his stuff")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You trek up the podium steps and begin attempting to mentally destroy this man in a war of wits.","hard",0,"Old Mans Pride",0)
      elif event_choice == "2":
        event_setup.event_outcome("You sprint up to the stage and steal his bag","easy",10,"Wizened Mans Bag",30)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(9)--------------------------------------#
    elif event_id == 9:
      event_setup.event_prompt("You're out at the seaside, relaxing away from the conventionalities of modern living.","white","bold",0.05)

      event_setup.event_choices("Hit the waves","Steal unattended valuables")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("""You grab a surfboard and attempt to "shred some gnar" as the lads would say.""","medium",30,"Cool Surfboard",30)
      elif event_choice == "2":
        event_setup.event_outcome("You march up and down the beach looking for bags or anything potentially valuable like a kleptomaniactic seagull","easy",20,"Variety of Sunscreens and Hats",20)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(10)--------------------------------------#
    elif event_id == 10:
      event_setup.event_prompt("You're on a plane yet to take off when the assistant comes over and offers various rations for the journey.","white","bold",0.05)

      event_setup.event_choices("Take one.","Take her purse")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You accept a solitary bottle of water and settle in.\nSuddenly, you realise you're still bound by the thiefs code and must escape the plane before you leave the city.","easy",40,"Water Bottle",5)
      elif event_choice == "2":
        event_setup.event_outcome("You grab her bag and attempt to have off with it!","medium",20,"Flight-Attendants Purse",30)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(11)--------------------------------------#
    elif event_id == 11:
      event_setup.event_prompt("You eventually tire of hunting for more items all the time and are studying under your local thief master\nwhen you see his things left unattended.","white","bold",0.05)

      event_setup.event_choices("Grab and Run","Respect his seniority")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You grab it and rush out of the front door.\n\nYou look back momentarily and swear you can see stood in the front door looking proud.","medium",50,"Master Thief Tools",250)
      elif event_choice == "2":
        event_setup.event_outcome(""""Seniority!?" comes booming from the top of the roof.\n"You're a thief! act like it!" He berates you as he attempts to leap at you.\nYou attempt to dodge and attack back in an attempt to survive his onslaught.""","hard",100,None,1)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(12)--------------------------------------#
    elif event_id == 12:
      event_setup.event_prompt("You're dining with some of the finer society in the city.","white","bold",0.05)

      event_setup.event_choices("Rob somebody","Steal some unattended valuables")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You tail somebody rich-looking into a more secluded spot and ask kindly for their money.","medium",20,"Rich-People Money",300)
      elif event_choice == "2":
        event_setup.event_outcome("You spot a crown/tiara left unattended and try to make off with it.","easy",30,"Fanciful Headwear",150)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(13)--------------------------------------#
    elif event_id == 13:
      event_setup.event_prompt("You're browsing the web when you see a listing for \nan irritatingly expensive painting being sold not two street away!'.","white","bold",0.05)

      event_setup.event_choices("Rush there and attempt to steal it","Make your own painting")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You rush down the street, into the exhibit and attempt to grab the painting, \nsubsequently exiting the exhibit post-haste.","hard",50,"Expensive Painting",150)
      elif event_choice == "2":
        event_setup.event_outcome("""'Screw these rich people! I'll make my own.' \nYou think to yourself as you scrawl haphazardly at a piece of paper.""","hard",5,"scrawled masterpiece and a sense of accomplishment",10)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(14)--------------------------------------#
    elif event_id == 14:
      event_setup.event_prompt("A cat walks up to you amidst your purloining spree and seems to want you to follow it.","white","bold",0.05)

      event_setup.event_choices("Follow it","Take it with you")
      event_choice = getch()
      print()
      if event_choice == "1":
        event_setup.event_outcome("You stay close to the feline whilst being led across rooftops and through alleys \nin hopes it leads you to something useful.","easy",20,"Shiny, Jingly Bell",20)
      elif event_choice == "2":
        event_setup.event_outcome("The cat seems rather insistant that you go with it, but you insist in return that it come with you.","hard",30,"rather indignant Cat",10)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)
    #----------------------------------Event(15)--------------------------------------#
    elif event_id == 15:
      global wealth
      event_setup.event_prompt("You waddle down the street as usual when you notice a wandering salesmen has set up shop and is selling his wares.","white","bold",0.05)

      event_setup.event_choices("Haggle with him","Steam from him")
      event_choice = getch()
      print()
      if event_choice == "1":
        global wealth
        event_setup.event_outcome("You use your collected knowledge of the trade to move him down from 60 UMs to only 10 for an item you know to be worth 80.","medium",0,"Vase",80)
        wealth = wealth - 10
      elif event_choice == "2":
        event_setup.event_outcome("You chat to him whilst sneaking an item worth about 80 UMs into your coat, hoping he doesn't notice.","medium",10,"Vase",80)

      else:
        text.taeris("look, just pick one, it'll be a lot more fun.",0.05)
        sleep(1)
        events.event(event_id)

class draw:
  def title():
    text.title("     TTTTTTTTTTTT   HHH     HHH   IIIIIIIIIII   EEEEEEEEEEE   FFFFFFFFFFF\n",0.01)
    text.title("         TTT        HHH     HHH       III       EEE           FFF        \n",0.01)
    text.title("         TTT        HHH     HHH       III       EEE           FFF        \n",0.01)
    text.title("         TTT        HHHHHHHHHHH       III       EEEEEEEEEEE   FFFFFFFFFFF\n",0.01)
    text.title("         TTT        HHH     HHH       III       EEE           FFF        \n",0.01)
    text.title("         TTT        HHH     HHH       III       EEE           FFF        \n",0.01)
    text.title("         TTT        HHH     HHH   IIIIIIIIIII   EEEEEEEEEEE   FFF        \n",0.01)

  def game_over():
    def G():
        text.title("                 GGGGGGGGGGGG             \n",0.01)
        text.title("               GGGG        GGGG            \n",0.01)
        text.title("              GGGG          GG             \n",0.01)
        text.title("             GGGG                             \n",0.01)
        text.title("             GGGG        GGGGGGGG             \n",0.01)
        text.title("              GGG          GGGG             \n",0.01)
        text.title("               GGGG        GGGG             \n",0.01)
        text.title("                 GGGGGGGGGGGG             \n",0.01)

    def A():
        text.title("                      A             \n",0.01)
        text.title("                    AA AA             \n",0.01)
        text.title("                 AAAA   AAAA            \n",0.01)
        text.title("               AAAA       AAAA            \n",0.01)
        text.title("             AAAAAAAAAAAAAAAAAA             \n",0.01)
        text.title("           AAAA              AAAA             \n",0.01)
        text.title("         AAAA                  AAAA             \n",0.01)
        text.title("       AAAAAAA                AAAAAAA             \n",0.01)

    def M():
        text.title("       MMMMMMM              MMMMMMM             \n",0.01)
        text.title("       MMMMM  MM           MM MMMMM             \n",0.01)
        text.title("       MMMM    MM         MM   MMMM             \n",0.01)
        text.title("       MMMM     MM       MM    MMMM             \n",0.01)
        text.title("       MMMM      MM     MM     MMMM             \n",0.01)
        text.title("       MMMM       MM   MM      MMMM             \n",0.01)
        text.title("       MMMM         MMMM       MMMM             \n",0.01)
        text.title("       MMMM          MM        MMMM             \n",0.01)

    def E():
        text.title("               EEEEEEEEEEEEEE                 \n",0.01)
        text.title("               EEEEE               \n",0.01)
        text.title("               EEEE               \n",0.01)
        text.title("               EEEEEEEEEEE                 \n",0.01)
        text.title("               EEEEEEEEEEE                \n",0.01)
        text.title("               EEEE               \n",0.01)
        text.title("               EEEEE               \n",0.01)
        text.title("               EEEEEEEEEEEEEE                  \n",0.01)

    def O():
        text.title("                 GGGGGGGGGGGG             \n",0.01)
        text.title("               GGG          GGG            \n",0.01)
        text.title("              GGG            GGG             \n",0.01)
        text.title("             GGGG            GGGG                 \n",0.01)
        text.title("             GGGG            GGGG             \n",0.01)
        text.title("              GGG            GGG             \n",0.01)
        text.title("               GGG          GGG             \n",0.01)
        text.title("                 GGGGGGGGGGGG             \n",0.01)

    def V():
        text.title("             VV              VV             \n",0.01)
        text.title("              VV            VV             \n",0.01)
        text.title("               VV          VV             \n",0.01)
        text.title("                VV        VV             \n",0.01)
        text.title("                 VV      VV            \n",0.01)
        text.title("                  VV    VV            \n",0.01)
        text.title("                   VV  VV            \n",0.01)
        text.title("                     VV             \n",0.01)

    def R():
        text.title("               RRRRRRRRRRR                 \n",0.01)
        text.title("               RRRR      RRR               \n",0.01)
        text.title("               RRRR      RRRR         \n",0.01)
        text.title("               RRRR      RRR                \n",0.01)
        text.title("               RRRRRRRRRRR                \n",0.01)
        text.title("               RRRR   RRRR           \n",0.01)
        text.title("               RRRR    RRRR         \n",0.01)
        text.title("               RRRR     RRRR           \n",0.01)

    G()
    print()
    A()
    print()
    M()
    print()
    E()
    print()
    print()
    print()
    print()
    O()
    print()
    V()
    print()
    E()
    print()
    R()
    print()
    print()
    print()
    print()
    print()
    print()

#the cooler cool bit
def player_choices(*args):
  options = []
  for x in args:
    options.append([str(x)])
  for x,res in enumerate(options,1):
    for x in str("(" + str(x) + ")" + " " + colored(str(res),player_color)):
      sys.stdout.write(x)
      sys.stdout.flush()
      sleep(0.05)
    for x in str(len(options)):
      print()

def generic_choices(*args):
  options = []
  for x in args:
    options.append([str(x)])
  for x,res in enumerate(options,1):
    for x in str("(" + str(x) + ")" + " " + colored(res,"white")):
      sys.stdout.write(x)
      sys.stdout.flush()
      sleep(0.05)
    for x in str(len(options)):
      print()

def game_end_choice(*args):
  options = []
  for x in args:
    options.append([str(x)])
  for x,res in enumerate(options,1):
    for x in str("(" + str(x) + ")" + " " + colored(res,"red","on_white",attrs=["bold"])):
      sys.stdout.write(x)
      sys.stdout.flush()
      sleep(0.05)
    for x in str(len(options)):
      print()

def settings():
    global Taeris_Talk
    text.basic("----------", "white",None,0.05)
    text.player("Welcome to the Settings!",0.05)
    text.basic("----------", "white",None,0.05)

    print()
    print()
    print()

    player_choices("Change Text Color","Change Random Seed","Allow Taeris Talk(tutorials)","Back")

    choice = getch()

    if choice == "1":
        os.system("clear")
        text_choice()
        os.system("clear")
        settings()

    elif choice == "2":
        os.system("clear")
        print()
        print()
        print()
        text.basic("What would you like to set the random seed to? (Numbers only)","white","bold",0.05)
        print()
        chose_seed = int(input("\n"))
        print()
        random.seed(chose_seed)
        text.basic("Seed=Set!","white","bold",0.05)
        sleep(1)
        os.system("clear")
        settings()

    elif choice == "3":
      os.system("clear")
      print()
      print()
      print()
      player_choices("Enabled","Disabled")
      print()

      ena_dis = getch()

      if ena_dis == "1":
        Taeris_Talk = True
        os.system("clear")
        settings()

      elif ena_dis == "2":
        Taeris_Talk = False
        os.system("clear")
        settings()

      else:
        os.system("clear")
        settings()

    else:
      return

def text_choice():
  global player_color
  global player_attrs
  global color_change
  if color_change == False:
    text.basic("What would you like your colour of text to be?\n","white",None,0.05)
    print()
    sleep(0.5)
    text.basic("Remember, it is a stand-in for you as an entity in this world.\n","blue",None,0.05)
    print()
    sleep(1)
    text.taeris("and likewise for the other characters in our story...",0.05)
  print()
  print()
  print()

  #the cool bit
  for x,res in enumerate(possible_colors,1):
    for x in str("(" + str(x) + ")" + " " + colored(res,str(res))):
      sys.stdout.write(x)
      sys.stdout.flush()
      sleep(0.05)
    for x in str(len(possible_colors)):
      print()

  choice = int(getch())
  if choice != 7:
    for x in range(len(possible_colors)):
      if choice == x:
        player_color = possible_colors[x - 1]
  elif choice == 7:
    player_color = "magenta"
  color_change = True
  os.system("clear")

def HUD():
  global health
  text.basic("Health: ","red","bold",0)
  text.basic(health,"white","bold",0)
  print()
  global stamina
  text.basic("Stamina: ","yellow","bold",0)
  text.basic(stamina,"white","bold",0)
  print()
  global wealth
  text.basic("Wealth: ","green","bold",0)
  text.basic(wealth,"white","bold",0)

def shop():
  global wealth
  global stolen_things

  os.system("clear")

  HUD()
  print()
  print()
  print()

  text.basic("Welcome to the shop, we'll buy everything you obtain at... OK prices!","blue","bold",0.05)
  print()
  print()
  print()
  items.show()
  print()
  print()
  print()
  sell_choice = getch()
  if sell_choice == "1":
    os.system("clear")
    game_main()
  if sell_choice == "2":
    text.basic("Those are your thievin tools. You're gonna want those with you.","blue","bold",0.05)
    sleep(1)
    os.system("clear")
    shop()
  else:
    items.sell(sell_choice)

    text.basic("A fine item, you shall be compensated accordingly.","blue","bold",0.05)
    print()
    print()
    print()
    player_choices("Sell again","Return to hideout")
    print()

    repeat = getch()

    if repeat == "1":
      os.system("clear")
      shop()
    else:
      game_main()

def start():
  os.system("clear")
  draw.title()
  print()
  print()
  print()

  generic_choices("Start Game", "Options")

  print()

  choice = getch()

  if choice == "1" and Taeris_Talk == True:
    os.system("clear")
    Set_Up()
  elif choice == "1" and Taeris_Talk == False:
    os.system("clear")
    game_main()
  elif choice == "2":
    os.system("clear")
    settings()
    os.system("clear")
    start()

def Set_Up():

      text.taeris("Welcome to your Life of Crime.",0.05)
      print()
      print()
      print()
      text.taeris("Might you require any assistance on the path ahead of you?",0.05)
      print()
      print()
      print()
      player_choices("Yes","No")
      print()
      first_choice = getch()

      if first_choice == "1":
        first_time = False
        tutorial()
      elif first_choice == "2":
        first_time = False
        text.taeris("Fine, suit yourself, but don't say I didn't offer.",0.05)
        sleep(2)
        print()
        text.taeris("Now then, off with you. Go steal me some stuff.",0.05)
        sleep(3)
        os.system("clear")
        game_main()

def tutorial():
  global player_color
  global color_change
  global first_time
  os.system("clear")
  if first_time == False:
    text.taeris("We've been through this before, need you any additional guidance?",0.04)
    print()
    print()
    print()
    player_choices("Yes","No","Fight Me!")

    player_choice = getch()
    if player_choice == "1":
      first_time = False
      tutorial()
    elif player_choice == "2":
      print()
      text.taeris("Very well, on with it.",0.04)
      os.system("clear")
      game_main()
    elif player_choice == "3":
      text.taeris("No",1)
      print()
      print()
      print()
      player_choices("Yes!","no...")
      print()
      player_challenge = getch()
      if player_challenge == "1":
        text.taeris("I had such high hopes.",0.04)
        text.waiting(1)
        os.system("clear")
        game_over()
      elif player_challenge == "2":
        text.taeris("Get on with it then.",0.04)
        sleep(1)
        os.system("clear")
        game_main()
  else:

    if color_change == False:
      text.taeris("Sorry, I can't help but notice your text is quite plain. Do you wish to change?",0.04)
      print()
      print()
      print()
      player_choices("Yes","No")

      color_change_Y_N = getch()

      if color_change_Y_N == "1":
          os.system("clear")
          text_choice()
          print()
          text.taeris("Now then, let's get on with it.",0.04)
          sleep(0.5)
          os.system("clear")
          tutorial()
      elif color_change_Y_N == "2":
        print()
        text.taeris("Rather abhorrent to my eyes, but so have you.",0.04)
        os.system("clear")

    text.taeris("First, what would you like explained?",0.05)
    print()
    print()
    print()
    player_choices("Health","Stamina","Wealth","Encounters")
    print()
    tutorial_choice = getch()

    if tutorial_choice == "1":
      os.system("clear")
      tutorials.health()
    elif tutorial_choice == "2":
      os.system("clear")
      tutorials.stamina()
    elif tutorial_choice == "3":
      os.system("clear")
      tutorials.wealth()
    elif tutorial_choice == "4":
      os.system("clear")
      tutorials.encounters()

def game_over():
  global health
  global stamina
  global wealth
  os.system("clear")
  text.taeris("hmmm, I had such high hopes, but it appears you've overextended yourself.",0.06)
  sleep(2)
  text.waiting(5)

  os.system("clear")

  draw.game_over()

  text.basic("Start Again?","white","bold",0.05)
  print()
  player_choices("Yes","No")

  choice = getch()
  if choice == "1":
    os.system("clear")
    start()
  else:
    text.basic("You've kinda gotta. Sorry.","white","bold",0.06)
    sleep(2)
    print()
    text.basic("Or you could just close the browser...","grey","bold",0.07)
    sleep(4)
    print()
    text.basic("On we go then!","white","bold",0.04)
    sleep(1)
    os.system("clear")
    health = 100
    stamina = 100
    wealth = 0
    start()

def game_win():
  global wealth
  global health
  global stamina
  global Returned
  if Returned == False:
    text.taeris("Well... You've done it.",0.07)
    print()
    sleep(2)
    text.taeris("To be honest, I didn't expect you to actually amass this much wealth.",0.06)
    print()
    sleep(2)
    text.taeris("That isn't to say I'm not very proud, but there isn't much for you to do now.",0.07)
    print()
    sleep(2)
    text.taeris("You may, however, choose to do one of two things.",0.08)
    print()
    sleep(2)
    Returned = True
  print()
  game_end_choice("Restart","Go Back")
  print()
  end_choose = getch()
  if end_choose == "1":
    text.taeris("I respect your decision even if I don't understand it.",0.06)
    os.system("clear")
    health = 100
    stamina = 100
    wealth = 0

    start()

  elif end_choose == "2":
    text.taeris("Come back whenever you wish.",0.09)
    os.system("clear")
    game_main()

  else:
    text.taeris("that's all there is now, so you better choose one.",0.05)

def game_main():
  global Taeris_Talk
  stats.health_stat.death_check()
  os.system("clear")

  HUD()

  print()
  print()
  print()

  if wealth < 1000:
    player_choices("Look for Things to Steal","Try to Sell Some Stolen Stuff","See Items","Rest","Settings","Exit")

    choice = getch()
    if choice == "1":
        stats.stamina_stat.tire_check()
        os.system("clear")
        random.seed(random.randint(0,1168))
        events.event(random.randint(0,15))
    elif choice == "2":
        shop()
    elif choice == "3":
        os.system("clear")
        HUD()
        print()
        print()
        items.show()
        print()
        print()
        text.basic("Press any key to close","white","bold",0.05)
        getch()
        os.system("clear")
        game_main()
    elif choice == "4":
        stats.stamina_stat.rest()
        os.system("clear")
        game_main()
    elif choice == "5":
      os.system("clear")
      settings()
      os.system("clear")
      game_main()
    elif choice == "6":
        Taeris_Talk = False
        os.system("clear")
        start()
  elif wealth >= 1000:
      player_choices("Look for Things to Steal","Try to Sell Some Stolen Stuff","See Items","Rest","Return to Taeris","Settings","exit")

      choice = getch()
      if choice == "1":
        stats.stamina_stat.tire_check()
        os.system("clear")
        random.seed(random.randint(0,1204))
        events.event(random.randint(0,15))
      elif choice == "2":
        shop()
      elif choice == "3":
        os.system("clear")
        HUD()
        print()
        print()
        items.show()
        print()
        print()
        text.basic("Press any key to close","white","bold",0.05)
        getch()
        os.system("clear")
        game_main()
      elif choice == "4":
        stats.stamina_stat.rest()
        os.system("clear")
        game_main()
      elif choice == "5":
        os.system("clear")
        game_win()
      elif choice == "6":
        os.system("clear")
        settings()
        os.system("")
        game_main()
      elif choice == "7":
        os.system("clear")
        start()

start()


##### import needed packages
from brian2 import *
from brian2.units.constants import zero_celsius, gas_constant as R, faraday_constant as F
import numpy as np

##### import functions
import functions.calculations as calc

# =============================================================================
# Temperature
# =============================================================================
T_celsius = 37

# =============================================================================
# Permeabilities
# =============================================================================
##### Dividing factor for permeabilities in the somatic region (makes currents smaller)
dividing_factor = 30
##### permeabilites active compartments
P_Na = 51.5*um/second
P_K = 2.0*um/second
P_KLT = 0.018*um/second
P_HCN = 0.0108*um/second
##### permeabilites somatic region
P_Na_somatic_region = P_Na / dividing_factor
P_K_somatic_region = P_K / dividing_factor
P_KLT_somatic_region = P_KLT / dividing_factor
P_HCN_somatic_region = P_HCN / dividing_factor

# =============================================================================
# Conductivities
# =============================================================================
# conductivity of leakage channels
g_L = 728*siemens/meter**2
g_L_somatic_region = g_L / dividing_factor

# =============================================================================
# Ion concentrations
# =============================================================================
Na_i = 10*mole/meter**3
Na_e = 142*mole/meter**3
K_i = 141*mole/meter**3
K_e = 4.2*mole/meter**3
C_i = 60*mole/meter**3
C_e = 15*mole/meter**3

# =============================================================================
# Resistivities
# =============================================================================
##### axoplasmatic resistivity
rho_in = 70*ohm*cm
##### external resistivity
rho_out = 300*ohm*cm

# =============================================================================
# Other parameters
# =============================================================================
##### Myelin layers soma and presomatic region
myelin_layers_somatic_region = 4

# =============================================================================
# Differential equations
# =============================================================================
eqs = '''
I_Na = P_Na*m**3*h*(v*F**2)/(R*T_kelvin) * (Na_e-Na_i*exp(v*F/(R*T_kelvin)))/(exp(v*F/(R*T_kelvin))-1) : amp/meter**2
I_K = P_K*n**2*(v*F**2)/(R*T_kelvin) * (K_e-K_i*exp(v*F/(R*T_kelvin)))/(exp(v*F/(R*T_kelvin))-1) : amp/meter**2
I_KLT = P_KLT*w**4*z*(v*F**2)/(R*T_kelvin) * (K_e-K_i*exp(v*F/(R*T_kelvin)))/(exp(v*F/(R*T_kelvin))-1) : amp/meter**2
I_HCN = P_HCN*r*(v*F**2)/(R*T_kelvin) * (C_e-C_i*exp(v*F/(R*T_kelvin)))/(exp(v*F/(R*T_kelvin))-1) : amp/meter**2
I_L = g_L*(E_Leak-(v-V_res)) : amp/meter**2
Im = I_Na + I_K + I_KLT + I_HCN + I_L + g_myelin*(-(v-V_res)): amp/meter**2
I_stim = stimulus(t,i) : amp (point current)
dm/dt = alpha_m * (1-m) - beta_m * m : 1
dn/dt = alpha_n * (1-n) - beta_n * n : 1
dh/dt = alpha_h * (1-h) - beta_h * h : 1
dw/dt = alpha_w * (1-w) - beta_w * w : 1
dz/dt = alpha_z * (1-z) - beta_z * z : 1
dr/dt = alpha_r * (1-r) - beta_r * r : 1
alpha_m = 0.49/mV*((v-V_res)-25.41*mV)/(1-exp((25.41*mV-(v-V_res))/(6.06*mV))) * 2.2**(0.1*(T_celsius-20))/ms : Hz
alpha_n = 0.02/mV*((v-V_res)-35*mV)/(1-exp((35*mV-(v-V_res))/(10*mV))) * 3**(0.1*(T_celsius-20))/ms : Hz
alpha_h = 0.09/mV*(-27.74*mV-(v-V_res))/(1-exp(((v-V_res)+27.74*mV)/(9.06*mV))) * 2.9**(0.1*(T_celsius-20))/ms : Hz
beta_m = 1.04/mV*(21*mV-(v-V_res))/(1-exp(((v-V_res)-21*mV)/(9.41*mV))) * 2.2**(0.1*(T_celsius-20))/ms : Hz
beta_n = 0.05/mV*(10*mV-(v-V_res))/(1-exp(((v-V_res)-10*mV)/(10*mV))) * 3**(0.1*(T_celsius-20))/ms : Hz
beta_h = 3.7/(1+exp((56*mV-(v-V_res))/(12.5*mV))) * 2.9**(0.1*(T_celsius-20))/ms : Hz
w_inf = 1/(exp(13/5-(v-V_res)/(6*mV))+1)**(1/4) : 1
tau_w = 0.2887 + (17.53*exp((v-V_res)/(45*mV)))/(3*exp(17*(v-V_res)/(90*mV))+15.791) : 1
alpha_w = w_inf/tau_w * 3**(0.1*(T_celsius-37))/ms : Hz
beta_w = (1-w_inf)/tau_w * 3**(0.1*(T_celsius-37))/ms : Hz
z_inf = 1/(2*(exp((v-V_res)/(10*mV)+0.74)+1))+0.5 : 1
tau_z = 9.6225 + (2073.6*exp((v-V_res)/(8*mV)))/(9*(exp(7*(v-V_res)/(40*mV))+1.8776)) : 1
alpha_z = z_inf/tau_z * 3**(0.1*(T_celsius-37))/ms : Hz
beta_z = (1-z_inf)/tau_z * 3**(0.1*(T_celsius-37))/ms : Hz
r_inf = 1/(exp((v-V_res)/(7*mV)+62/35)+1) : 1
tau_r = 50000/(711*exp((v-V_res)/(12*mV)-3/10)+51*exp(9/35-(v-V_res)/(14*mV)))+25/6 : 1
alpha_r = r_inf/tau_r * 3.3**(0.1*(T_celsius-37))/ms : Hz
beta_r = (1-r_inf)/tau_r * 3.3**(0.1*(T_celsius-37))/ms : Hz
P_Na : meter/second
P_K : meter/second
P_KLT : meter/second
P_HCN : meter/second
g_L : siemens/meter**2
g_myelin : siemens/meter**2
E_Leak : volt
V_res : volt
T_celsius : 1
T_kelvin : kelvin
Na_i : mole/meter**3
Na_e : mole/meter**3
K_i : mole/meter**3
K_e : mole/meter**3
C_i : mole/meter**3
C_e : mole/meter**3
'''

# =============================================================================
#  Morphologic data
# =============================================================================
##### structure
nof_segments_presomatic_region = 10
nof_segments_soma = 10
nof_axonal_internodes = 10 # have to be at least 5
##### lengths
length_peripheral_terminal = 10*um
length_internodes_dendrite = [175,175,175,175,175,50]*um
length_internodes_axon = [150,200,250,300,350]*um # the last value defines the lengths of further internodes
length_nodes = 1*um
length_presomatic_region = 100*um
length_soma = 30*um
##### diameters
diameter_dendrite = 3*um
diameter_somatic_region = 2*um
diameter_soma = 10*um
diameter_axon = 3*um

# =============================================================================
# Capacitivites
# =============================================================================
##### membrane capacitivity one layer (calculated with the values given in Briaire and Frijns 2005 page 146)
c_m_layer = 2.801*uF/cm**2

# =============================================================================
# Condactivities internodes
# =============================================================================
##### membrane conductivity somatic region one layer (calculated with the values given in Briaire and Frijns 2005 page 146)
g_m_layer = 0.6*msiemens/cm**2

# =============================================================================
# Noise factor
# =============================================================================
k_noise = 0.00001*uA*np.sqrt(second/um**3)

# =============================================================================
# Electrode
# =============================================================================
electrode_distance = 300*um

# =============================================================================
# Display name for plots
# =============================================================================
display_name = "Briaire and Frijns 2005 a."
display_name_short = "Briaire 05 a."

# =============================================================================
# Define inter-pulse intervalls for refractory curve calculation
# =============================================================================
inter_pulse_intervals = np.hstack([np.linspace(0.8, 0.85, num=20, endpoint = False),
                                   np.linspace(0.85, 1.3, num=15, endpoint = False),
                                   np.linspace(1.3, 5, num=15)])*1e-3

# =============================================================================
# Calculations
# =============================================================================
##### Temperature
T_kelvin = zero_celsius + T_celsius*kelvin

##### rates for resting potential
alpha_m_0 = 0.49*(-25.41)/(1-np.exp(25.41/6.06)) * 2.2**(0.1*(T_celsius-20))
alpha_n_0 = 0.02*(-35)/(1-np.exp(35/10)) * 3**(0.1*(T_celsius-20))
alpha_h_0 = 0.09*(-27.74)/(1-np.exp(27.74/9.06)) * 2.9**(0.1*(T_celsius-20))
beta_m_0 = 1.04*21/(1-np.exp(-21/9.41)) * 2.2**(0.1*(T_celsius-20))
beta_n_0 = 0.05*10/(1-np.exp(-10/10)) * 3**(0.1*(T_celsius-20))
beta_h_0 = 3.7/(1+np.exp(56/12.5)) * 2.9**(0.1*(T_celsius-20))

##### initial values for gating variables
m_init = alpha_m_0 / (alpha_m_0 + beta_m_0)
n_init = alpha_n_0 / (alpha_n_0 + beta_n_0)
h_init = alpha_h_0 / (alpha_h_0 + beta_h_0)
w_init = 1/(np.exp(13/5)+1)**(1/4)
z_init = 1/(2*(np.exp(0.74)+1))+0.5
r_init = 1/(np.exp(+62/35)+1)

##### Potentials
# Resting potential (calculated with Goldman equation)
V_res = (R*T_kelvin)/F * np.log((P_K*n_init**2*K_e + P_Na*h_init*m_init**3*Na_e + P_KLT*z_init*w_init**4*K_e + P_HCN*r_init*C_e)/
         (P_K*n_init**2*K_i + P_Na*h_init*m_init**3*Na_i + P_KLT*z_init*w_init**4*K_i + P_HCN*r_init*C_i))

# Nerst potential for leakage current
E_L = (-1/g_L)*(P_Na*m_init**3*h_init*(V_res*F**2)/(R*T_kelvin) *
             (Na_e-Na_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))) +
             P_K*n_init**2*(V_res*F**2)/(R*T_kelvin) *
             (K_e-K_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))) +
             P_KLT*z_init*w_init**4*(V_res*F**2)/(R*T_kelvin) *
             (K_e-K_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))) +
             P_HCN*r_init*(V_res*F**2)/(R*T_kelvin) *
             (C_e-C_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))))

E_L_somatic_region = (-1/g_L_somatic_region)*(P_Na_somatic_region*m_init**3*h_init*(V_res*F**2)/(R*T_kelvin) *
                      (Na_e-Na_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))) +
                      P_K_somatic_region*n_init**2*(V_res*F**2)/(R*T_kelvin) *
                      (K_e-K_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))) +
                      P_KLT_somatic_region*z_init*w_init**4*(V_res*F**2)/(R*T_kelvin) *
                      (K_e-K_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))) +
                      P_HCN_somatic_region*r_init*(V_res*F**2)/(R*T_kelvin) *
                      (C_e-C_i*np.exp(V_res*F/(R*T_kelvin)))/(1-np.exp(V_res*F/(R*T_kelvin))))

##### structure of ANF
# structure of ANF
# terminal = 0
# internode = 1
# node = 2
# presomatic region = 3
# Soma = 4
structure = np.array([0] + list(np.tile([1,2],6)) + list(np.tile([3],nof_segments_presomatic_region)) + [2] +
                           list(np.tile([4],nof_segments_soma)) + [2] + list(np.tile([1,2],nof_axonal_internodes-1)) + [1])
# indexes presomatic region
index_presomatic_region = np.argwhere(structure == 3)
start_index_presomatic_region = int(index_presomatic_region[0])
# indexes of soma
index_soma = np.argwhere(structure == 4)
start_index_soma = int(index_soma[0])
end_index_soma = int(index_soma[-1])
# further structural data
nof_comps = len(structure)
nof_comps_dendrite = len(structure[:start_index_soma])
nof_comps_axon = len(structure[end_index_soma+1:])

#####  Compartment lengths
# initialize
compartment_lengths = np.zeros_like(structure)*um
# peripheral terminal
compartment_lengths[np.where(structure == 0)] = length_peripheral_terminal
# internodes dendrite
compartment_lengths[0:start_index_soma][structure[0:start_index_soma] == 1] = length_internodes_dendrite
# internodes axon
compartment_lengths[end_index_soma+1:][structure[end_index_soma+1:] == 1] = list(list(length_internodes_axon) +
                         list(np.tile(length_internodes_axon[-1],nof_axonal_internodes-5)))
# nodes
compartment_lengths[np.where(structure == 2)] = length_nodes
# presomatic region
compartment_lengths[np.where(structure == 3)] = length_presomatic_region/nof_segments_presomatic_region
# soma
compartment_lengths[np.where(structure == 4)] = length_soma/nof_segments_soma
# total length neuron
length_neuron = sum(compartment_lengths)

##### Compartment diameters
# initialize
compartment_diameters = np.zeros(nof_comps+1)*um
# dendrite
compartment_diameters[0:start_index_presomatic_region+1] = diameter_dendrite
dendrite_outer_diameter = diameter_dendrite / 0.6 # see Gillespie 1983
# region before soma
compartment_diameters[start_index_presomatic_region+1:start_index_soma+1] = diameter_somatic_region
# soma
compartment_diameters[start_index_soma+1:end_index_soma+1] = diameter_soma
# node after soma
compartment_diameters[end_index_soma+1] = diameter_somatic_region
# axon
compartment_diameters[end_index_soma+2:] = diameter_axon
axon_outer_diameter = diameter_axon / 0.6 # see Gillespie 1983

#####  Compartment middle point distances (needed for plots)
distance_comps_middle = np.zeros_like(compartment_lengths)
distance_comps_middle[0] = 0.5*compartment_lengths[0]
for ii in range(0,nof_comps-1):
    distance_comps_middle[ii+1] = 0.5*compartment_lengths[ii] + 0.5*compartment_lengths[ii+1]

##### Capacitivites
# initialize
c_m = np.zeros_like(structure)*uF/cm**2
# peripheral terminal and nodes
c_m[np.where(np.logical_or(structure == 0, structure == 2))] = c_m_layer
# somatic region
c_m[np.where(np.logical_or(structure == 3, structure == 4))] = c_m_layer/(1+myelin_layers_somatic_region)
# values for internodes are zero

##### Condactivities internodes
# initialize
g_m = np.zeros_like(structure)*msiemens/cm**2
# somatic region
g_m[np.where(np.logical_or(structure == 3, structure == 4))] = g_m_layer/(1+myelin_layers_somatic_region)
# values for internodes are zero

##### Axoplasmatic resistances
compartment_center_diameters = np.zeros(nof_comps)*um
compartment_center_diameters = (compartment_diameters[0:-1] + compartment_diameters[1:]) / 2
R_a = (compartment_lengths*rho_in) / ((compartment_center_diameters*0.5)**2*np.pi)

##### Surface arias
# lateral surfaces
m = [np.sqrt(abs(compartment_diameters[i+1] - compartment_diameters[i])**2 + compartment_lengths[i]**2)
           for i in range(0,nof_comps)]
# total surfaces
A_surface = [(compartment_diameters[i+1] + compartment_diameters[i])*np.pi*m[i]*0.5
           for i in range(0,nof_comps)]

##### Noise term
P_Na_vector = np.zeros(nof_comps)*0*meter/second
P_Na_vector[:] = P_Na
P_Na_vector[structure == 1] = 0*meter/second
P_Na_vector[structure == 3] = P_Na_somatic_region
P_Na_vector[structure == 4] = P_Na_somatic_region
noise_term = np.sqrt(A_surface*P_Na_vector)

##### Compartments to plot
# get indexes of all compartments that are not segmented
indexes_comps = np.where(np.logical_or(np.logical_or(structure == 0, structure == 1), structure == 2))
# calculate middle compartments of presomatic region and soma
middle_comp_presomatic_region = int(start_index_presomatic_region + np.floor((nof_segments_presomatic_region)/2))
middle_comp_soma = int(start_index_soma + np.floor((nof_segments_soma)/2))
# create array with all compartments to plot
comps_to_plot = np.sort(np.append(indexes_comps, [middle_comp_presomatic_region, middle_comp_soma]))

# =============================================================================
# Set up the model
# =============================================================================
def set_up_model(dt, model, update = False):
    """This function calculates the stimulus current at the current source for
    a single monophasic pulse stimulus at each point of time

    Parameters
    ----------
    dt : time
        Sets the defaultclock.
    model : module
        Contains all morphologic and physiologic data of a model

    Returns
    -------
    neuron
        Gives back a brian2 neuron
    model
        Gives back the whole module
    """

    start_scope()

    ##### Update model parameters (should be done, if original parameters have been changed)
    if update:
        ##### Temperature
        model.T_kelvin = model.zero_celsius + model.T_celsius*kelvin

        ##### rates for resting potential
        alpha_m_0 = 0.49*(-25.41)/(1-np.exp(25.41/6.06)) * 2.2**(0.1*(model.T_celsius-20))
        alpha_n_0 = 0.02*(-35)/(1-np.exp(35/10)) * 3**(0.1*(model.T_celsius-20))
        alpha_h_0 = 0.09*(-27.74)/(1-np.exp(27.74/9.06)) * 2.9**(0.1*(model.T_celsius-20))
        beta_m_0 = 1.04*21/(1-np.exp(-21/9.41)) * 2.2**(0.1*(model.T_celsius-20))
        beta_n_0 = 0.05*10/(1-np.exp(-10/10)) * 3**(0.1*(model.T_celsius-20))
        beta_h_0 = 3.7/(1+np.exp(56/12.5)) * 2.9**(0.1*(model.T_celsius-20))

        ##### initial values for gating variables
        model.m_init = alpha_m_0 / (alpha_m_0 + beta_m_0)
        model.n_init = alpha_n_0 / (alpha_n_0 + beta_n_0)
        model.h_init = alpha_h_0 / (alpha_h_0 + beta_h_0)

        ##### Potentials
        # Resting potential (calculated with Goldman equation)
        model.V_res = (model.R*model.T_kelvin)/model.F * np.log((model.P_K*model.n_init**2*model.K_e + model.P_Na*model.h_init*model.m_init**3*model.Na_e +
                      model.P_KLT*model.z_init*model.w_init**4*model.K_e + model.P_HCN*model.r_init*model.C_e)/
                 (model.P_K*model.n_init**2*model.K_i + model.P_Na*model.h_init*model.m_init**3*model.Na_i +
                  model.P_KLT*model.z_init*model.w_init**4*model.K_i + model.P_HCN*model.r_init*model.C_i))

        # Nerst potential for leakage current
        model.E_L = (-1/model.g_L)*(model.P_Na*model.m_init**3*model.h_init*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                     (model.Na_e-model.Na_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))) +
                     model.P_K*model.n_init**2*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                     (model.K_e-model.K_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))) +
                     model.P_KLT*model.z_init*model.w_init**4*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                     (model.K_e-model.K_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))) +
                     model.P_HCN*model.r_init*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                     (model.C_e-model.C_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))))

        model.E_L_somatic_region = (-1/model.g_L_somatic_region)*(model.P_Na_somatic_region*model.m_init**3*model.h_init*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                                     (model.Na_e-model.Na_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))) +
                                     model.P_K_somatic_region*model.n_init**2*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                                     (model.K_e-model.K_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))) +
                                     model.P_KLT_somatic_region*model.z_init*model.w_init**4*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                                     (model.K_e-model.K_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))) +
                                     model.P_HCN_somatic_region*model.r_init*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *
                                     (model.C_e-model.C_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))))

        ##### Potentials
        # Resting potential (calculated with Goldman equation)
        model.V_res = (model.R*model.T_kelvin)/model.F * np.log((model.P_K*model.n_init**2*model.K_e + model.P_Na*model.h_init*model.m_init**3*model.Na_e)/\
                 (model.P_K*model.n_init**2*model.K_i + model.P_Na*model.h_init*model.m_init**3*model.Na_i))

        # Nerst potential for leakage current; leakage chanels were excluded but could be added by using: g_L*(E_L-(v-V_res))
        model.E_L = (-1/model.g_L)*(model.P_Na*model.m_init**3*model.h_init*(model.V_res*model.F**2)/(model.R*model.T_kelvin) *\
                     (model.Na_e-model.Na_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))) +\
                     model.P_K*model.n_init**2*(model.V_res*F**2)/(model.R*model.T_kelvin) *\
                     (model.K_e-model.K_i*np.exp(model.V_res*model.F/(model.R*model.T_kelvin)))/(1-np.exp(model.V_res*model.F/(model.R*model.T_kelvin))))

        ##### structure of ANF
        # structure of ANF
        # terminal = 0
        # internode = 1
        # node = 2
        # presomatic region = 3
        # Soma = 4
        model.structure = np.array([0] + list(np.tile([1,2],6)) + list(np.tile([3],model.nof_segments_presomatic_region)) + [2] + \
                                   list(np.tile([4],model.nof_segments_soma)) + [2] + list(np.tile([1,2],model.nof_axonal_internodes-1)) + [1])
        # indexes presomatic region
        model.index_presomatic_region = np.argwhere(model.structure == 3)
        model.start_index_presomatic_region = int(model.index_presomatic_region[0])
        # indexes of soma
        model.index_soma = np.argwhere(model.structure == 4)
        model.start_index_soma = int(model.index_soma[0])
        model.end_index_soma = int(model.index_soma[-1])
        # further structural data
        model.nof_comps = len(model.structure)
        model.nof_comps_dendrite = len(model.structure[:model.start_index_soma])
        model.nof_comps_axon = len(model.structure[model.end_index_soma+1:])

        #####  Compartment lengths
        # initialize
        model.compartment_lengths = np.zeros_like(model.structure)*um
        # peripheral terminal
        model.compartment_lengths[np.where(model.structure == 0)] = model.length_peripheral_terminal
        # internodes dendrite
        model.compartment_lengths[0:model.start_index_soma][model.structure[0:model.start_index_soma] == 1] = model.length_internodes_dendrite
        # internodes axon
        model.compartment_lengths[model.end_index_soma+1:][model.structure[model.end_index_soma+1:] == 1] = list(list(model.length_internodes_axon) +
                         list(np.tile(model.length_internodes_axon[-1],model.nof_axonal_internodes-5)))
        # nodes
        model.compartment_lengths[np.where(model.structure == 2)] = model.length_nodes
        # presomatic region
        model.compartment_lengths[np.where(model.structure == 3)] = model.length_presomatic_region/model.nof_segments_presomatic_region
        # soma
        model.compartment_lengths[np.where(model.structure == 4)] = model.length_soma/model.nof_segments_soma
        # total length neuron
        model.length_neuron = sum(model.compartment_lengths)

        ##### Compartment diameters
        # initialize
        model.compartment_diameters = np.zeros(model.nof_comps+1)*um
        # dendrite
        model.compartment_diameters[0:model.start_index_presomatic_region+1] = model.diameter_dendrite
        # region before soma
        model.compartment_diameters[model.start_index_presomatic_region+1:model.start_index_soma+1] = model.diameter_somatic_region
        # soma
        model.compartment_diameters[model.start_index_soma+1:model.end_index_soma+1] = model.diameter_soma
        # node after soma
        model.compartment_diameters[model.end_index_soma+1] = model.diameter_somatic_region
        # axon
        model.compartment_diameters[model.end_index_soma+2:] = model.diameter_axon

        #####  Compartment middle point distances (needed for plots)
        model.distance_comps_middle = np.zeros_like(model.compartment_lengths)
        model.distance_comps_middle[0] = 0.5*model.compartment_lengths[0]
        for ii in range(0,model.nof_comps-1):
            model.distance_comps_middle[ii+1] = 0.5*model.compartment_lengths[ii] + 0.5*model.compartment_lengths[ii+1]

        ##### Capacitivites
        # initialize
        model.c_m = np.zeros_like(model.structure)*uF/cm**2
        # peripheral terminal and nodes
        model.c_m[np.where(np.logical_or(model.structure == 0, model.structure == 2))] = model.c_m_layer
        # somatic region
        model.c_m[np.where(np.logical_or(model.structure == 3, model.structure == 4))] = model.c_m_layer/(1+model.myelin_layers_somatic_region)
        # values for internodes are zero

        ##### Condactivities internodes
        # initialize
        model.g_m = np.zeros_like(model.structure)*msiemens/cm**2
        # somatic region
        model.g_m[np.where(np.logical_or(model.structure == 3, model.structure == 4))] = model.g_m_layer/(1+model.myelin_layers_somatic_region)
        # values for internodes are zero

        ##### Axoplasmatic resistances
        model.compartment_center_diameters = np.zeros(model.nof_comps)*um
        model.compartment_center_diameters = (model.compartment_diameters[0:-1] + model.compartment_diameters[1:]) / 2
        model.R_a = (model.compartment_lengths*model.rho_in) / ((model.compartment_center_diameters*0.5)**2*np.pi)

        ##### Surface arias
        # lateral surfaces
        m = [np.sqrt(abs(model.compartment_diameters[i+1] - model.compartment_diameters[i])**2 + model.compartment_lengths[i]**2)
                   for i in range(0,model.nof_comps)]
        # total surfaces
        model.A_surface = [(model.compartment_diameters[i+1] + model.compartment_diameters[i])*np.pi*m[i]*0.5
                   for i in range(0,model.nof_comps)]

        ##### Noise term
        model.P_Na_vector = np.zeros(model.nof_comps)*0*meter/second
        model.P_Na_vector[:] = model.P_Na
        model.P_Na_vector[model.structure == 1] = 0*meter/second
        model.P_Na_vector[model.structure == 3] = model.P_Na/model.dividing_factor
        model.P_Na_vector[model.structure == 4] = model.P_Na/model.dividing_factor
        model.noise_term = np.sqrt(model.A_surface*model.P_Na_vector)

        ##### Compartments to plot
        # get indexes of all compartments that are not segmented
        model.indexes_comps = np.where(np.logical_or(np.logical_or(model.structure == 0, model.structure == 1), model.structure == 2))
        # calculate middle compartments of presomatic region and soma
        model.middle_comp_presomatic_region = int(model.start_index_presomatic_region + np.floor((model.nof_segments_presomatic_region)/2))
        model.middle_comp_soma = int(model.start_index_soma + np.floor((model.nof_segments_soma)/2))
        # create array with all compartments to plot
        model.comps_to_plot = np.sort(np.append(model.indexes_comps, [model.middle_comp_presomatic_region, model.middle_comp_soma]))

    ##### initialize defaultclock
    defaultclock.dt = dt

    ##### define morphology
    morpho = Section(n = model.nof_comps,
                     length = model.compartment_lengths,
                     diameter = model.compartment_diameters)

    ##### define neuron
    neuron = SpatialNeuron(morphology = morpho,
                           model = model.eqs,
                           Cm = model.c_m,
                           Ri = model.rho_in,
                           method="exponential_euler")

    ##### initial values
    neuron.v = model.V_res
    neuron.m = model.m_init
    neuron.n = model.n_init
    neuron.h = model.h_init
    neuron.w = model.w_init
    neuron.z = model.z_init
    neuron.r = model.r_init

    ##### Set parameter values of differential equations
    # permeabilities peripheral terminal and active compartments
    neuron.P_Na[np.asarray(np.where(np.logical_or(model.structure == 0, model.structure == 2)))] = model.P_Na
    neuron.P_K[np.asarray(np.where(np.logical_or(model.structure == 0, model.structure == 2)))] = model.P_K
    neuron.P_KLT[np.asarray(np.where(np.logical_or(model.structure == 0, model.structure == 2)))] = model.P_KLT
    neuron.P_HCN[np.asarray(np.where(np.logical_or(model.structure == 0, model.structure == 2)))] = model.P_HCN
    neuron.g_L[np.asarray(np.where(np.logical_or(model.structure == 0, model.structure == 2)))] = model.g_L

    # permeabilities internodes
    neuron.P_Na[np.asarray(np.where(model.structure == 1))] = 0*meter/second
    neuron.P_K[np.asarray(np.where(model.structure == 1))] = 0*meter/second
    neuron.P_KLT[np.asarray(np.where(model.structure == 1))] = 0*meter/second
    neuron.P_HCN[np.asarray(np.where(model.structure == 1))] = 0*meter/second
    neuron.g_L[np.asarray(np.where(model.structure == 1))] = 0*siemens/meter**2

    # conductances
    neuron.g_myelin = model.g_m

    # permeabilities presomatic region
    neuron.P_Na[index_presomatic_region] = model.P_Na_somatic_region
    neuron.P_K[index_presomatic_region] = model.P_K_somatic_region
    neuron.P_KLT[index_presomatic_region] = model.P_KLT_somatic_region
    neuron.P_HCN[index_presomatic_region] = model.P_HCN_somatic_region
    neuron.g_L[index_presomatic_region] = model.g_L_somatic_region

    # permeabilities soma
    neuron.P_Na[index_soma] = model.P_Na_somatic_region
    neuron.P_K[index_soma] = model.P_K_somatic_region
    neuron.P_KLT[index_soma] = model.P_KLT_somatic_region
    neuron.P_HCN[index_soma] = model.P_HCN_somatic_region
    neuron.g_L[index_soma] = model.g_L_somatic_region

    # Nernst potential for leakage current
    neuron.E_Leak = model.E_L
    neuron.E_Leak[index_presomatic_region] = E_L_somatic_region
    neuron.E_Leak[model.index_soma] = E_L_somatic_region

    # other parameters
    neuron.V_res = model.V_res
    neuron.T_celsius = model.T_celsius
    neuron.T_kelvin = model.T_kelvin
    neuron.Na_i = model.Na_i
    neuron.Na_e = model.Na_e
    neuron.K_i = model.K_i
    neuron.K_e = model.K_e
    neuron.C_i = model.C_i
    neuron.C_e = model.C_e

    return neuron, model


import csv

from pymotifs import core
from pymotifs import models as mod
from sqlalchemy import desc

class Loader(core.SimpleLoader):

    merge_data = True
    allow_no_data = True

    """
    @property
    def table(self):
        return mod.LoopAnnotations
    """

    def to_process(self, pdbs, **kwrags):
        """ return the different loop types to process
        """
        return ["IL","HL"]

    """
    def remove(self, *args, **kwargs):
        Does not actually remove from the DB. We always want the loop ids to
        be consistent so we do not automatically remove loops.
        """

    """Added has_data because it should force the stage to recompute "IL". This is not a permenant solution."""

    def has_data(self, args, **kwargs):
        return False

    def query(self, session, pdb):
        return session.query(mod.LoopAnnotations)

    def _get_loop_annotations(self,loop_type):
        """ Gets access to the spreadsheet, reads values, and saves data in DB friendly format.
        Create dictionary for loop_annotations 1 and 2 and motif_annotations 1 and 2 with loop_id and motif_id
        as their respective keys and with values consisting of a 2-element set: annotation and author.
        """

        loop_annotation1 = {}
        loop_annotation2 = {}
        motif_annotation1 = {}
        motif_annotation2 = {}

        #Read data from csv file and save each row as a list of strings

        loop_data = []

        self.logger.info("Reading %s annotations from CSV file" % loop_type)
        print("annotations.py reading %s loop annotations" % loop_type)

        with open("/usr/local/pipeline/hub-core/%s_annotations.csv" % loop_type, "r") as annotations:
            entries = csv.reader(annotations, delimiter = ',')
            for rawrow in entries:
                #If a loop_id is indicated, then check for and save loop annotation 1
                #and/or loop annotation 2

                # trim leading and trailing white space
                row = [r.strip() for r in rawrow]

                # record loop-specific annotations
                if isinstance(row[1], str) and row[1]:
                    for row1 in row[1].replace(" ","").replace(";",",").split(","):
                        print(row1)
                        if isinstance(row[2], str) and row[2]:
                            loop_annotation1[row1] = [row[2], row[4]]     # save annotation and author
                            if isinstance(row[3], str) and row[3]:
                                loop_annotation2[row1] = [row[3], row[4]] # save annotation and author
                            else:
                                loop_annotation2[row1] = [row[2], row[4]] # repeat column C annotation

                    # Listing a loop id with two blank annotations means
                    # not to annotate that loop, even using the motif annotation
                    if len(row[2]) == 0 and len(row[3]) == 0:
                        loop_annotation1[row1] = ["NULL", row[4]]
                        loop_annotation2[row1] = ["NULL", row[4]]

                #Otherwise check for motif group and save motif annotation 1 and/or motif annotation 2
                elif isinstance(row[0],str) and row[0]:
                    if isinstance(row[2], str) and row[2]:
                        motif_annotation1[row[0]] = [row[2], row[4]]   # save annotation and author
                    if isinstance(row[3], str) and row[3]:
                        motif_annotation2[row[0]] = [row[3], row[4]]  # save annotation and author

        #Save each loop annnotation to loop_data in DB-friendly format

        #if a loop instance has either annotation1 and annotation 2 indicated,
        #then do not check for motif annotations
        annotated_loops = list(set(loop_annotation1.keys()) | set(loop_annotation2.keys()))

        for loop in annotated_loops:
            if loop in loop_annotation1:
                ann1 = loop_annotation1[loop][0]
                author = loop_annotation1[loop][1]
            else:
                ann1 = "NULL"
            if loop in loop_annotation2:
                ann2 = loop_annotation2[loop][0]
                author = loop_annotation1[loop][1]
            else:
                ann2 = "NULL"

            if not ann1 == "NULL" and not ann2 == "NULL":
                loop_data.append(mod.LoopAnnotations(
                    loop_id = loop,
                    annotation_1 = ann1,
                    annotation_2 = ann2,
                    author = author))

        #The rest of the LoopAnnotations entries need to be checked for motif_annotations

        #Get current motif release
        with self.session() as session:
            current_ml_release = session.query(mod.MlReleases.ml_release_id).\
                order_by(desc(mod.MlReleases.index)).\
                limit(1)

#            self.logger.info("Current motif atlas release %s" % current_ml_release)

        # collect together all annotated motif groups
        all_motifs = set(motif_annotation1.keys()) | set(motif_annotation2.keys())
        for motif in all_motifs:
            # For each motif, get the associated loop_ids
            with self.session() as session:
                query = session.query(mod.MlLoops.loop_id).\
                     filter(mod.MlLoops.motif_id == motif)
                loop_ids = [r.loop_id for r in query]

            loop_ids = list(set(loop_ids))     # remove any duplicates
            print("Unique loop ids in motif group %s" % motif)
            print(loop_ids)

            # For each loop in this motif, check for loop instance annotations, otherwise use the motif annotation (if it exists)
            # This code should handle the cases that the loop has either annotation1, annotation2, or neither
            for loop in loop_ids:
                # If a loop in this motif does not have a loop instance annotation1, then use motif_annotation1 given that motif_annotation1 exists
                if loop not in annotated_loops:
                    if motif in motif_annotation1:
                        loop_annotation1[loop] = motif_annotation1[motif]
                    else:
                        loop_annotation1[loop] = ["NULL", "NULL"]
                    if motif in motif_annotation2:
                        loop_annotation2[loop] = motif_annotation2[motif]
                    else:
                        loop_annotation2[loop] = ["NULL", "NULL"]

                    #Add completed loop_entry to loop_data
                    loop_data.append(mod.LoopAnnotations(
                        loop_id = loop,
                        annotation_1 = loop_annotation1[loop][0],
                        annotation_2 = loop_annotation2[loop][0],
                        author = loop_annotation1[loop][1]))

        return loop_data

    def data(self, loop_type, **kwargs):
        """ Get the loop annotations from the spreadsheet.
        Return a list of all loop annotations. """

        print("annotations.py running data method")

        #Get loop annotations
        data = []
        data = self._get_loop_annotations(loop_type)

        #Return data to be written to the database.
        if not data:
            self.logger.error("No loop annotations found.")
        else:
            return data


from typing import TYPE_CHECKING, Any

from azure.core.pipeline.policies import HTTPPolicy

from ._anonymous_exchange_client import AnonymousACRExchangeClient
from ._exchange_client import ACRExchangeClient
from ._helpers import _enforce_https

if TYPE_CHECKING:
    from azure.core.credentials import TokenCredential
    from azure.core.pipeline import PipelineRequest, PipelineResponse

class ContainerRegistryChallengePolicy(HTTPPolicy):
    """Authentication policy for ACR which accepts a challenge"""

    def __init__(self, credential, endpoint, **kwargs):
        # type: (TokenCredential, str, **Any) -> None
        super(ContainerRegistryChallengePolicy, self).__init__()
        self._credential = credential
        if self._credential is None:
            self._exchange_client = AnonymousACRExchangeClient(endpoint, **kwargs)
        else:
            self._exchange_client = ACRExchangeClient(endpoint, self._credential, **kwargs)

    def on_request(self, request):
        # type: (PipelineRequest) -> None
        """Called before the policy sends a request.
        The base implementation authorizes the request with a bearer token.
        :param ~azure.core.pipeline.PipelineRequest request: the request
        """
        # Future caching implementation will be included here
        pass  # pylint: disable=unnecessary-pass

    def send(self, request):
        # type: (PipelineRequest) -> PipelineResponse
        """Authorizes a request with a bearer token, possibly handling an authentication challenge
        :param ~azure.core.pipeline.PipelineRequest request: the request
        """
        _enforce_https(request)

        self.on_request(request)

        response = self.next.send(request)

        if response.http_response.status_code == 401:
            challenge = response.http_response.headers.get("WWW-Authenticate")
            if challenge and self.on_challenge(request, response, challenge):
                response = self.next.send(request)

        return response

    def on_challenge(self, request, response, challenge):
        # type: (PipelineRequest, PipelineResponse, str) -> bool
        """Authorize request according to an authentication challenge
        This method is called when the resource provider responds 401 with a WWW-Authenticate header.
        :param ~azure.core.pipeline.PipelineRequest request: the request which elicited an authentication challenge
        :param ~azure.core.pipeline.PipelineResponse response: the resource provider's response
        :param str challenge: response's WWW-Authenticate header, unparsed. It may contain multiple challenges.
        :returns: a bool indicating whether the policy should send the request
        """
        # pylint:disable=unused-argument,no-self-use

        access_token = self._exchange_client.get_acr_access_token(challenge)
        request.http_request.headers["Authorization"] = "Bearer " + access_token
        return access_token is not None

    def __enter__(self):
        self._exchange_client.__enter__()
        return self

    def __exit__(self, *args):
        self._exchange_client.__exit__(*args)