#!/usr/bin/python3
from dreamtown_config import *
import sys
import os
import json
import sqlite3
import time
import hashlib
import random

#
#    Bassed off https://github.com/MrBlinky/Tamagotchi-friends-code-generator
#
#    MIT License
#
#    Copyright (c) 2021 Mr.Blinky
#
#    Permission is hereby granted, free of charge, to any person obtaining a copy
#    of this software and associated documentation files (the "Software"), to deal
#    in the Software without restriction, including without limitation the rights
#    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#    copies of the Software, and to permit persons to whom the Software is
#    furnished to do so, subject to the following conditions:
#
#    The above copyright notice and this permission notice shall be included in all
#    copies or substantial portions of the Software.
#
#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#    SOFTWARE.



passwordchars1  = "0123456789ABCDEF" 
passwordchars2  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0"
namechars       = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
namechars_xor   = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xA0]
checksum_xor    = [0x11, 0x32, 0x53, 0x74, 0x15, 0x36, 0x58, 0x77, 0x29, 0x4F, 0x6E, 0x0D, 0x2C, 0x4B, 0x6A, 0x7F]
step3count_xor  = [0x57, 0x3A]
nameorder       = [6, 8, 10, 7, 9, 13, 11, 12] #digit order of name characters
step13count_add = [[1, 2, 3, 4, 5, 6, 7, 8],
                  [2, 3, 4, 5, 6, 7, 8, 1],
                  [3, 4, 5, 6, 7, 8, 1, 2],
                  [4, 5, 6, 7, 8, 1, 2, 3], 
                  [5, 6, 7, 8, 1, 2, 3, 4], 
                  [6, 7, 8, 1, 2, 3, 4, 5],
                  [7, 2, 1, 2, 3, 4, 5, 6],
                  [8, 1, 2, 3, 4, 5, 6, 7]]
                         


print("Content-Type: application/json")
print("")

content_len = int(os.environ["CONTENT_LENGTH"])
post = sys.stdin.read(content_len)
jsonData = json.loads(post)
result = {"status":SUCCESS}

method = os.environ["REQUEST_METHOD"]
if method != "POST":
    print("Expected POST")
    os._exit(1)

def GetTamaName(tama_id):
    if tama_id >= len(tamagotchi) or tama_id < 0:
        return "Unknown #"+str(tama_id)
    
    tamaName = tamagotchi[tama_id]
    if tamaName == "":
        tamaName = "Unknown #"+str(tama_id)
    
    return tamaName
    
def InputValidation(password):
    if len(password) != 14:
        return False
    
    for i in range(0,6):
        if passwordchars1.find(password[i]) == -1:
            return False
    
    for i in range(6,14):
        if passwordchars2.find(password[i]) == -1:
            return False    
    
    return True

def GetItem(special):
    #check item input
    if not special:
        itemType = random.randrange(0, 2)
    else:
        itemType = random.randrange(2, 6)
    itemId = random.randrange(0, 30)
    if itemType == 0:
        pass
    if itemType == 1:
        itemId += 30;
    else:
        if itemId > 14:
            itemId = 14;
        itemId += (itemType-2) * 15;
        itemType = 2;
    return [itemId, itemType]
    


def GetName(password, stepCount):
    name = '';
    for i in range(0,8): #get user name
        c = passwordchars2.find(password[nameorder[i]]) - step13count_add[stepCount][i];
        if c < 0: 
            c += 0x1B;
        name += namechars[c];
    return name

def TryGet():
    authToken = jsonData['authToken']
    
    c = db.cursor()
    c.execute('SELECT COUNT(1) from users WHERE LastSession=?',(authToken,))
    rows = c.fetchone()
    count = rows[0]
    if count == 0:
        result['status'] = USER_DOES_NOT_EXIST
        return 0

    #validate
    loginPassword = jsonData["code"].upper()
    ticketNumber = jsonData['ticketNumber']
    if not InputValidation(loginPassword):
        result['status'] = 2 # code invalid
        return 0        
    
    #decode
    checksum = passwordchars1.find(loginPassword[0])
    step13count = passwordchars1.find(loginPassword[1]) >> 1
    step3count  = passwordchars1.find(loginPassword[1]) &  1
    
    name = GetName(loginPassword, step13count)
    
    #login data for decoding and checksum verifying
    logindata = [ 
                    (passwordchars1.find(loginPassword[4]) << 4)  | passwordchars1.find(loginPassword[5]), 
                    (passwordchars1.find(loginPassword[2]) << 4)  | passwordchars1.find(loginPassword[3]),
                    ((namechars.find(name[1]) & 0x3) << 6) |  namechars.find(name[0]),
                    ((namechars.find(name[2]) & 0xF) << 4) | (namechars.find(name[1]) >> 2),
                    (namechars.find(name[3]) << 2)         | (namechars.find(name[2]) >> 4),
                    ((namechars.find(name[5]) & 0x3) << 6) |  namechars.find(name[4]),
                    ((namechars.find(name[6]) & 0xF) << 4) | (namechars.find(name[5]) >> 2),
                    (namechars.find(name[7]) << 2)         | (namechars.find(name[6]) >> 4),
                    (passwordchars1.find(loginPassword[0]) << 4) | passwordchars1.find(loginPassword[1])
                ]
    
    
    # step 3 counter xoring
    logindata[0] ^= step3count_xor[step3count]
    logindata[1] ^= step3count_xor[step3count]
    
    #name xor
    logindata[0] ^= namechars_xor[namechars.find(name[1])];
    logindata[1] ^= namechars_xor[namechars.find(name[3])];

    #checksum xor
    logindata[0] ^= checksum_xor[checksum];
    logindata[1] ^= checksum_xor[checksum];
    
    # Check checksum
    c = 0;
    for i in range(0, 17):
        if (i & 1) == 1:
            c -= (logindata[i >> 1] >> 4);
        else:
            c -= (logindata[i >> 1] & 0xF);
        
    if (c & 0xF) != checksum:
        result['status'] = 2 # code invalid
        return 0        
    
    tama_id = logindata[0] & 0x3F
    step7count = logindata[1] >> 4;
    device_id  = (logindata[0] >> 6) | ((logindata[1] & 0xF) << 2);
    
    special = False
    if ticketNumber > 1:
        special = True
    
    itemResult = GetItem(special)
    itemId = itemResult[0]
    itemType = itemResult[1]
    
    #create logout data                                               
    logoutdata = [ 
                logindata[0],                                    
                logindata[1] | 0x80,                             
                itemId,                           
                (step3count << 3) | itemType
                ]
    checksum = (0 - (logoutdata[1] >> 4) - (logoutdata[1] & 0xF) -
                     (logoutdata[0] >> 4) - (logoutdata[0] & 0xF) -
                     (logoutdata[2] >> 4) - (logoutdata[2] & 0xF) -
                     (logoutdata[3] & 0xF)) & 0XF;
    
    #xor checksum
    for i in range(0,3):
        logoutdata[i] ^= checksum_xor[checksum];
        
    #player name xoring    
    logoutdata[0] ^= namechars_xor[namechars.find(name[1])];
    logoutdata[1] ^= namechars_xor[namechars.find(name[3])];
    logoutdata[2] ^= namechars_xor[namechars.find(name[5])];
    #step 3 counter xoring    
    for i in range(0,3):
        logoutdata[i] ^= step3count_xor[step3count];

    logoutPassword = '%X%X%X%X-%X%X%X%X' % (
    logoutdata[1] >> 4, logoutdata[1] & 0xF,
    logoutdata[0] >> 4, logoutdata[0] & 0xF,
    checksum          , logoutdata[3] & 0xF,
    logoutdata[2] >> 4, logoutdata[2] & 0xF)
        
    result["code"] = logoutPassword
try: 
    db = DbConnect()
    TryGet()
    db.commit()
    db.close()
except Exception as e:
    raise e
    os._exit(1)

print(json.dumps(result))