'''
Οι κωδικοί ASCII για τους κεφαλαίους λατινικούς χαρακτήρες είναι 65-90
και για τους πεζούς 97-122. Δεδομένου ότι οι χαρακτήρες καταλαμβάνουν
συνεχόμενες θέσεις στον πίνακα ascii μπορώ να πάρω τα όρια αυτά για
πεζούς και κεφαλαίους χαρακτήρες με τις εντολές:
lowercaseStart = ord('a')
lowercaseEnd = ord('z')
uppercaseStart = ord('A')
uppercaseEnd = ord('Z')
Η μέθοδος κρυπτογράφησης ROT13 συνίσταται  στην αντικατάσταση 
κάθε χαρακτήρα που εμφανίζεται στο μήνυμα με τον χαρακτήρα που προκύπτει
αν γίνει μετακίνηση 13 θέσεις μπροστά στο αλφάβητο.
Κωδικοποιούνται μόνο οι χαρακτήρες κειμένου. Τα κενά διαστήματα και 
τα σημεία στίξης παραμένουν ανέπαφα.
'''

from sys import exit    # Για τερματισμό του προγράμματος

'''
--------------------------------------------------------------------
Η συνάρτηση που κρυπτογραφεί το αλφαριθμητικό που δίνεται ως όρισμα
με την μέθοδο κρυπτογάφησης ROT13. Η συνάρτηση ROT13() παίρνει
ως όρισμα ένα αλφαριθμητικό ως μια λίστα χαρακτήρων και επιστρέφει μια
κωδικοποιημένη λίστα.
--------------------------------------------------------------------
'''
def ROT13(str):

    # Παίρνουμε τους κωδικούς ascii για τους πρώτους
    # και τελευταίους χαρακτήρες πεζών και κεφαλαίων
    
    a = ord('a')
    z = ord('z')
    A = ord('A')
    Z = ord('Z')

    # Το κρυπτογραφημένο κείμενο που θα επιστραφεί
    # (δεν γίνεται τροποποίηση του αρχικού)
    encrypted = ""

    # Ορίζουμε στην μεταβλητή length, το μήκος του κειμένου
    length = len(str)

    for i in range(length):   # Για κάθε χαρακτήρα του αρχικού κειμένου
        
        # Παίρνουμε τον επόμενο χαρακτήρα και τον ascii κωδικό του
        ch = str[i]
        newCode = ord(ch)

        # Έλεγχος για κεφαλαίο - πεζό χαρακτήρα

        if ord(ch) >= A and ord(ch) <= Z:     # Κεφαλαίος χαρακτήρας
            
            # Κωδικοποίηση
            newCode = ord(ch) + 13

            # Αν ξεπαράσουμε το άνω όριο, τότε κάνουμε αναδίπλωση.
            # Π.χ. αν είναι 91 πρέπει να γίνει 65. 
            # Άρα, προσθέτουμε στο κάτω όριο (65) τη διαφορά
            # και αφαιρούμε ένα, π.χ. 65 + (91-90) - 1 = 65
            if newCode > Z:
                newCode = A + (newCode - Z) - 1

            # Προσθέτουμε τον κρυπτογραφημένο χαρακτήρα
            # στο νέο κρυπτογραφημένο κείμενο
            encrypted += chr(newCode)
                     

        elif ord(ch) >= a and ord(ch) <= z:  # Πεζός χαρακτήρας
            
            # Κωδικοποίηση
            newCode = ord(ch) + 13

            # Αν ξεπαράσουμε το άνω όριο, αναδίπλωση
            # (όπως και προηγουμένως)
            if newCode > z:
                newCode = a + (newCode - z) - 1
                
            # Προσθέτουμε τον κρυπτογραφημένο χαρακτήρα
            # στο νέο κρυπτογραφημένο κείμενο
            encrypted += chr(newCode)

        else:   # Αν είναι άλλος χαρακτήρας τον αφήνουμε ανέπαφο

            encrypted += ch
       
        
    # Επιστροφή του κωδικοποιημένου κειμένου
    return encrypted


'''
-----------------------------------------------------------------------------
Επειδή δεν προκύπτει από την εκφώνηση αν είσοδος του κειμένου
θα γίνεται από τον χρήστη ή από κάποιοι αρχείο, έχουν υλοποιηθεί
και οι δύο επιλογές. Η πρώτη υλοποιείται στην συνάρτηση encryptConsole()
και η δεύτερη στην συνάρτηση encryptFile(). Και οι δύο συναρτήσεις
καλούν την ROT13() για την κωδικοποίηση. Στην περίπτωση κωδικοποίησης
κειμένου από αρχείο, το αποτέλεσμα αποθηκεύεται στο αρχείο "ecrypted.txt"
στον κατάλογο του προγράμματος. 
-----------------------------------------------------------------------------
'''



'''
-------------------------------------------------------
Δημιουργία ενός μενού επιλογών για κρυπτογράφηση
αρχείου είτε κειμένου που δίνει ο χρήστης στην κονσόλα
και επιστροφή της επιλογής του χρήστη (1 ή 2)
-------------------------------------------------------
'''
def getUserChoice():
    
    # Εκτύπωση του μενού
    print("-" * 35)         # τυπώνει 35 παύλες
    print("Options Menu")
    print("-" * 35)
    print ("1. Encrypt User's Input On Console")
    print ("2. Encrypt Text File")
    print ("3. Exit")
    print("-" * 35)
    
    # Παίρνουμε την επιλογή του χρήστη 
    c = input("Please give your choice> ")
    
    # Αφαιρουμε τον χαρακτήρα αλλαγής γραμμής στο τέλος
    # και επιστρέφουμε
    return c.rstrip()
    

''' 
-----------------------------------------------------------
Κρυπτογράφηση του κειμένου που δίνει ο χρήστης στην κονσόλα
-----------------------------------------------------------
'''
def encryptConsole():
    
    # Παίρνουμε το προς κρυπτογράφηση κείμενο από τον χρήστη
    # Η συνάρτηση input() επιστρέφει την είσοδο του χρήστη
    # μέχρι να πατηθεί το enter (χαρακτήρας αλλαγής γραμμής)
    print("Type the text to be encrypted")
    print("-----------------------------")
    text = input() 
 
    # encrypt text and print it
    print("")   
    print("The encrypted text is:")
    print("----------------------")
    print(ROT13(text))
    print("")
    
    


''' 
------------------------------------------------------
Κρυπτογράφηση ενός αρχείου που δίνει ο χρήστης
Η κρυπτογράφηση αποθηκεύεται στο αρχείο encrypted.txt
επειδή το αρχείο φορτώνεται στην μνήμη σαν μια λίστα
------------------------------------------------------
'''
def encryptFile():    
    
    # Παίρνουμε από τον χρήστη το όνομα του αρχείου
    inputFile = input("Give the file name to be encrypted> ")
    
    # Αν προκύψει σφάλμα κατά το άνοιγμα του αρχείου
    # εκτυπώνεται σχετικό μύνημα και η συνάρτηση πηγαίνει
    # (στο τμήμα except του try block)
    text = ""   # το αρχικό κείμενο
    
    try:
        fr = open(inputFile, 'r')   # Άνοιγμα αρχείου για ανάγνωση
        text = fr.readlines()
        # Η readlines() επιστρέφει μια λίστα αλφαριθμητικών
        # (ένα για κάθε γραμμή του αρχείου)
        # Δημιουργούμε ένα εννιαίο string με την join
        text = ''.join(text)    
        fr.close()
    except:
        print('Error opening file', inputFile)
        return
    
    # Το κωδικοποιούμε
    encryptedText = ROT13(text)
    
    # Γράφουμε το κωδικοποιημένο κείμενο
    # σε ένα νέο αρχείο
    try:
        outputFile = "encrypted.txt"
        fw = open(outputFile, 'w')
        fw.write(encryptedText)
        fw.close()
        print("The encrypted text has been saved into file",outputFile )
    except:
        print("Error writing encrypted file")
        return

     
    



'''
----------------------
Εκτέλεση προγράμματος
----------------------
'''

# Ανάλογα με την επιλογή καλούμε την αντίστοιχη συνάρτηση
# Εκτελούμε επαναληπτικά μέχρι να δωθεί "exit" (3)
while True:

    # Παίρνουμε την επιλογή του χρήστη
    # επαναληπτικά μέχρι να εισαχθεί ένας έγκυρος αριθμός (1,2 ή 3)
    i = getUserChoice()
    while i not in ["1","2","3"]:
        print("Invalid input!")
        i = getUserChoice()


    if i is "1":

        # Κρυπτογράφηση της εισόδου του χρήστη
        encryptConsole()

    elif i is "2":

        # Κρυπτογράφηση αρχείου
        encryptFile()

    elif i is "3":

        # Τερματισμός εφαρμογής
        exit()

'''
---------------------------------------------------------------------------------------
Η ροή του κυρίως προγράμματος είναι η εξής: 
1. Τυπώνεται το μενού επιλογών και λαμβάνεται η επιλογή του χρήστη.

2. Αν ο χρήστης έχει επιλέξει είσοδο κειμένου από την κονσόλα, καλείται
η συνάρτηση encryptConsole() η οποία λαμβάνει το κείμενο, το κωδικοποιεί
και επιστρέφει την κωδικοποιημένη λίστα χαρακτήρων.

3. Αν ο χρήστης έχει επιλέξει είσοδο από κάποιο αρχείο καλείται η συνάρτηση
encryptFile() η οποία λαμβάνει από τον χρήστη το όνομα του αρχείου κειμένου,
το φορτώνει στην μνήμη, το κωδικοποιεί και επιστρέφει το κωδικοποιημένο κείμενο.

4. Αν η επιλογή του χρήστη είναι "Έξοδος" το πρόγραμμα τερματίζει.



Μετά από την κωδικοποίηση το πρόγραμμα δεν τερματίζει αλλά εμφανίζει
ξανά το μενού επιλογών ώστε να είναι δυνατή η κωδικοποίηση και νέου κειμένου. 

----------------------------------------------------------------------------------------

'''
