print(__file__)

"""other signals"""

aps = APS_devices.ApsMachineParametersDevice(name="APS")

# make sure these values are logged by every scan
sd.baseline.append(aps)

# always record current as secondary stream during scans
# name: aps_current_monitor
# db[-1].table("aps_current_monitor")
aps_current = aps.current
sd.monitors.append(aps_current)

scans = APS_devices.SscanDevice("sky:", name="scans")
calcs = APS_devices.UserCalcsDevice("sky:", name="calcs")
calcs.enable.put("Enable")
calc1 = calcs.calc1


import numpy as np
import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.decomposition.pca import PCA

def plot_embeddings(embeddings, labels, protecteds,
                    plot3d=False,
                    subsample=False,
                    label_names=None,
                    protected_names=None):
    if protected_names is None:
        protected_names = ["A0", "A1"]
    if label_names is None:
        label_names = ["L0", "L1"]
    n = embeddings.shape[0]
    if not subsample:
        subsample = n
    inds = np.random.permutation(n)[:subsample]
    pca = PCA(n_components= 3 if plot3d else 2)
    labels = labels.astype(bool)[inds]
    protecteds = protecteds.astype(bool)[inds]
    pca.fit(embeddings)
    embs = pca.transform(embeddings)[inds, :]
    fig = plt.figure()
    if plot3d:
        ax = fig.add_subplot(111, projection='3d')
    else:
        ax = fig.add_subplot(111)
    for l in [False, True]:# labels
        for p in [False, True]: # protecteds
            idxs = np.logical_and(labels == l, protecteds == p)
            embs_slice = embs[idxs, :]
            data_vectors = [embs_slice[:, 0], embs_slice[:, 1]]
            if plot3d: data_vectors.append(embs_slice[:,2])
            color = "b" if p else "r"
            marker = "o" if l else "x"
            name = "{} {}".format(protected_names[p],
                                  label_names[l])
            ax.scatter(*data_vectors,
                        edgecolors=color,
                        marker=marker,
                        facecolors=[color, 'none'][l], # only leave circles unfilled
                        label=name)
    ax.legend(fontsize="small")
    plt.show()


import dash
import dash_auth

def extract_creds(file='hist_auth.txt'):
    with open(file, 'r') as f:
        return [f.read().split(',')]

creds = extract_creds()
app = dash.Dash('auth')
auth = dash_auth.BasicAuth(app, creds)

server = app.server
app.config.suppress_callback_exceptions = True


# PygameProject.py

import pygame
import random
pygame.font.init()

# Global constants
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
WIDTH = 800
HEIGHT = 600
FONT = pygame.font.SysFont("Comic Sans", 50)
HEART = pygame.transform.scale(pygame.image.load("./assets/heart.png"), (35, 35))

class Player(pygame.sprite.Sprite):
    """" This class represents the sprite that the player controls. """

    def __init__(self):
        super().__init__()

        # Set the image of the sprite and scale it
        self.image = pygame.image.load("./assets/kirby.png")
        self.image = pygame.transform.scale(self.image, (40, 40))

        # Make a hitbox
        self.rect = self.image.get_rect()

        # Set the speed vector of player
        self.change_x = 0
        self.change_y = 0

        # Set the starting point
        self.rect.x = (WIDTH/2)
        self.rect.y = (HEIGHT/2)

    def update(self):
        """" Moves the player (arrow keys). """

        # Move the player according to its speed/vectors
        self.rect.x += self.change_x
        self.rect.y += self.change_y

        if self.rect.x < 0:
            self.rect.x = WIDTH
        if self.rect.x > WIDTH:
            self.rect.x = 0
        if self.rect.y < 0:
            self.rect.y = HEIGHT
        if self.rect.y > HEIGHT:
            self.rect.y = 0

    # Player-controlled movement:
    def go_left(self):
        """ Called when the user hits the left arrow. """
        self.change_x = -6

    def go_right(self):
        """ Called when the user hits the right arrow. """
        self.change_x = 6

    def go_up(self):
        """ Called when the user hits the up arrow. """
        self.change_y = -6

    def go_down(self):
        """ Called when the user hits the down arrow. """
        self.change_y = 6

    def stop_horizontal(self):
        """ Called when the user lets go of the left or right. """
        self.change_x = 0

    def stop_vertical(self):
        """ Called when the user lets go of up or down. """
        self.change_y = 0

class Bullet(pygame.sprite.Sprite):
    """" This class represents the bullets that will be launched at the player"""

    def __init__(self):
        super().__init__()

        # Set the image of the sprite and scale it
        self.image = pygame.image.load("./assets/bullet.png")
        self.image = pygame.transform.scale(self.image, (21, 21))

        # Make a hitbox
        self.rect = self.image.get_rect()

        # Set the speed vector of bullet
        self.change_x = random.randrange(-4, 4)
        self.change_y = random.randrange(-4, 4)

        if self.change_x == 0:
            self.change_x += 1
        elif self.change_y == 0:
            self.change_y += 1

        # Set the start point
        if self.change_x > 0:
            self.rect.x = random.randrange(0, 50)
        else:
            self.rect.x = random.randrange(WIDTH - 50, WIDTH)

        if self.change_y > 0:
            self.rect.y = random.randrange(0, 50)
        else:
            self.rect.y = random.randrange(HEIGHT - 50, HEIGHT)

    def update(self):
        """" Moves the bullet """

        # Move the bullet according to its speed/vectors
        self.rect.x += self.change_x
        self.rect.y += self.change_y

def main():
    """ Main Program. """
    pygame.init()

    # ----- SCREEN PROPERTIES -----
    size = (WIDTH, HEIGHT)
    screen = pygame.display.set_mode(size)
    pygame.display.set_caption("Dodgeball")

    # ----- LOCAL VARIABLES -----
    done = False
    clock = pygame.time.Clock()
    num_bullets = 15
    lives = 3
    score = 0

    # Create sprite groups and fill them
    all_sprites_group = pygame.sprite.Group()
    bullet_sprites_group = pygame.sprite.Group()
    player = Player()
    all_sprites_group.add(player)

    for i in range(num_bullets):
        bullet = Bullet()
        all_sprites_group.add(bullet)
        bullet_sprites_group.add(bullet)

    # ----- MAIN LOOP -----
    while not done:

        # -- Event Handler -----
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True

            # Make the player move around depending on the arrow keys pressed down
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    player.go_left()
                if event.key == pygame.K_RIGHT:
                    player.go_right()
                if event.key == pygame.K_UP:
                    player.go_up()
                if event.key == pygame.K_DOWN:
                    player.go_down()

            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT and player.change_x < 0:
                    player.stop_horizontal()
                if event.key == pygame.K_RIGHT and player.change_x > 0:
                    player.stop_horizontal()
                if event.key == pygame.K_UP and player.change_y < 0:
                    player.stop_vertical()
                if event.key == pygame.K_DOWN and player.change_y > 0:
                    player.stop_vertical()

        # ----- LOGIC -----
        all_sprites_group.update()

        # Collide the player with any bullets
        collisions = pygame.sprite.spritecollide(player, bullet_sprites_group, dokill=True)

        # take away a life for every collision and lower the current number of bullets
        for collision in collisions:
            num_bullets -= 1
            lives -= 1

        # If a bullet goes out of bounds, delete it and lower the current number of bullets
        for bullet in bullet_sprites_group:
            if bullet.rect.x < 0 or bullet.rect.x > WIDTH:
                bullet.kill()
                num_bullets -= 1

            if bullet.rect.y < 0 or bullet.rect.y > HEIGHT:
                bullet.kill()
                num_bullets -= 1

        # Make sure there are always ten bullets
        if num_bullets < 15:

            # Replace the bullet
            bullet = Bullet()
            all_sprites_group.add(bullet)
            bullet_sprites_group.add(bullet)
            num_bullets += 1

        # ----- RENDER -----
        screen.fill(BLACK)
        all_sprites_group.draw(screen)

        # If the player is still alive
        if lives > 0:
            # Add 1 to the score every tick
            score += 1

            # Display a timer at the bottom right as the score
            screen.blit(FONT.render(f'Score: {score}', True, GREEN), (20, HEIGHT - 80))

        # If the player has three lives
        if lives == 3:
            # Display three hearts
            screen.blit(HEART, (0, 0))
            screen.blit(HEART, (45, 0))
            screen.blit(HEART, (90, 0))

        # If the player has two lives
        if lives == 2:
            # Display two hearts
            screen.blit(HEART, (0, 0))
            screen.blit(HEART, (45, 0))

        # If the player has one life left
        if lives == 1:
            # Display one heart
            screen.blit(HEART, (0, 0))

        # If the player runs out of lives
        if lives <= 0:
            # Kill the player
            player.kill()

            # Display a Game Over Screen and the final score
            screen.blit(FONT.render('Game Over', True, GREEN), ((WIDTH/2) - 120, (HEIGHT/2) - 50))
            screen.blit(FONT.render(f'Score: {score}', True, GREEN), (20, HEIGHT - 80))

        # ----- UPDATE DISPLAY -----
        pygame.display.flip()
        clock.tick(60)

    pygame.quit()

if __name__ == "__main__":
    main()


"""
Configure gh-pipelines virtual enviroments
"""
import threading
import logging

from argparse import ArgumentParser
from utils.venv_tool import VenvTool, VenvTag
from typing import List

def setup_env(venv_tag: VenvTag) -> None:
    ''' Configure a single environment '''
    env_tool = VenvTool(venv_tag)
    env_tool.configure()

def parse(args: List[str]) -> None:
    ''' Configure gh-pipline environments '''
    parser = ArgumentParser(
        prog="configure",
        description="Configure gh-pipeline virtual enviroments"
    )

    parser.add_argument(
        'selected_tags',
        help="environment options",
        nargs='*'
    )

    command_args = parser.parse_args(args)

    venv_tags = [t for t in VenvTag]

    if command_args.selected_tags:
        tag_strings = [t.name for t in venv_tags]
        actual_set = set(tag_strings)
        selected_set = set(command_args.selected_tags)
        invalid_tags = selected_set - actual_set
        valid_tags = selected_set.intersection(actual_set)
        for tag in invalid_tags:
            message = '{} is not valid environment option.'
            logging.warning(message.format(tag))
        venv_tags = [VenvTag[st] for st in valid_tags]

    threads = []
    for tag in venv_tags:
        config_thread = threading.Thread(
            target=setup_env,
            args=(tag,),
            daemon=True
        )
        threads.append(config_thread)
        config_thread.start()

    for thread in threads:
        thread.join()


# Tom Krystian Noodt 147
# Kristoffer Sørensen 140
# Najim Zaouaghi 161
# Slavko Radisevic 132

#Noen av funksjonene er tatt utgangspunkt i gruppens tidligere levering i PRG1000R oblig2
#Passer på at filene blir oppretet visst de ikke eksisterer fra før av
toppBildefil = open ('Bilde10.txt', 'a')
toppTag = open('TagForBilde10.txt','a')
toppKommentar = open ('Kommentar10.txt','a')
toppBruker = open ('Bruker10.txt', 'a')
toppLikes = open ('Likes10.txt', 'a')
toppEmne = open ('Emneknagg10.txt','a')

toppBildefil.close ()
toppTag.close ()
toppKommentar.close ()
toppBruker.close ()
toppLikes.close ()
toppEmne.close ()

### Hovedmeny ###
def main ():

    programkjorer=True
    while programkjorer:
        #Meny design og oversikt over eksisterende funksjoner
        print()
        print("//**Administrasjonssystem for bildedelingstjeneste**//")
        print()
        print("For valg av operasjon: skriv inn en av følgende kommandoer:")
        print("----------------")
        print("Registrere: \nBruker: 1\nBilde: 2\nKommentar: 3\nLike: 4\nEmneknagg: 5\nTagForBilde : 6")
        print("----------------")
        print("Slette: \nBruker: 7\nBilde: 8\nKommentar: 9")
        print("----------------")
        print("Vis: \nBilder og fotograf: 10\nAntall bilder per bruker: 11\nAntall likes på bilder: 12\nKommentarer på et bilde: 13")
        print("Bilde og fotograf via emneknagg: 14\nKommentarer via emneknagg: 15")
        print("----------------")
        print("Avslutt: 16")
        print("----------------")

        print('Skriv inn ønsket funksjon:')
        operasjon = input ('')

        #Meny for valg av funksjoner
        if operasjon == '1':
            registrerBruker ()
        elif operasjon == '2':
            registrerBilde ()
        elif operasjon == '3':
            registrerKommentar ()
        elif operasjon == '4':
            registrerLike ()
        elif operasjon == '5':
            registrerEmneknagg ()
        elif operasjon == '6':
            registrerTagForBilde ()
        elif operasjon == '7':
            slettBruker ()
        elif operasjon == '8':
            slettBilde ()
        elif operasjon == '9':
            slettKommentarer ()
        elif operasjon == '10':
            visBildeFotograf ()
        elif operasjon == '11':
            visBrukereBilde ()
        elif operasjon == '12':
            visLikesBilde ()
        elif operasjon == '13':
            visAlleKommentarer () #Det er mulighet for at denne skaper bugs
        elif operasjon == '14':
            visEmneknaggBildeFotograf ()
        elif operasjon == '15':
            visInfoEmneknagg ()
        elif operasjon == '16':
            programkjorer = False

    print ('Program avsluttet')

####################################################
### Registrer-funksjoner ###
def registrerBruker ():
    print ('-------------------------------------------------------')
    print ('----- Kjører operasjon registrer bruker -----')
    print ('-------------------------------------------------------\n')

    nyInndata = True
    while nyInndata == True:

        # Ber brukeren oppgi nødvendig informasjon
        brukerID = input ('Bruker: ')
        fornavn = input ('Fornavn: ')
        etternavn = input ('Etternavn: ')
        epost = input ('Epost: ')

        # Sjekker om input oppfyller krav, hvis ja skrives input til filen
        if  finnesBruker (brukerID) ==False:
            brukerfil = open ('Bruker10.txt', 'a')
            brukerfil.write (brukerID + '\n')
            brukerfil.write (fornavn + '\n')
            brukerfil.write (etternavn + '\n')
            brukerfil.write (epost + '\n')

            brukerfil.close ()

            print('Bruker registrert!\n')
        else:
            print('Bruker allerede registrert!\n')

        print ('Ønsker du å registrere en ny bruker? (Ja/ja)')
        svar = input ('')

        if svar == 'ja' or svar == 'Ja':
            nyInndata = True
        else:
            nyInndata = False

def registrerBilde ():
    print ('-------------------------------------------------------')
    print ('----- Kjører operasjon registrer bilde -----')
    print ('-------------------------------------------------------\n')

    # Ber brukeren oppgi nødvendig informasjon
    print('For å registere et bilde må du være en registrert bruker')
    brukerID = input ('Bruker: ')
    bildeID = input ('Bilde: ')
    beskrivelse = input ('Beskriv bildet: ')
    opplastetDato = input ('Skriv inn dato for bildet: ')

    # Kjører tester for å sjekke om bilde og bruker eksisterer
    # for å gi tilbakemelding til bruker
    if finnesBruker (brukerID) ==False:
        print('Bruker eksisterer ikke!')
    if finnesBilde (bildeID) ==True:
        print('Bilde eksisterer allerede!')

    # Sjekker om input oppfyller krav, hvis ja skrives input til filen
    if finnesBruker (brukerID) ==True and finnesBilde (bildeID) ==False:
        bildefil = open ('Bilde10.txt', 'a')

        bildefil.write (bildeID + '\n')
        bildefil.write (beskrivelse + '\n')
        bildefil.write (opplastetDato + '\n')
        bildefil.write (brukerID + '\n')

        bildefil.close ()

        print('Bilde registrert!')

def registrerKommentar ():
    print ('-------------------------------------------------------')
    print ('----- Kjører operasjon registrer kommentar -----')
    print ('-------------------------------------------------------\n')

    print('For å registere en kommentar må du ha en bruker og ha riktig bilde')

    # Ber brukeren oppgi nødvendig informasjon
    brukerID = input ('Bruker: ')
    bildeID = input ('Bilde: ')
    kommentar = input ('Skriv inn kommentar på bildet: ')

    # Kjører tester for å sjekke om bilde og bruker eksisterer
    # for å gi tilbakemelding til bruker
    if finnesBruker (brukerID) ==False:
        print('Bruker eksisterer ikke!')
    if finnesBilde (bildeID) ==False:
        print('Bilde eksisterer ikke!')

    # Sjekker om input oppfyller krav, hvis ja skrives input til filen
    if finnesBruker (brukerID) ==True and finnesBilde (bildeID) ==True:
        kommentarfil = open ('Kommentar10.txt', 'a')

        kommentarfil.write (bildeID + '\n')
        kommentarfil.write (brukerID + '\n')
        kommentarfil.write (kommentar + '\n')

        kommentarfil.close ()

        print('Kommentar registrert!')

def registrerLike ():
    # Funksjon for registrering av likes
    print ('-------------------------------------------------------')
    print ('----- Kjører operasjon registrer like -----')
    print ('-------------------------------------------------------\n')

    print('For å registere en like må du være en registrert bruker og ha et opplastet bilde')

    # Ber brukeren oppgi nødvendig informasjon
    bildeID = input ('Bilde: ')
    brukerID = input ('Bruker: ')

    # Kjører tester for å sjekke om bilde, bruker og like eksisterer
    # for å gi tilbakemelding til bruker
    if finnesBilde (bildeID) ==False:
        print('Bilde eksisterer ikke!')
    if finnesBruker(brukerID) ==False:
        print('Bruker eksisterer ikke!')
    if finnesLikeAllerede(brukerID, bildeID) ==True:
        print('Like for bilde eksisterer allerede!')

    # Sjekker om alle kravene er oppfylt for å så skrive til filen
    if finnesLikeAllerede(brukerID, bildeID) ==False and finnesBilde(bildeID) ==True and finnesBruker (brukerID) ==True:
        likesfil = open ('Likes10.txt', 'a')
        likesfil.write (bildeID + '\n')
        likesfil.write (brukerID + '\n')
        likesfil.close ()

        print('Like registrert!')

def registrerEmneknagg ():
    print ('-------------------------------------------------------')
    print ('----- Kjører operasjon registrer emneknagg -----')
    print ('-------------------------------------------------------\n')

    ny_emne=True
    while ny_emne==True:

        # Ber brukeren registrere ønsket emneknagg
        emneknaggID = input('Skriv inn emneknagg-id: ')
        emneknaggNavn = input ('Skriv inn emneknagg-navn: ')

        # Sjekker om kravet er oppfylt for å så skrive til filen
        if finnesEmneknagg(emneknaggID) ==False:
            emneknaggfil = open ('Emneknagg10.txt', 'a')
            emneknaggfil.write (emneknaggID + '\n')
            emneknaggfil.write (emneknaggNavn + '\n')

            emneknaggfil.close ()

            print('Emneknagg registrert!\n')

        else:
            print ('Emneknagg finnes fra før')
        print ('-------------------------------------------------------\n')

        svar=input('Registrere ny emneknagg? ')
        if svar=='Nei' or svar=='nei':
            ny_emne=False

def registrerTagForBilde ():
    print ('-------------------------------------------------------')
    print ('----- Kjører operasjon tag for bilde -----')
    print ('-------------------------------------------------------\n')

    print('For å registere en tag må du ha riktig bilde og emneknaggen må være registrert')

    # Ber brukeren oppgi nødvendig informasjon
    bildeID = input ('Bilde: ')
    emneknaggID = input ('Skriv inn emneknagg-id: ')

    # Kjører tester for å sjekke om bilde og emneknagg eksisterer
    # for å gi tilbakemelding til bruker
    if finnesBilde (bildeID) ==False:
        print('Bilde eksistrer ikke!')
    if finnesEmneknagg(emneknaggID) ==False:
        print('Emneknaggen eksisterer ikke!')

    # Sjekker om alle kravene er oppfylt for å så skrive til filen
    if finnesBilde(bildeID) ==True and finnesEmneknagg (emneknaggID) ==True:
        tagforbildefil = open ('TagForBilde10.txt', 'a')

        tagforbildefil.write (bildeID + '\n')
        tagforbildefil.write (emneknaggID + '\n')

        tagforbildefil.close ()

        print('Emneknagg lagt til på bilde!')

####################################################
### Slett-funksjoner ###
def slettBruker ():
    """Slett bruker ved bruk av temp-fil"""

    #importerer os for å kunne erstatte originalfil med tempfil
    import os
    nyInndata = True
    while nyInndata == True:
        print ('-------------------------------------------------------')
        print ('----- Kjører operasjon slett bruker -----')
        print ('-------------------------------------------------------\n')

        brukerID = input ('BrukerID: ')

        # Sjekk om brukeren finnes, og om det er registrert aktivitet på brukerID
        if finnesLike (brukerID)==False and finnesBilde (brukerID) == False and finnesKommentar (brukerID) == False and finnesBruker (brukerID) == True:
            print ('Starter sletting av bruker...')

            brukerfil = open ('Bruker10.txt', 'r')
            tempfil = open ('tempfil.txt', 'w')

            linje = brukerfil.readline ()
            while linje != '':

                filFornavn = brukerfil.readline ()
                filEtternavn = brukerfil.readline ()
                filEpost = brukerfil.readline ()

                filBrukerID = linje.rstrip ('\n')
                # Skriver inn linjene, for bruker som ikke skal slettes, til temp-fil
                if filBrukerID != brukerID:

                    tempfil.write (filBrukerID + '\n')
                    tempfil.write (filFornavn)
                    tempfil.write (filEtternavn)
                    tempfil.write (filEpost)

                # Leser neste linje i fila, som da vil være neste brukerID eller blank
                linje = brukerfil.readline ()

            brukerfil.close ()
            tempfil.close ()

            # Erstatter den originale Bruker10.txt-filen med tempfilen
            os.remove ('Bruker10.txt')
            os.rename ('tempfil.txt', 'Bruker10.txt')
            print ('Bruker fjernet!')

        elif finnesBruker(brukerID) == False:

            print ('Brukeren du ønsker å slette finnes ikke')
        else:
            print ('Brukeren kan ikke slettes grunnet følgende registrert aktivitet fra denne brukeren:')
            # Her kommer det if tester for å gi info om hvilke krav som ikke er oppfyllt
            if finnesLike (brukerID) == True:
                print ('Likes')
            if finnesBilde (brukerID) == True:
                print ('Bilder')
            if finnesKommentar (brukerID) == True:
                print ('Kommentarer')
        print ('-------------------------------------------------------\n')

        # Spør om det ønskes å slette flere brukere
        print ('Ønsker du å slette flere brukere? (Ja/ja)')
        svar = input ('')

        if svar == 'ja' or svar == 'Ja':
            nyInndata = True
        else:
            nyInndata = False

def slettBilde ():
    """Slett bilde (+likes +kommentarer) ved bruk av temp-fil"""

    # Importerer os for å kunne erstatte originalfil med tempfil
    import os
    nyInndata = True
    while nyInndata == True:
        print ('-------------------------------------------------------')
        print ('----- Kjører operasjon slett bilde -----')
        print ('-------------------------------------------------------\n')

        bildeID = input ('Bilde-ID: ')

        # Sjekk om brukeren finnes, og om det er registrert aktivitet på brukerID
        if finnesBilde (bildeID) == True:

            #Sjekk om det finnes bilder og kommentarer, hvis det gjør det, kjør funksjonene
            finnerViserKommentarer (bildeID, False)
            visAlleLikes (bildeID)

            print ('All likes og kommentarer i listen over vil bli slettet, ønsker du fortsatt å slette bildet?(ja)')
            svar = input ('')

            if svar == "ja":

                #Sletting av bilde
                bildefil = open('Bilde10.txt', 'r')
                tempfil = open('temp.txt', 'w')

                filBildeID = bildefil.readline()

                while filBildeID != '':
                    filBeskrivelse = bildefil.readline()
                    filOpplastetDato = bildefil.readline()
                    filFotograf = bildefil.readline()

                    filBildeID = filBildeID.rstrip('\n')

                    if filBildeID != bildeID:
                        tempfil.write (filBildeID + '\n')
                        tempfil.write (filBeskrivelse)
                        tempfil.write (filOpplastetDato)
                        tempfil.write (filFotograf)

                    filBildeID = bildefil.readline()

                bildefil.close()
                tempfil.close()

                os.remove('Bilde10.txt')
                os.rename('temp.txt', 'Bilde10.txt')

                ### Del for fjerning av likes ###
                slettAlleLikes (bildeID)
                ### Del for fjerning av kommentarer ###
                slettAlleKommentarer (bildeID)
        else:
            print ('Bildet finnes ikke og kan derfor ikke slettes.')
        print ('-------------------------------------------------------\n')

        # Spør om det ønskes å slette flere bilder
        print ('Ønsker du å slette et annet bilde? (Ja/ja)')
        svar = input ('')

        if svar == 'ja' or svar == 'Ja':
            nyInndata = True
        else:
            nyInndata = False

def slettKommentarer ():
    print ('-------------------------------------------------------')
    print ('---- Kjører operasjon slett kommentarer av bruker -----')
    print ('-------------------------------------------------------\n')

    print ('Skriv inn bruker-id for bruker som ønsker kommentarer slettet')
    brukerID = input ('')
    print ('Skriv inn bruker-id for brukers kommentarer som skal slettes')
    sBrukerID = input ('')

    import os

    funnet = False

    kommentarfil = open ('Kommentar10.txt', 'r')
    temp_file = open ('temp.txt', 'w')

    filBildeID = kommentarfil.readline ()

    while filBildeID != '':
        kommentarSkalSlettes = False

        filBrukerID = kommentarfil.readline()
        filKommentar = kommentarfil.readline()

        filBildeID = filBildeID.rstrip ('\n')
        filBrukerID = filBrukerID.rstrip('\n')

        if filBrukerID == sBrukerID:

            if erBrukerFotograf (filBildeID, brukerID) == True:
                kommentarSkalSlettes = True
                funnet = True

        #Her må kommentarene skrives inn hvis kommentaren ikke samsvarer
        if kommentarSkalSlettes == False:
            temp_file.write (filBildeID + '\n')
            temp_file.write (filBrukerID + '\n')
            temp_file.write (filKommentar)

        filBildeID = kommentarfil.readline ()

    kommentarfil.close ()
    temp_file.close ()
    os.remove('Kommentar10.txt')
    os.rename('temp.txt', 'Kommentar10.txt')

    if funnet == False:
        print ('Det er ingen kommentarer å slette fra denne brukeren!')
    else:
        print ('Kommentarer slettet!')

def erBrukerFotograf(bildeID, brukerID):
    """ Returnerer sant eller usant basert på om bilde finnes """
    bildefil = open ('Bilde10.txt', 'r')
    resultat = False

    bFilBildeID = bildefil.readline ()
    while bFilBildeID != '':

        bFilBeskrivelse = bildefil.readline ()
        bFilOpplastetDato = bildefil.readline ()
        bFilFotograf = bildefil.readline ()

        bFilBildeID = bFilBildeID.rstrip ('\n')
        bFilFotograf = bFilFotograf.rstrip ('\n')

        if (bildeID == bFilBildeID ) and (brukerID == bFilFotograf):
            resultat = True

        bFilBildeID = bildefil.readline ()
    bildefil.close ()

    return resultat
####################################################
### Vis-funksjoner ###

def visAlleKommentarer ():
    print ('-------------------------------------------------------')
    print ('--- Kjører operasjon vis alle kommmentarer på bilde ---')
    print ('-----------------------------------------------------\n')

    nyInndata = True
    while nyInndata == True:
        finnerViserKommentarer ('', True)

        print ('-----------------------------------------------------\n')

        # Spør om det ønkes å vise flere kommentarer
        print ('Ønsker du å vise kommentarer på et annet bilde? (Ja/ja)')
        svar = input ('')

        if svar == 'ja' or svar == 'Ja':
            nyInndata = True
        else:
            nyInndata = False

def visLikesBilde ():
    print ('-------------------------------------------------------')
    print ('----- Kjører operasjon vis antall likes per bilde -----')
    print ('-------------------------------------------------------\n')

    antallBilder = 0

    bildefil = open ('Bilde10.txt', 'r')
    linje = bildefil.readline ()

    # Teller likes for hver post funnet i fila
    while linje != '':
        antallBilder += 1

        bildeID = linje.rstrip ('\n')
        filBeskrivelse = bildefil.readline ()
        filOpplastetDato = bildefil.readline ()
        filFotograf = bildefil.readline ()

        likesfil = open ('Likes10.txt', 'r')
        linje = likesfil.readline ()

        likeTeller = 0
        while linje != '':
            filBrukerID = likesfil.readline ()

            filBildeID = linje.rstrip ('\n')
            filBrukerID = filBrukerID.rstrip ('\n')

            if filBildeID == bildeID:
                likeTeller +=1

            linje = likesfil.readline ()
        print ('Bilde-id:', bildeID)
        print ('Antall likes:', likeTeller, '\n')

        likesfil.close ()
        linje = bildefil.readline ()

    bildefil.close ()

    if antallBilder == 0:
        print ('Det er ingen bilder registrert i systemet!\n')

    print ('-------------------------------------------------------\n')

    # For å unngå visning av meny
    svar = input ('Trykk enter for å vise meny\n')

def visEmneknaggBildeFotograf ():
    print ('----------------------------------------------------------------')
    print ('----- Kjører operasjon vis bilde og fotograf via emneknagg -----')
    print ('--------------------------------------------------------------\n')

    #While loop som sjekker for om bruker ønsker å søke på flere emneknagger
    nyInndata = True
    while nyInndata == True:

        # Ber brukeren skrive inn emneknagg
        print ('Skriv inn emneknagg:')
        emneknaggnavn = input ('')
        print ('')

        resultat = False
        antSokeRes = 0

        emneknaggfil = open ('Emneknagg10.txt', 'r')

        linje = emneknaggfil.readline ()
        while linje != '':

            filEmneknaggnavn = emneknaggfil.readline ()

            filEmneKnaggID = linje.rstrip('\n')
            filEmneknaggnavn = filEmneknaggnavn.rstrip ('\n')

            if filEmneknaggnavn == emneknaggnavn:
                resultat = True
                emneknaggID = filEmneKnaggID

            linje = emneknaggfil.readline ()
        emneknaggfil.close ()

        if resultat == True:
            # Emneknaggnavnet finnes og emneknaggid er lagret i emneknaggID

            tagForBildefil = open ('TagForBilde10.txt', 'r')
            linje = tagForBildefil.readline ()
            while linje != '':

                filEmneknaggID =tagForBildefil.readline ()

                filBildeID = linje.rstrip ('\n')
                filEmneKnaggID = filEmneknaggID.rstrip ('\n')

                # Sjekk for om bildet i posten har emneknaggen vi leter etter

                if emneknaggID == filEmneKnaggID:
                    bildeID = filBildeID
                    # Finne forfatter info fra Bilde10.txt

                    bildefil = open ('Bilde10.txt', 'r')
                    linje = bildefil.readline ()
                    while linje != '':

                        filBeskrivelse = bildefil.readline ()
                        filOpplastetDato = bildefil.readline ()
                        filFotograf = bildefil.readline ()

                        filBildeID = linje.rstrip ('\n')
                        if filBildeID == bildeID:
                            fotograf = filFotograf.rstrip ('\n')

                            print ('Bilde-id:', bildeID, ', Fotograf:', fotograf)
                            print ()

                        linje = bildefil.readline ()
                    bildefil.close ()

                linje = tagForBildefil.readline ()

            tagForBildefil.close ()
        else:
            print ('Emneknaggen finnes ikke\n')

        print ('-------------------------------------------------------')

        # Spør om det ønskes å søke i ny emneknagg
        print ('Ønsker du å søke på en annen emneknagg? (Ja/ja)')
        svar = input ('')

        if svar == 'ja' or svar == 'Ja':
            nyInndata = True
        else:
            nyInndata = False

def visBrukereBilde():
    print ('-----------------------------------------------------------')
    print ('--- Kjører operasjon for å vise antall bilder pr bruker ---')
    print ('---------------------------------------------------------\n')

    brukerfil=open('Bruker10.txt','r')
    brukerID = brukerfil.readline()

    while brukerID !='':
        bildeTeller = 0
        brukerID = brukerID.rstrip('\n')
        fornavn = brukerfil.readline()
        etternavn = brukerfil.readline()
        epost = brukerfil.readline()

        bildefil = open ('Bilde10.txt', 'r')
        bildeID = bildefil.readline()

        while bildeID !='':
            beskrivelse = bildefil.readline()
            opplastetDato = bildefil.readline()
            fotograf = bildefil.readline()

            fotograf = fotograf.rstrip ('\n')

            if brukerID == fotograf:
                bildeTeller +=1

            bildeID = bildefil.readline()
        print ('BrukerID: ',brukerID)
        print ('Antall bilder: ',bildeTeller)
        print ('')

        bildefil.close()

        brukerID = brukerfil.readline()
    brukerfil.close()

    print ('-------------------------------------------------------\n')

    # For å unngå visning av meny
    svar = input ('Trykk enter for å vise meny\n')

def visInfoEmneknagg ():
    print ('---------------------------------------------------------------')
    print ('------- Kjører operasjon vis kommentarer via emneknagg --------')
    print ('-------------------------------------------------------------\n')

    # While loop som sjekker for om bruker ønsker å søke på flere emneknagger
    nyInndata = True
    while nyInndata == True:
        print ('Skriv inn emneknaggnavn:')
        emneknaggnavn = input ('')
        print ('')

        emneknaggfil = open ('Emneknagg10.txt', 'r')
        resultat = False
        antSokeRes = 0

        linje = emneknaggfil.readline ()

        while linje != '':

            filEmneknaggnavn = emneknaggfil.readline ()

            filEmneKnaggID = linje.rstrip('\n')
            filEmneknaggnavn = filEmneknaggnavn.rstrip ('\n')

            if filEmneknaggnavn == emneknaggnavn:
                resultat = True
                emneKnaggID = filEmneKnaggID

            linje = emneknaggfil.readline ()
        emneknaggfil.close ()

        if resultat == True:
        # Nå er emneknaggnavnfunnet og emneknaggen finnes

            tagForBildefil = open ('TagForBilde10.txt', 'r')

            linje = tagForBildefil.readline ()
            while linje != '':

                filEmneKnaggID = tagForBildefil.readline ()

                filBildeID = linje.rstrip ('\n')
                filEmneKnaggID = filEmneKnaggID.rstrip ('\n')

                if filEmneKnaggID == emneKnaggID:
                    # Her har vi funnet et nytt bilde
                    bildeID = filBildeID

                    kommentarfil = open('Kommentar10.txt', 'r')

                    linje = kommentarfil.readline ()
                    while linje != '':
                        filBrukerID = kommentarfil.readline ()
                        filKommentar = kommentarfil.readline ()

                        filbildeID = linje.rstrip ('\n')
                        filBrukerID = filBrukerID.rstrip ('\n')
                        filKommentar = filKommentar.rstrip ('\n')

                        if filbildeID == bildeID:

                            # bildeID er funnet
                            brukerID = filBrukerID
                            kommentar = filKommentar
                            # Mangler fornavn og etternavn
                            navnFunnet=False

                            brukerfil = open ('Bruker10.txt', 'r')
                            linje = brukerfil.readline ()
                            while linje !='':

                                filFornavn = brukerfil.readline ()
                                filEtternavn = brukerfil.readline ()
                                filEpost = brukerfil.readline ()

                                filBrukerID = linje.rstrip ('\n')
                                filFornavn = filFornavn.rstrip ('\n')
                                filEtternavn = filEtternavn.rstrip ('\n')

                                if filBrukerID == brukerID:
                                    fornavn = filFornavn
                                    etternavn = filEtternavn
                                    navnFunnet = True

                                linje = brukerfil.readline ()
                            brukerfil.close ()
                            # Nå er fornavn etternavn funnet og alle variablene kan printes.

                            print ('Bilde-id:', bildeID)
                            print ('Bruker-id:', brukerID, ', Fornavn:', fornavn, ', Etternavn:', etternavn)
                            print ('Kommentar:', kommentar, '\n')

                            antSokeRes +=1

                        linje = kommentarfil.readline ()
                    kommentarfil.close ()

                linje = tagForBildefil.readline ()
            tagForBildefil.close ()
        else:
            print ('Emneknaggen finnes ikke')

        print ('Antall resultater funnet i søket:', antSokeRes)
        print ('---------------------------------------------------------------\n')

        # Spørsmål om søk på ny emneknagg
        print ('Ønsker du å vise kommentarer på et annet bilde? (Ja/ja)')
        svar = input ('')

        if svar == 'ja' or svar == 'Ja':
            nyInndata = True
        else:
            nyInndata = False

def visBildeFotograf():
    print ('----------------------------------------------------------------------')
    print ('----- Kjører operasjon å vise bilder med informasjon om fotograf -----')
    print ('--------------------------------------------------------------------\n')

    bildefil = open ('Bilde10.txt', 'r')
    bildeID = bildefil.readline()

    while bildeID !='':
        filbeskrivelse = bildefil.readline ()
        filOpplastetDato = bildefil.readline ()
        filFotograf = bildefil.readline ()

        bildeID = bildeID.rstrip('\n')

        print('BildeID: ',bildeID)
        print('Fotograf: ',filFotograf)

        bildeID = bildefil.readline()

    bildefil.close()

    print ('-------------------------------------------------------\n')
    # For å unngå visning av meny
    svar = input ('Trykk enter for å vise meny\n')

####################################################
### Finnes-funksjoner ###

def finnesBilde (bildeID):
    """ Returnerer sant eller usant basert på om bilde finnes """

    bildefil = open ('Bilde10.txt', 'r')
    resultat = False

    linje = bildefil.readline ()

    while linje != '':
        linje = linje.rstrip('\n')

        if linje == bildeID:
            resultat = True

        linje = bildefil.readline ()
    bildefil.close ()

    return resultat

def finnesBruker (brukerID):
    """ Returnerer sant eller usant basert på om bruker finnes """

    brukerfil = open ('Bruker10.txt', 'r')
    resultat = False

    linje = brukerfil.readline ()

    while linje != '':
        linje = linje.rstrip('\n')

        if linje == brukerID:
            resultat = True

        linje = brukerfil.readline ()
    brukerfil.close ()

    return resultat

def finnesKommentar (brukerID):
    """ Returnerer sant eller usant basert på om kommentar finnes """

    kommentarfil = open ('Kommentar10.txt', 'r')
    resultat = False

    linje = kommentarfil.readline ()

    while linje != '':
        linje = linje.rstrip('\n')

        if linje == brukerID:
            resultat = True

        linje = kommentarfil.readline ()
    kommentarfil.close ()

    return resultat

def finnesLike (brukerID):
    """ Returnerer sant eller usant basert på om like finnes """

    likefil = open ('Likes10.txt', 'r')
    resultat = False

    linje = likefil.readline ()

    while linje != '':
        linje = linje.rstrip('\n')

        if linje == brukerID:
            resultat = True

        linje = likefil.readline ()
    likefil.close ()

    return resultat

def finnesLikeAllerede (brukerID, bildeID):
    """ Sjekker om like på bilde eksisterer allerede og returnerer resultat """

    likefil = open ('Likes10.txt', 'r')
    resultat = False

    linje = likefil.readline ()
    while linje != '':
        filBrukerID = likefil.readline ()

        filBildeID = linje.rstrip('\n')
        filBrukerID = filBrukerID.rstrip('\n')

        if filBrukerID == brukerID and filBildeID == bildeID:
            resultat = True

        linje = likefil.readline ()

    likefil.close ()

    return resultat

def finnesEmneknagg (emneknaggID):
    """ Returnerer sant eller usant basert på om emneknagg finnes """

    emneknaggfil = open ('Emneknagg10.txt', 'r')
    resultat = False

    linje = emneknaggfil.readline ()

    while linje != '':
        filEmneknagg = emneknaggfil.readline ()

        linje = linje.rstrip('\n')
        filEmneknagg = filEmneknagg.rstrip ('\n')

        if linje == emneknaggID:
            resultat = True

        linje = emneknaggfil.readline ()
    emneknaggfil.close ()

    return resultat

####################################################
### Delfunksjoner av meny-funksjoner ###

def finnerViserKommentarer (bildeID, trengerID):
    # Denne delen er her for å kunne bruke funksjonen i både slettBilde og menyfunksjonen for visning av kommentarer for et gitt bilde.
    if trengerID == True:
        print ('Skriv inn bilde-ID: ')
        bildeID = input ('')
        print ('')
        if finnesBilde (bildeID) == False:
            print ('Bildet finnes ikke!')

    # Her starter funksjonen
    antKommentarer = 0
    kommentarFil = open ('Kommentar10.txt', 'r')

    linje = kommentarFil.readline ()
    while linje != '':
        filBildeID = linje.rstrip ()
        filBrukerID = kommentarFil.readline ()
        filKommentar = kommentarFil.readline ()

        filBrukerID = filBrukerID.rstrip ('\n')
        filKommentar = filKommentar.rstrip ('\n')

        # Hvis bildeID'en samsvarer med gitt bildeID, print kommentaren
        if filBildeID == bildeID:
            print ('Bruker:', filBrukerID)
            print (filKommentar, '\n')

            antKommentarer += 1
        linje = kommentarFil.readline ()

    kommentarFil.close ()

    if trengerID == False:
        print ('\nAntall Kommentarer registrert på bildet:', antKommentarer, '\n')

def visAlleLikes (bildeID):
    '''Brukes til å vise alle kommentarer for slettBilde () '''
    tempVariabel = 'temp'
    antKommentarer = 0
    likesfil = open ('Likes10.txt', 'r')

    linje = likesfil.readline ()
    while linje != '':
        filBildeID = linje.rstrip ()
        filBrukerID = likesfil.readline ()

        # Hvis bildeID'en samsvarer med gitt bildeID, print kommentaren
        if filBildeID == bildeID:
            print ('Bruker:', filBrukerID, '\n')

            antKommentarer += 1
        linje = likesfil.readline ()

    likesfil.close ()

    print ('\nAntall likes registrert på bildet:', antKommentarer, '\n')

def slettAlleKommentarer (bildeID):
    """ Funksjon som sletter alle kommentarer og likes på et gitt bilde """
    import os

    ### Del for å slette kommentarer ###
    kommentarfil = open('Kommentar10.txt', 'r')
    tempfil = open('temp.txt', 'w')

    filBildeID = kommentarfil.readline()

    while filBildeID != '':
        brukerid = kommentarfil.readline()
        kommentar = kommentarfil.readline()

        filBildeID = filBildeID.rstrip('\n')

        if filBildeID != bildeID:
            tempfil.write(filBildeID + '\n')
            tempfil.write(brukerid)
            tempfil.write(kommentar)

        filBildeID = kommentarfil.readline()

    kommentarfil.close()
    tempfil.close()

    os.remove('Kommentar10.txt')
    os.rename('temp.txt', 'Kommentar10.txt')

def slettAlleLikes (bildeID):
    """ Funksjon som sletter alle kommentarer og likes på et gitt bilde """
    import os

    likesfil = open('Likes10.txt', 'r')
    tempfil = open('temp.txt', 'w')

    filBildeID = likesfil.readline()

    while filBildeID != '':
        filBrukerID = likesfil.readline()

        filBildeID = filBildeID.rstrip('\n')

        if filBildeID != bildeID:
            tempfil.write(filBildeID + '\n')
            tempfil.write(filBrukerID)

        filBildeID = likesfil.readline()

    likesfil.close()
    tempfil.close()

    os.remove('Likes10.txt')
    os.rename('temp.txt', 'Likes10.txt')

main ()


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
import datetime
import logging.config

import cherrypy
from config import log_conf, conf

from modules import Rule
from modules import TinkerforgeConnection
from modules import BVG

log = logging.getLogger()  # the logger

##########################################################################################
# Data class to store global information
##########################################################################################

class Data(object):
    bvg = None
    active_rules = []
    tinkerforge_connection = None
    wakeup_time = "0:0"

##########################################################################################
# Rule logic
##########################################################################################

def watering_rule():
    now = datetime.datetime.now()
    if (now.hour == 9 and now.minute == 0) or (now.hour == 19 and now.minute == 0):
        log.info("It is {0!s}:{1!s}, started watering.".format(now.hour, now.minute))
        Data.tinkerforge_connection.switch_socket("nXN", 50, 1, 1)
        time.sleep(60)
        log.info("It is {0!s}:{1!s}, stopped watering.".format(now.hour, now.minute))
        Data.tinkerforge_connection.switch_socket("nXN", 50, 1, 0)

def balkony_rule():
    now = datetime.datetime.now()
    if now.hour == 17 and now.minute == 0:
        log.info("It is {0!s}:{1!s}, activated Balkonbeleuchtung.".format(now.hour, now.minute))
        Data.tinkerforge_connection.switch_socket("nXN", 50, 1, 1)
    if now.hour == 22 and now.minute == 0:
        log.info("It is {0!s}:{1!s}, deactivated Balkonbeleuchtung.".format(now.hour, now.minute))
        Data.tinkerforge_connection.switch_socket("nXN", 50, 1, 0)

def wakeup_rule():
    wakup = datetime.datetime.strptime(Data.wakeup_time, "%H:%M")
    now = datetime.datetime.now()
    if wakup.minute >= 10:
        time_until_wakup = wakup.minute - now.minute
        if wakup.hour == now.hour and time_until_wakup == 10:
            log.info("Beginning to light up the bedroom, wake up!")
            for i in range(15):
                Data.tinkerforge_connection.dim_socket("nXN", 25, 1, i)
                time.sleep(30)
    else:
        log.warn("We don't support wakup times with minute <10 currently.")

##########################################################################################
# The Webserver
##########################################################################################

class Webserver(object):
    """Every api the webserver provides should be in its own class. The webserver shall
    initialize these classes. Then, cherrypy mounts the webserver and the subclasses at
    the desired endpoints."""

    # initialize subclasses
    def __init__(self):
        self.socket = self.Socket()
        self.rule = self.Rule()
        self.additional_information = self.AdditionalInformation()

    # entrypoint to webpage
    @cherrypy.expose
    def index(self):
            raise cherrypy.HTTPRedirect("/static")

    class Socket:
        """Webserver class to control sockets and dimmer."""

        @cherrypy.expose
        def list(self):
            sockets = self.create_socket_entry("Schreibtischlampe", 30, 3)
            sockets += self.create_socket_entry("Hintergrundbeleuchtung", 30, 2)
            sockets += self.create_socket_entry("Laterne", 31, 2)
            sockets += self.create_dim_entry("Nachtlicht", 25, 1)
            sockets += self.create_socket_entry("Balkonbewässerung", 50, 1)
            return sockets

        @cherrypy.expose
        def switch(self, address=-1, unit=-1, state=-1):
            if address == -1 or unit == -1 or state == -1:
                return "Invalid information: {0!s}, {1!s}, {2!s}.".format(address, unit, state)
            Data.tinkerforge_connection.switch_socket("nXN", int(address), int(unit), int(state))
            log.info("Sending signal to switch socket {0!s}-{1!s} to {2!s}.".format(address, unit, state))

        @cherrypy.expose
        def dim(self, address=-1, unit=-1, dim_value=-1):
            if address == -1 or unit == -1 or dim_value == -1:
                return "Invalid information: {0!s}, {1!s}, {2!s}.".format(address, unit, dim_value)
            Data.tinkerforge_connection.dim_socket("nXN", int(address), int(unit), int(dim_value))
            log.info("Tried to dim socket {0!s}-{1!s} to {2!s}.".format(address, unit, dim_value))

        @staticmethod
        def create_socket_entry(name, address, unit):
            return """

{n} ({a}_{u}, nXN)

                            <button class='alert button' onclick='$.ajax(
                                "/socket/switch?address={a}&unit={u}&state=0");'>Aus
                            </button>
                            <button class='success button' onclick='$.ajax(
                                "/socket/switch?address={a}&unit={u}&state=1");'>An
                            </button>

            """.format(n=name, a=str(address), u=str(unit))

        @staticmethod
        def create_dim_entry(name, address, unit):
            item_id = str(address) + "_" + str(unit)
            return """

{n} ({a}_{u}, nXN)

                        <input id='{i}' type='hidden'>

                        <button class='alert expanded button' onclick='$.ajax(
                          "/socket/switch?address={a}&unit={u}&state=0");'>Aus
                        </button>

            """.format(n=name, a=str(address), u=str(unit), i=item_id)

    class Rule:
        """Webserver class to control rules."""

        @cherrypy.expose
        def list(self):
            rules = ""
            for index in range(0, len(Data.active_rules)):
                rules += self.create_rule_entry(index)
            return rules

        @cherrypy.expose
        def toggle_rule_keep_alive(self, position, keep_alive):
            position = int(position)
            if 0 <= position < len(Data.active_rules):
                if keep_alive == "false":
                    Data.active_rules[position].deactivate_rule()
                else:
                    Data.active_rules[position].activate_rule()
            else:
                log.warn("No rule at position " + str(position))

        @staticmethod
        def create_rule_entry(position):
            name = Data.active_rules[position].tname
            if Data.active_rules[position].keep_alive:
                checked = "checked "
            else:
                checked = ""
            return """

                {n}

                        <input class='switch-input' id='{n}' type='checkbox' {c} onclick=
                          '$.ajax("/rule/toggle_rule_keep_alive?position={p}&keep_alive="
                          + event.target.checked);'>

            """.format(n=name, p=str(position), c=checked)

    class AdditionalInformation:
        """Webserver class to provide additional information."""

        @cherrypy.expose
        def connected_devices(self):
            if len(Data.tinkerforge_connection.current_entries) == 0:
                return "
Keine Geräte angeschlossen
"
            string = ""
            for key in Data.tinkerforge_connection.current_entries:
                string += ("
{0} ({1})
"
                           .format(key, Data.tinkerforge_connection.current_entries[key]))
            return string

        @cherrypy.expose
        def amm_illuminance(self):
            return (
                "
Aktuelle Helligkeit (amm): " +
                str(Data.tinkerforge_connection.get_illuminance("amm")) + " Lux
"
            )

        @cherrypy.expose
        def bvg(self):
            array = Data.bvg.call()
            if array is None:
                return "
Keine Abfahrtzeiten verfügbar
"
            string = ""
            for entry in array:
                string += "
{0} -> {1} ({2})
".format(entry[3], entry[1], entry[2])
            return string

        @cherrypy.expose
        def set_wakeup_time(self, wakeup_time):
            Data.wakeup_time = wakeup_time
            # TODO make sure that is has the right format
            log.info("Set wakeup time to {0}.".format(wakeup_time))

        @cherrypy.expose
        def get_wakeup_time(self):
            return Data.wakeup_time

##########################################################################################
# The Entrypoint of the Application
##########################################################################################

if __name__ == '__main__':
    # start all needed modules
    Data.tinkerforge_connection = TinkerforgeConnection("192.168.0.248")
    Data.bvg = BVG("Seesener Str. (Berlin)", limit=4)

    # load all rules
    Data.active_rules.append(Rule(tname="Bewässerungsregel (9 und 19 Uhr)", rule_logic=watering_rule, sleep_time=50))

    # start the webserver and configure logging
    cherrypy.engine.unsubscribe('graceful', cherrypy.log.reopen_files)
    logging.config.dictConfig(log_conf)
    webserver = Webserver()
    cherrypy.tree.mount(webserver.socket, '/socket', conf)
    cherrypy.tree.mount(webserver.rule, '/rule', conf)
    cherrypy.tree.mount(webserver.additional_information, '/additional_information', conf)
    cherrypy.quickstart(webserver, '/', conf)


# TODO List:
#          Make game work on wii
#          Default sprite when move becomes inactive
#          "Active frame" for throws
#          Allow characters to change direction at will
#          Landing lag
#          When a character in hitstun hits a wall, make it bounce off
#          Character get stunned when their tether collides with an attack
#          Comment
#          Re-implement keyboard movement
#          Allow any port for controller

# TODO future:
#       Wall attack and wall walking
#       Allow tethers to be angled and to snap to the wall
#       Prevent attacks while frozen from boost
#       Relax jumping constraints
#       Select stage + character with controller
#       Make characters bigger?
#       Make it look prettier, including images
#       Add new characters
#

# TODO stretch:
#       Handle character collision
#       Add new stages
#       Better wall collision detection

# TODO: bugs
#       If a character hits a wall before the throw is over, the throw doesn't happen
#       If robotnik dies on the bottom wall, the final sprite doesn't show up

import time
import pygame.key
import os
import gc

from Char import *
from Stage import Stage

WIDTH = 1280
HEIGHT = 800
WINDOW_OFFSET_X = 5
WINDOW_OFFSET_Y = 30  # no idea why but this works
START_COLOR = (60, 100, 140)  # start screen color
START_TEXT_COLOR = (0, 0, 0)
GAME_COLOR = (50, 100, 150)  # game screen color -- possible to make this stage-dependent
FPS = 60  # 60 frames per second

START_TEXT = 'ZERO GRAVITY'  # text on the start screen
START_TEXT_POS = (WIDTH / 2 - 300, .371 * HEIGHT)  # top left corner of start text
START_TEXT_SIZE = 120  # start screen text will be this big
START_TEXT_RECT_DIMS = (600, 100)  # somewhat arbitrary
BUTTON_TEXT_SIZE = 50  # text on the button will be this big
BUTTON_CORNER = (0.278 * WIDTH, 0.614 * HEIGHT)  # top left corner of start button
BUTTON_DIMS = (0.444 * WIDTH, 0.1 * HEIGHT)  # length and width of start button

SELECT_SCREEN_SPACE = (10, 10)  # distance in between each character/stage on the select screen
SELECT_TEXT_SIZE = 18

NUM_STAGES = 1
STAGE_SELECT_DIMS = (int(0.2 * WIDTH), int(0.25 * HEIGHT))  # length and width of stage select buttons
STAGE_SELECT_WIDTH = STAGE_SELECT_DIMS[0] + SELECT_SCREEN_SPACE[0]
STAGE_SELECT_LEN = STAGE_SELECT_DIMS[1] + SELECT_SCREEN_SPACE[1]
STAGE_IMAGE_SPACE = 5
STAGE_IMAGE_BUFFER = 33

NUM_CHARS = 2
CHAR_SELECT_DIMS = (0.17 * WIDTH, 0.30 * HEIGHT)  # length and width of character select buttons
CHAR_SELECT_WIDTH = CHAR_SELECT_DIMS[0] + SELECT_SCREEN_SPACE[0]
CHAR_SELECT_LEN = CHAR_SELECT_DIMS[1] + SELECT_SCREEN_SPACE[1]
CHAR_IMAGE_SCALE = 1.7

WALL_WIDTH = 20  # how wide the wall is

X_BUTTON = 0
A_BUTTON = 1
B_BUTTON = 2
Y_BUTTON = 3
L_BUTTON = 4
R_BUTTON = 5
Z_BUTTON = 7
START_BUTTON = 9

CONTROL_STICK_HORIZONTAL = 0
CONTROL_STICK_VERTICAL = 1
C_STICK_VERTICAL = 2
C_STICK_HORIZONTAL = 3

DEAD_ZONE = 0.3
DIAG_DEAD_ZONE = 0.15
SINGLE_DIRECTION_BUFFER = 0.2

BOOST_PAUSE = 30

class ZeroGravity:
    def __init__(self):
        pygame.init()
        self.w = WIDTH
        self.h = HEIGHT

        # screen is in top left corner
        os.environ['SDL_VIDEO_WINDOW_POS'] = '%i,%i' % (WINDOW_OFFSET_X, WINDOW_OFFSET_Y)
        self.screen = pygame.display.set_mode((self.w, self.h), pygame.FULLSCREEN)  # create the screen
        # self.screen.fill(START_COLOR)  # fill the screen with white
        pygame.display.flip()  # open the screen

        self.background = pygame.image.load('imgs/background.jpg')
        self.background = pygame.transform.scale(self.background, (self.w, self.h))

        self.chars = []  # list of playable characters
        self.status = 'start screen'  # whether the game is at the start screen, character select screen, or in a game
        self.playChars = []  # list of characters currently playing
        self.clock = pygame.time.Clock()

        self.running = False  # whether the program is running
        self.fps = 0  # frames per second
        # (both of these will be set in the mainloop function)

        rect = pygame.Rect(BUTTON_CORNER, BUTTON_DIMS)
        self.startButton = TextButton('START', BUTTON_TEXT_SIZE, START_TEXT_COLOR, rect, self)

        self.wallWidth = WALL_WIDTH
        # TODO: config file?
        self.walls = [[pygame.Rect(0, 0, WALL_WIDTH, self.h), pygame.Rect(0, 0, self.w, WALL_WIDTH),
                       pygame.Rect(self.w - WALL_WIDTH, 0, WALL_WIDTH, self.h),
                       pygame.Rect(0, self.h - WALL_WIDTH, self.w, WALL_WIDTH)]]
        self.stage = Stage(self.walls[0], self)

        self.chars = ['ALUCARD', 'DR ROBOTNIK']
        self.charButtons = []
        start_x = 0.5 * WIDTH - 0.5 * CHAR_SELECT_DIMS[0] - CHAR_SELECT_WIDTH * 0.5 * (NUM_CHARS - 1)
        start_y = 0.5 * HEIGHT - CHAR_SELECT_DIMS[0] * 0.5
        for i in range(NUM_CHARS):
            xPos = start_x + i * (SELECT_SCREEN_SPACE[0] + CHAR_SELECT_DIMS[0])
            rect = pygame.Rect(xPos, start_y, CHAR_SELECT_DIMS[0], CHAR_SELECT_DIMS[1])

            char = Char(self.chars[i], self, 1)
            charImage = char.spriteSheet.subsurface(char.defaultSprite).copy()
            charImage = pygame.transform.scale(charImage, (int(charImage.get_width() * CHAR_IMAGE_SCALE),
                                                           int(charImage.get_height() * CHAR_IMAGE_SCALE)))
            self.charButtons.append(ImageButton(charImage, rect, self.chars[i], SELECT_TEXT_SIZE, self))

        self.stages = ['DEFAULT']
        self.stageButtons = []
        start_x = 0.5 * WIDTH - 0.5 * STAGE_SELECT_DIMS[0] - STAGE_SELECT_WIDTH * 0.5 * (NUM_STAGES - 1)
        start_y = 0.5 * HEIGHT - STAGE_SELECT_DIMS[0] * 0.5
        for i in range(NUM_STAGES):
            xPos = start_x + i * (SELECT_SCREEN_SPACE[0] + STAGE_SELECT_DIMS[0])
            rect = pygame.Rect(xPos, start_y, STAGE_SELECT_DIMS[0], STAGE_SELECT_DIMS[1])

            stage = Stage(self.walls[i], self)
            stageImage = stage.image(STAGE_SELECT_DIMS[0] - 2 * STAGE_IMAGE_SPACE,
                                     STAGE_SELECT_DIMS[1] - 2 * STAGE_IMAGE_SPACE - STAGE_IMAGE_BUFFER)
            self.stageButtons.append(ImageButton(stageImage, rect, self.stages[i], SELECT_TEXT_SIZE, self))

        # self.walls.append(pygame.Rect(LEFT_PLAT))
        # self.walls.append(pygame.Rect(RIGHT_PLAT))

        # self.screen.fill(pygame.Color('Red'), pygame.Rect(0,0,50,50))

        #self.controls = 'joystick'
        self.controls = 'keyboard'

        pygame.joystick.init()
        self.controllers = []

        for i in range(pygame.joystick.get_count()):
            self.controllers.append(pygame.joystick.Joystick(i))

        #self.p1controls = self.controllers[1]
        #self.p2controls = self.controllers[0]
        #self.p1controls.init()
        #self.p2controls.init()
        # self.controllers[4].init()
        # self.controllers[2].init()
        # self.controllers[3].init()
        # print('Controller name: %s' % (self.p1controls.get_name()))
        # print('Number of axes: %d' % (self.p1controls.get_numaxes()))

    def mainLoop(self, fps):
        self.running = True
        self.fps = fps

        while self.running:
            self.loop(fps)

        pygame.quit()

    def loop(self, fps):
        pygame.display.set_caption('Zero Gravity')  # text at the top of the window
        self.handleEvents()  # detect inputs
        self.update()  # update the screen to the next frame
        self.draw()  # display everything on the screen for the next frame
        pygame.display.flip()
        self.clock.tick(fps)

    def draw(self):
        # start screen
        if self.status == 'start screen':
            self.screen.fill(START_COLOR)
            rect = pygame.Rect(START_TEXT_POS, START_TEXT_RECT_DIMS)
            self.displayText(START_TEXT, START_TEXT_SIZE, rect, START_TEXT_COLOR)
            self.startButton.draw()
        elif self.status == 'stage select':
            self.screen.fill(START_COLOR)
            for button in self.stageButtons:
                button.draw()
        elif self.status == 'char select':
            self.screen.fill(START_COLOR)
            for button in self.charButtons:
                button.draw()
        elif self.status == 'in game':
            self.screen.fill(GAME_COLOR)
            self.screen.blit(self.background, (0, 0))
            for char in self.playChars:
                char.draw()
            self.stage.draw()

    def handleEvents(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # when you click x, exit the program
                self.running = False
            elif event.type == pygame.KEYDOWN:
                # print('key pressed')
                self.keyDown(pygame.key.get_pressed())  # if a key was pressed, do the associated action
            # elif event.type == pygame.KEYUP:
            #   self.keyUp(pygame.key.get_pressed())  # if a key was released, do the associated action
            elif event.type == pygame.MOUSEBUTTONUP:
                self.mouseUp(event.button, event.pos)  # if the mouse was clicked, do the associated action
            # elif event.type == pygame.MOUSEBUTTONDOWN:
            # self.mouseDown(event.button, event.pos)  #if the mouse button was released, do the associated action
            # elif event.type == pygame.MOUSEMOTION:
            # self.mouseMotion(event.buttons, event.pos, event.rel) #if the mouse was moved, do the associated action
            elif event.type == pygame.JOYAXISMOTION:
                controller = self.controllers[event.joy]
                control_x = controller.get_axis(CONTROL_STICK_HORIZONTAL)
                control_y = controller.get_axis(CONTROL_STICK_VERTICAL)
                c_x = controller.get_axis(C_STICK_HORIZONTAL)
                c_y = controller.get_axis(C_STICK_VERTICAL)
                self.joystickMoved(controller, control_x, control_y, c_x, c_y)
                # if control_x > 0.1 or control_x < -0.1 or control_y > 0.1 or control_y < -0.1:
                #     print('Control stick values: (%f, %f)' % (control_x, control_y))
            elif event.type == pygame.JOYBUTTONUP:
                # print('button pressed')
                print('controller: ' + str(event.joy))
                controller = self.controllers[event.joy]
                self.buttonUp(controller, event.button)

    def joystickMoved(self, controller, controlX, controlY, cStickX, cStickY):
        # print('Status: ' + self.status)
        # print('Controls: ' + self.controls)
        if self.status == 'in game' and self.controls == 'joystick':  # if a game is being played
            if controller == self.p1controls:
                char = self.playChars[0]
            else:
                char = self.playChars[1]
            # print('control stick angle: %d' % joystickAngle(controlX, controlY))
            walls = char.onWall  # the wall(s) player 1's character is on
            cDirection = joystickDirection(cStickX, cStickY)
            controlDirection = joystickDirection(controlX, controlY)
            angle = joystickAngle(controlX, controlY)

            if walls:  # if player 1's character is on a wall and a direction is inputted,
                # if the player angles away from the wall, that character jumps off the wall
                if self.stage.checkWalls(angle, walls, char):
                    # print('Joystick angle: ' + str(joystickAngle(controlX, controlY)))
                    char.startJump(angle)

            else:
                if cDirection == 'left':  # if left was inputted
                    if char.lookingLeft:
                        char.forwardA()
                    else:
                        char.backA()
                elif cDirection == 'up':  # if up was inputted
                    char.upA()  # do an up a
                elif cDirection == 'down':  # if down was inputted
                    char.downA()  # do a down a
                elif cDirection == 'right':  # if right was inputted
                    if char.lookingLeft:
                        char.backA()  # do a back a
                    else:
                        char.forwardA()  # do a forward a

            if char.grabbing is not None:
                char.throw(controlDirection)
            elif char.frozen and angle != -1:
                char.boost(angle)

    # determines what happens when a button is pressed
    def buttonUp(self, controller, button):
        # for i in range(4):
        #     print('Controller axis %d: %d' % (i, self.p1controls.get_axis(i)))
        # Player 1: directions--WASD, jump--joystick, A attack--a, B attack--b.
        # Player 2: directions--p, l, ;, and ', jump--right alt, A attack--right shift, B attack--right enter.
        if self.status == 'in game':  # if a game is being played
            if controller == self.p1controls:
                char = self.playChars[0]
            else:
                char = self.playChars[1]
            if button == X_BUTTON:
                # print('can be frozen')
                char.unfreezing = False
            if button == L_BUTTON or button == R_BUTTON:
                char.shielding = False
            if not char.onWall:
                direction = joystickDirection(controller.get_axis(CONTROL_STICK_HORIZONTAL),
                                              controller.get_axis(CONTROL_STICK_VERTICAL))

                if button == A_BUTTON:
                    # if a was pressed and player 1's character is not on a wall
                    if direction == 'left':  # if left was inputted
                        if char.lookingLeft:
                            char.forwardA()
                        else:
                            char.backA()
                    elif direction == 'up':  # if up was inputted
                        char.upA()  # do an up a
                    elif direction == 'down':  # if down was inputted
                        char.downA()  # do a down a
                    elif direction == 'right':  # if right was inputted
                        if char.lookingLeft:
                            char.backA()  # do a back a
                        else:
                            char.forwardA()  # do a forward a
                    else:  # if no directions were inputted with the a
                        char.neutralA()  # do a neutral a

                if button == B_BUTTON:
                    # if b was pressed and player 1's character is not on a wall
                    if direction == 'left':  # if left was inputted
                        if char.lookingLeft:
                            char.forwardB()
                        else:
                            char.backB()
                    elif direction == 'up':  # if up was inputted
                        char.upB()  # do an up b
                    elif direction == 'down':  # if down was inputted
                        char.downB()  # do a down b
                    elif direction == 'right':  # if right was inputted
                        if char.lookingLeft:
                            char.backB()  # do a back b
                        else:
                            char.forwardB()  # do a forward b
                    else:  # if no directions were inputted with the b
                        char.neutralB()  # do a neutral b

                if button == X_BUTTON and self.playChars[0].frozen:
                    self.playChars[0].frozen = False

                if button == Z_BUTTON:
                    print('tether performed')
                    char.tether()

    def keyUp(self, keys):  # determines what happens when a key is released
        pass

    def keyDown(self, keys):
        # determines what happens when a key is pressed
        if self.status == 'in game':  # if a game is being played
            char2 = self.playChars[1]
            walls = char2.onWall  # the wall player 2's character is on
            if keys[pygame.K_RALT]:  # if right alt was pressed
                # if player 2's character is on a wall and a direction is inputted,
                if (keys[pygame.K_p] or keys[pygame.K_l] or keys[pygame.K_SEMICOLON] or
                    keys[pygame.K_QUOTE]) and walls:
                    if self.stage.checkWalls(keyAngle(keys), walls, char2):
                        char2.startJump(keyAngle(keys))
                        # if the player angles away from the wall, that character jumps off the wall

            if keys[pygame.K_RSHIFT] and not walls:
                # if right shift was pressed and player 2's character is not on a wall
                if keys[pygame.K_l]:  # if left was inputted
                    char2.forwardA()  # do a forward a; if the character is able to turn around, change this
                elif keys[pygame.K_p]:  # if up was inputted
                    char2.upA()  # do an up a
                elif keys[pygame.K_SEMICOLON]:  # if down was inputted
                    char2.downA()  # do a down a
                elif keys[pygame.K_QUOTE]:  # if right was inputted
                    char2.backA()  # do a back a; if the character is able to turn around, change this
                else:  # if no directions were inputted with the a
                    char2.neutralA()  # do a neutral a

            if char2.grabbing:
                char2.throw(keyDirection(keys))

    def keyHeld(self, keys):
        if self.status == 'in game':  # if a game is being played
            if self.controls == 'keyboard':
                # if player 1's character is not on a wall and a direction is inputted,
                if (keys[pygame.K_w] or keys[pygame.K_a] or keys[pygame.K_s] or keys[pygame.K_d]) \
                        and self.playChars[0].onWall == []:
                    self.playChars[0].drift(keyAngle(keys))  # player 1's character drifts
                    # print('drift: %f' % (keyAngle(keys)))

            # if player 2's character is not on a wall and a direction is inputted,
            if (keys[pygame.K_p] or keys[pygame.K_l] or keys[pygame.K_SEMICOLON] or keys[pygame.K_QUOTE]) and \
                    self.playChars[1].onWall == []:
                self.playChars[1].drift(keyAngle(keys))  # player 2's character drifts

    def joystickHeld(self, xAxis, yAxis):
        if self.status == 'in game':  # if a game is being played
            if self.controls == 'joystick':
                angle = joystickAngle(xAxis, yAxis)
                if self.playChars[0].onWall == [] and angle != -1:
                    # if player 1's character is not on a wall and a direction is inputted,
                    self.playChars[0].drift(angle)  # player 1's character drifts
                    # print('drift: %f' % angle)

    def buttonHeld(self):
        for i in range(self.p1controls.get_numbuttons()):
            if self.status == 'in game':  # if a game is being played
                char1 = self.playChars[0]
                # if self.p1controls.get_button(i) and i == X_BUTTON:
                #     print('unfreezing: ' + str(char1.unfreezing))
                if not char1.onWall and i == X_BUTTON and self.p1controls.get_button(i) and not char1.unfreezing:
                    if char1.boostCount > 0:
                        if not char1.frozen:
                            char1.freeze()
                            char1.freezeTimer = BOOST_PAUSE

                if (i == L_BUTTON and self.p1controls.get_button(i)) or \
                        (i == R_BUTTON and self.p1controls.get_button(i)):
                    angle = joystickAngle(self.p1controls.get_axis(CONTROL_STICK_HORIZONTAL),
                                          self.p1controls.get_axis(CONTROL_STICK_VERTICAL))
                    char1.shield(angle)
                    char1.shielding = True

        for i in range(self.p2controls.get_numbuttons()):
            if self.status == 'in game':  # if a game is being played
                char2 = self.playChars[1]
                # if self.p1controls.get_button(i) and i == X_BUTTON:
                #     print('unfreezing: ' + str(char1.unfreezing))
                if not char2.onWall and i == X_BUTTON and self.p2controls.get_button(i) and not char2.unfreezing:
                    if char2.boostCount > 0:
                        if not char2.frozen:
                            char2.freeze()
                            char2.freezeTimer = BOOST_PAUSE

                if (i == L_BUTTON and self.p2controls.get_button(i)) or \
                        (i == R_BUTTON and self.p2controls.get_button(i)):
                    angle = joystickAngle(self.p2controls.get_axis(CONTROL_STICK_HORIZONTAL),
                                          self.p2controls.get_axis(CONTROL_STICK_VERTICAL))
                    char2.shield(angle)
                    char2.shielding = True

    # TODO: implement joystick for both players

    def mouseUp(self, button, pos):
        if self.status == 'start screen':
            if self.startButton.clicked(pos) and button == 1:
                self.status = 'stage select'

        elif self.status == 'stage select':
            print('click')
            for i in range(NUM_STAGES):
                if self.stageButtons[i].clicked(pos) and button == 1:
                    self.status = 'char select'
                    self.stage = Stage(self.walls[i], self)

        elif self.status == 'char select':
            for i in range(NUM_CHARS):
                if self.charButtons[i].clicked(pos) and button == 1:
                    self.status = 'in game'
                    p1 = Char(self.chars[i], self, 0)
                    p2 = Char('ALUCARD', self, 1)
                    p1.opponent = p2
                    p2.opponent = p1
                    self.playChars.append(p1)
                    self.playChars.append(p2)

    def update(self):  # update the screen to the next frame
        self.keyHeld(pygame.key.get_pressed())  # perform actions based on keys held down
        #self.buttonHeld()
        #self.joystickHeld(self.p1controls.get_axis(0), self.p1controls.get_axis(1))
        #self.joystickHeld(self.p2controls.get_axis(0), self.p2controls.get_axis(1))

        for char in self.playChars:  # update each character
            char.update()

        self.detectCollisions()  # see if characters have collided with objects, walls, hitboxes, or other characters

    def detectCollisions(self):  # see if a character has collided with a wall, effect box, or other character
        if self.status == 'in game':
            for char in self.playChars:  # for every character playing,
                # TODO: change this
                walls = []

                for wall in self.stage.walls:
                    if char.collidesWith(wall):
                        walls.append(wall)

                # if char == self.playChars[0] and collided == 2:
                #     print('collided with right wall')
                for wall in walls:
                    # if char == self.playChars[0]:
                    #     print('Collided: ' + str(collided))
                    #     print('Rect: ' + str(char.rect))
                    if wall not in char.onWall and wall not in char.leavingWall and not isinstance(char.currMove, Jump):
                        # if that character collides with a wall (and is not already on the wall),
                        # if the character is not moving away from the wall
                        # (this prevents characters 'colliding' with the wall as they jump off it)
                        # if (('right' in sides and char.xVelocity <= 0) or ('down' in sides and char.yVelocity <= 0) or
                        #         ('left' in sides and char.xVelocity >= 0) or ('up' in sides and char.yVelocity >= 0)):
                        char.hitWall(wall)
                        # if char == self.playChars[0]:
                        #   print('x: %d, y:%d' % (char.pos[0], char.pos[1]))

                for c2 in self.playChars:
                    if c2 != char:
                        shieldCollision(char, c2)
                        boxCollision(char, c2)

                # TODO: handle character collision
                # if pygame.sprite.collide_mask(char, c2):
                # print('character collision')

    def end(self, loser):
        print('Player %d wins!' % ((loser.player + 1) % 2 + 1))
        # self.draw()
        time.sleep(1)
        self.playChars = []
        self.status = 'char select'
        gc.collect()

    # show text on the screen
    def displayText(self, message, textSize, rect, textColor=(0, 0, 0)):
        font = pygame.font.Font('Fonts/Elianto-Regular.ttf', textSize)
        text = font.render(message, False, textColor)
        textpos = text.get_rect()
        textpos.centerx = rect.centerx
        textpos.centery = rect.centery
        # pygame.draw.rect(self.screen, bkgColor, textpos)
        self.screen.blit(text, textpos)

def shieldCollision(c1: Char, c2: Char):
    for box in c1.effectBoxes:
        if isinstance(box, ShieldBox):
            for box2 in c2.effectBoxes:
                if isinstance(box2, Hitbox) and pygame.sprite.collide_mask(box, box2):
                    box.hit(box2)

def boxCollision(c1: Char, c2: Char):
    for box in c1.effectBoxes:
        if not isinstance(box, ShieldBox):
            if pygame.sprite.collide_mask(box, c2):
                box.hit(c2)  # the character gets hit
                if c1.currMove is not None:
                    c1.currMove.deactivate()  # deactivate the hitbox so it doesn't keep hitting

# given a set of joystick inputs, return the angle that corresponds to those inputs
def joystickAngle(xAxis, yAxis):
    if yAxis < -1 + SINGLE_DIRECTION_BUFFER:
        return 90
    if xAxis < -1 + SINGLE_DIRECTION_BUFFER:
        return 180
    if yAxis > 1 - SINGLE_DIRECTION_BUFFER:
        return 270
    if xAxis > 1 - SINGLE_DIRECTION_BUFFER:
        return 0

    right = False
    left = False
    up = False
    down = False

    right_diag = False
    left_diag = False
    up_diag = False
    down_diag = False

    if yAxis < - DEAD_ZONE:
        up = True

    if yAxis < - DIAG_DEAD_ZONE:
        up_diag = True

    if xAxis < - DEAD_ZONE:
        left = True

    if xAxis < - DIAG_DEAD_ZONE:
        left_diag = True

    if yAxis > DEAD_ZONE:
        down = True

    if yAxis > DIAG_DEAD_ZONE:
        down_diag = True

    if xAxis > DEAD_ZONE:
        right = True

    if xAxis > DIAG_DEAD_ZONE:
        right_diag = True

    if right_diag and up_diag:
        return 45
    elif left_diag and up_diag:
        return 135
    elif up:
        return 90
    elif down_diag and left_diag:
        return 225
    elif left:
        return 180
    elif down_diag and right_diag:
        return 315
    elif right:
        return 0
    elif down:
        return 270
    else:
        return -1

def joystickDirection(xAxis, yAxis):
    if abs(xAxis) < DEAD_ZONE and abs(yAxis) < DEAD_ZONE:
        return None

    if abs(xAxis) > abs(yAxis):
        if xAxis > 0:
            return 'right'
        else:
            return 'left'
    else:
        if yAxis > 0:
            return 'down'
        else:
            return 'up'

def keyAngle(keys):  # given a set of inputs, return the angle that corresponds to those inputs
    right = False
    left = False
    up = False
    down = False

    if keys[pygame.K_w] or keys[pygame.K_p]:
        up = True

    if keys[pygame.K_a] or keys[pygame.K_l]:
        left = True

    if keys[pygame.K_s] or keys[pygame.K_SEMICOLON]:
        down = True

    if keys[pygame.K_d] or keys[pygame.K_QUOTE]:
        right = True

    if right and up:
        return 45
    elif left and up:
        return 135
    elif up:
        return 90
    elif down and left:
        return 225
    elif left:
        return 180
    elif down and right:
        return 315
    elif right:
        return 0
    elif down:
        return 270

def keyDirection(keys):
    if keys[pygame.K_w] or keys[pygame.K_p]:
        return 'up'

    if keys[pygame.K_a] or keys[pygame.K_l]:
        return 'left'

    if keys[pygame.K_s] or keys[pygame.K_SEMICOLON]:
        return 'down'

    if keys[pygame.K_d] or keys[pygame.K_QUOTE]:
        return 'right'

z = ZeroGravity()  # instantiate the game object
z.mainLoop(FPS)  # start the game


import cv2 as cv
import numpy as np
import sys
from math import acos, asin, cos, pi
from time import sleep
import serial
import time
import os

LOG_VIDEO = False
CAMERA_NUMBER = 0

# define thresholds
BALL = {
    'thresh': [np.array([0, 45, 15]), np.array([21, 255, 255])],
    'area': [30000, 39000],
    'loc_x': [750, 1100],
    }
BASKET = {
    'thresh': [np.array([0, 26, 81]), np.array([207, 255, 255])],
    'area': [2000, 3500],
    'loc_x': [0, 750],
    }

BLOB_DATA = [BALL, BASKET]

# crop bounds
CROP = [[385, 182], [1467, 867]]
DIMENSIONS = (CROP[1][0] - CROP[0][0], CROP[1][1] - CROP[0][1])

# blur kernel
GAUS_KERNEL = np.ones((3, 3), np.float32)/15

# robot home position
HOME = [0, 65]

# aim time for the shoot
SHOOT_DELAY = 0.2
INFER_DELAY = 1.44

# tick frequency (used for timing between frames to get velocities in px/sec)
FREQUENCY = cv.getTickFrequency()

# kernel for morphological operations performed on the binary images
MORPH_KERNEL = np.ones((4, 4),np.uint8)

# Port connection variables
COM_PORT = "COM3"
BAUD = 115200
TIMEOUT = 0.1

# Information about servos in the form,
# [rest_point, scaling_factor (for accurate angles)]
# Last one is for the pen servo in the form, [up, down]
SERVO_CALIB = [
    [93, 0.8556],
    [80, 0.8556],
    [80, 20],
]

# maximum pixel value the robot can safely reach to (higher is more restricted)
MAX_PIX = 470

# number of increments used for robot commands
# higher number means more lag/slower speed, lower number means higher movement fidelity
INCREMENTS = 20

BASE_LOG_DIRECTORY = "D:\\basketball_logs"
LOG_DATA_FILENAME = "log.csv"

# log variables in the form, (heading, value). The input for the lambda function is vars()
LOG_VARS = [
    lambda x: x["current_time"],
    lambda x: x["vel"][0],
    lambda x: x["vel"][1],
    lambda x: np.std(x["vel_ave"][0]),
    lambda x: np.std(x["vel_ave"][1]),
    lambda x: x["basket_coord"][0],
    lambda x: x["basket_coord"][1],
    lambda x: x["pred_loc"][0],
    lambda x: x["pred_loc"][1],
    lambda x: x["ball_coord"][0],
    lambda x: x["ball_coord"][1],
    lambda x: x["m"],
    lambda x: x["c"],
]
LOG_VAR_HEADINGS = [
    "time",
    "vel_x",
    "vel_y",
    "vel_x_std",
    "vel_y_std",
    "basket_x",
    "basket_y",
    "predict_x",
    "predict_y",
    "ball_x",
    "ball_y",
    "shot_grad",
    "shot_int",
]

VIDEO_ENCODER = cv.VideoWriter_fourcc(*'XVID')
VID_EXTENSION = ".avi"
VID_FPS = 25.0

def end():
    """ End the program """
    cam.release()
    if LOG_VIDEO:
        video_out.release()
        fd.close()
    cv.destroyAllWindows()
    ser.close()
    sys.exit()

def write_servo(ser_obj, pin, val):
    ser_obj.write(chr(pin))
    ser_obj.write(chr(val))

def send_move(pixel_coord, absolute=False):
    if not absolute:
        coord = [0, 0]
        coord[0] = 20.88 + 0.001015 * pixel_coord[0] - 0.07991 * pixel_coord[1]
        coord[1] = 169.3 - 0.09383 * pixel_coord[0] - 0.01384 * pixel_coord[1]
    else:
        coord = pixel_coord

    theta = [0, 0]

    theta[1] = asin(coord[0] / float(42))
    a = coord[1] - 50 * cos(theta[1])
    theta[0] = asin((50 - a) / 38)
    theta[1] *= -1

    theta = [x * 180 / pi for x in theta]

    for i, angle in enumerate(theta):
        calib = SERVO_CALIB[i]
        write_servo(ser, i, int(calib[0] + calib[1] * angle))
        sleep(0.001)

def pen(activate):
    if activate:
        write_servo(ser, 2, 34)
        pass
    else:
        write_servo(ser, 2, 110)
        pass
    sleep(0.001)

cam = cv.VideoCapture(CAMERA_NUMBER)

cam.set(3, 1920)
cam.set(4, 1080)

prev_pos = [0, 0]
prev_tick = cv.getTickCount()

# vector of previous velocities for running average
vel_ave = [np.zeros(40, np.float32), np.zeros(40, np.float32)]
# bounds of the basket movement
bounds_ave = [
    [np.zeros(3, np.float32), np.zeros(3, np.float32)],
    [np.zeros(3, np.float32), np.zeros(3, np.float32)]
    ]
bounds = [[0, 0], [0, 0]]

# open serial port to the Arduino
ser = serial.Serial(COM_PORT, BAUD, timeout=TIMEOUT)
# wait for port to settle
sleep(2)

# move to home position and deactivate pen
send_move(HOME, absolute=True)
pen(0)

# number of frames since the last shot. Allows for some settling since last shot
shot = 0

# current (and previous frame) sign of the velocity vector
sign = [0, 0]
prev_sign = [0, 0]

# predicted location of basket after shooting
pred_loc = [0, 0]

# true when frames are recorded
record = False
video_out = None

if LOG_VIDEO:
    # create a unique log directory
    log_directory = os.path.join(BASE_LOG_DIRECTORY, str(int(time.time())))
    os.mkdir(log_directory)

    # create data log file
    fd = open(os.path.join(log_directory, LOG_DATA_FILENAME), 'w')
    for heading in LOG_VAR_HEADINGS:
        fd.write(heading + ",")
    fd.write("\n")

while(True):
    # quit if q is pressed
    if cv.waitKey(1) & 0xFF == ord('q'):
        end()

    # get frame and crop
    ret, frame = cam.read()
    frame = frame[CROP[0][1]:CROP[1][1], CROP[0][0]:CROP[1][0]]

    # make copy for imshowing at the end
    original = frame.copy()

    # apply blur
    frame = cv.filter2D(frame, -1, GAUS_KERNEL)

    # convert to hsv
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)

    # make binary image of ball and basket
    ball_bin = cv.inRange(hsv, BALL['thresh'][0], BALL['thresh'][1])
    basket_bin = cv.inRange(hsv, BASKET['thresh'][0], BASKET['thresh'][1])

    # copy images to keep originals for imshowing at the end
    # ball_bin_original = ball_bin.copy()
    # basket_bin_original = basket_bin.copy()

    # process binary images to retrieve contours of points of interest
    binaries = [ball_bin, basket_bin]
    poi_coords = []
    # contours = []
    for i in range(len(binaries)):
        # filter noise
        binaries[i] = cv.erode(binaries[i], MORPH_KERNEL, iterations=2)
        binaries[i] = cv.dilate(binaries[i], MORPH_KERNEL, iterations=3)

        # find contours
        _, new_contours, heirarchy = cv.findContours(binaries[i].copy(),
            cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

        coords = []
        # filter contours
        # filtered_contours = []
        for contour in new_contours:
            area = cv.contourArea(contour)
            if BLOB_DATA[i]['area'][0] < area < BLOB_DATA[i]['area'][1]:
                # filtered_contours.append(contour)
                moments = cv.moments(contour)
                centroid_x = int(moments['m10']/moments['m00'])
                centroid_y = int(moments['m01']/moments['m00'])
                if BLOB_DATA[i]['loc_x'][0] < centroid_x < BLOB_DATA[i]['loc_x'][1]:
                    coords = [centroid_x, centroid_y]
        # contours.append(filtered_contours)
        if coords == []:
            continue
        poi_coords.append(coords)

    # make contours frame to display contours
    # contours_frame = original.copy()
    # _ = cv.drawContours(contours_frame, contours[0], -1, (255,0,0), 3)
    # _ = cv.drawContours(contours_frame, contours[1], -1, (0,255,0), 3)

    if len(poi_coords) != 2:
        cv.imshow('points', frame)
        if record and LOG_VIDEO:
            video_out.write(original)
        # cv.imshow('contour', contours_frame)
        continue

    # extract ball and basket coords
    ball_coord = poi_coords[0]
    basket_coord = poi_coords[1]

    # get delta time
    delta_time = (cv.getTickCount() - prev_tick) / FREQUENCY
    # calc instantaneous basket velocity vector
    vel = [(x - y) / float(delta_time) for x, y in zip(prev_pos, basket_coord)]

    # add the calculated velocities to the average list
    for i in range(len(vel_ave)):
        vel_ave[i] = np.concatenate(([abs(vel[i])], vel_ave[i][:-1]))

    # predict location of basket after throwing
    for i in range(len(pred_loc)):
        pred_loc[i] = basket_coord[i] - int(INFER_DELAY * sign[i] * np.average(vel_ave[i]))

    # detect and store movement bounds
    for i in range(len(vel)):
        if vel[i] != 0:
            prev_sign[i] = sign[i]
            sign[i] = cmp(vel[i], 0)

            if prev_sign[i] != sign[i] and shot > 20:
                j = 0
                if prev_sign[i] > 0:
                    j = 1
                bounds_ave[i][j] = np.concatenate(([basket_coord[i]], bounds_ave[i][j][:-1]))
                bounds[i][j] = np.average(bounds_ave[i][j])

    # detect if bounds have been reached
    if np.average(vel_ave[1]) > 9:
        for i in range(len(bounds)):
            if pred_loc[i] > bounds[i][0] and np.average(vel_ave[i]) > 5:
                pred_loc[i] = int(2 * bounds[i][0] - pred_loc[i])
            elif pred_loc[i] < bounds[i][1] and np.average(vel_ave[i]) > 5:
                pred_loc[i] = int(2 * bounds[i][1] - pred_loc[i])

    prev_tick = cv.getTickCount()
    prev_pos = basket_coord[:]

    points_frame = original.copy()
    points_frame = cv.circle(points_frame, tuple(poi_coords[0]), 20, (0,0,255), -1)
    points_frame = cv.circle(points_frame, tuple(poi_coords[1]), 20, (0,255,0), -1)
    points_frame = cv.circle(points_frame, tuple(pred_loc), 20, (255,0,0), -1)

    points_frame = cv.circle(points_frame, tuple(ball_coord), 20, (0,255,255), -1)

    shot += 1

    if (np.average(vel_ave[1]) < 9 or (np.std(vel_ave[0]) < 50 and np.std(vel_ave[1]) < 130 and ball_coord[1]-5 < pred_loc[1] < ball_coord[1]+5 and np.std(bounds_ave[0][0]) < 30 and np.std(bounds_ave[0][1]) < 30 and np.std(bounds_ave[1][0]) < 30 and np.std(bounds_ave[1][1]) < 30)) and shot > 20:
        print("shoot {0}, {1}".format(ball_coord, pred_loc))

        m = (pred_loc[1] - ball_coord[1]) / float(pred_loc[0] - ball_coord[0])
        c = ball_coord[1] - m * ball_coord[0]

        time_step = SHOOT_DELAY / float(INCREMENTS)
        x_step = (MAX_PIX - ball_coord[0]) / float(INCREMENTS)
        x = ball_coord[0]

        send_move(ball_coord)
        pen(1)
        sleep(0.5)
        for i in range(INCREMENTS):
            if i == int(INCREMENTS * 0.60):
                pen(0)
            send_move([x, m * x + c])
            x += x_step
            sleep(time_step)
        pen(0)
        send_move(HOME, absolute=True)

        if LOG_VIDEO:
            # record logs
            current_time = str(int(time.time()))
            if video_out:
                video_out.release()
            video_out = cv.VideoWriter(os.path.join(log_directory, current_time + VID_EXTENSION), VIDEO_ENCODER, VID_FPS, DIMENSIONS)
            record = True

            current_vars = vars()
            for log_var in LOG_VARS:
                fd.write(str(log_var(current_vars)) + ",")
            fd.write("\n")

        shot = 0

    # show images
    # cv.imshow('ball', ball_bin)
    cv.imshow('basket', basket_bin)
    # cv.imshow('frame', frame)
    cv.imshow('points', points_frame)
    # cv.imshow('contour', contours_frame)


import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

"""
Return the distance from node1 to node2
"""
def dist(node1, node2):
    return (((node1.position[0] - node2.position[0]) ** 2) + ((node1.position[1] - node2.position[1]) ** 2)) **0.5

def InspectReefData3D():
    def StandardReefFunction(x, y):
        x = (x-0.5 *100)/15
        y = (y-0.5 *100)/15
        return (1.0 - x / 2.0 + x ** 5.0 + y ** 3.0) * np.exp(-x ** 2.0 - y ** 2.0) - 10.0

    fig = plt.figure()
    ax = fig.gca(projection='3d')

    # Make data.
    X = np.arange(0, 100, 0.25)
    Y = np.arange(0, 100, 0.25)
    X, Y = np.meshgrid(X, Y)
    Z1 = StandardReefFunction(X, Y)

    # Plot the surface.
    surf = ax.plot_surface(X, Y, Z1, cmap='gray',alpha=1)
    ax.set_aspect('equal', 'box')
    ax.grid(False)

    plt.show()

def InspectReefData2D():
    def StandardReefFunction(x, y):
        x = (x-0.5 *100)/15
        y = (y-0.5 *100)/15
        return (1.0 - x / 2.0 + x ** 5.0 + y ** 3.0) * np.exp(-x ** 2.0 - y ** 2.0) - 10.0

    fig, ax = plt.subplots()

    # Make data.
    X = np.arange(0, 100, 0.25)
    Y = np.arange(0, 100, 0.25)
    X, Y = np.meshgrid(X, Y)

    # reef levels
    plt.contourf(X, Y, StandardReefFunction(X, Y), 8, alpha=.75, cmap='gray')

    # contour lines
    ax.contourf(X, Y, StandardReefFunction(X, Y),levels = [-9.5, 10], colors = 'red', linestyles = 'solid',zorder=10)

    # some formatting
    ax.set_aspect('equal', 'box')
    ax.set_xlim([0, 100])
    ax.set_ylim([0, 100])
    plt.title('2-D Cut at Depth z = -9.5')

    plt.show()

def InspectReefData2DComplete():
    UnknownRegions = { \
                                   0.8: [(50, 15), (43, 25), (80, 25), (88, 19), (90,18)], \
                                   0.4: [(80, 84), (95, 80), (95, 92), (76, 95)], \
                                   0.1: [(11, 8), (40, 0), (40, 17), (11, 11)] \
                                   }

    def StandardReefFunction(x, y):
        x = (x-0.5 *100)/15
        y = (y-0.5 *100)/15
        return (1.0 - x / 2.0 + x ** 5.0 + y ** 3.0) * np.exp(-x ** 2.0 - y ** 2.0) - 10.0

    fig, ax = plt.subplots()

    # Make data.
    X = np.arange(0, 100, 0.25)
    Y = np.arange(0, 100, 0.25)
    X, Y = np.meshgrid(X, Y)

    # reef levels
    plt.contourf(X, Y, StandardReefFunction(X, Y), 8, alpha=.75, cmap='gray')

    # contour lines
    ax.contourf(X, Y, StandardReefFunction(X, Y),levels = [-9.5, 10], colors = 'red', linestyles = 'solid',zorder=10)

    # unknown regions
    from matplotlib import path as mpath
    import matplotlib.patches as patches

    for risk_key in UnknownRegions:
        unknown_region = patches.PathPatch(mpath.Path(UnknownRegions[risk_key]), facecolor='red', lw=0.0001, alpha = risk_key)
        ax.add_patch(unknown_region)

    # some formatting
    ax.set_aspect('equal', 'box')
    ax.set_xlim([0, 100])
    ax.set_ylim([0, 100])
    plt.title('2-D Cut at Depth z = -9.5')

    plt.show()


from datetime import date
import random

class User:

    def __init__(self, first_name, last_name, location) -> str:
        self.first_name = first_name.title()
        self.last_name = last_name.title()
        self.location = location.title()
        self.last_access = date.today()
        self.account_number = random.randint(2000, 10000)
        self.login_attempts = 0  # initializing login atempts

    def describe_user(self):
        print(
            f"first name is: {self.first_name}\n"\
            f"last name is: {self.last_name}\n"\
            f"{self.first_name}'s last access is: {self.last_access}\n"\
            f"{self.first_name}'s account number is {self.account_number}\n"\
            f"{self.first_name} is from {self.location}\n"
        )

    def greet_user(self):
        print(f"Hello {self.first_name}, glad you are here with ust today!!!")

    def increment_login_attempts(self):
        self.login_attempts += 1

    def reset_login_attempts(self):
        self.login_attempts = 0

alex = User("alex", "garcia", "Guadalajara, Mexico")
alex.describe_user()
alex.greet_user()

# increment several times, print and then reset, print again

for i in range (0, 10):
    alex.increment_login_attempts()
    print(f"login attempts: {alex.login_attempts}")

alex.reset_login_attempts()
print(f"\nlogin attempts: {alex.login_attempts}")