from PIL import Image, ImageTk
from Consts import*
from Weapon import*
from random import*

class Command:
    ButtonImage = None
    ButtonPressedImage = None
    Name = ''
    entity = None
    NameTvor = ''

    def __init__(self, entity):
        self.entity = entity

    def SetImage(self, imageFileName):
        imagefull = Image.open(imageFileName).convert('RGBA')
        image = imagefull.crop((0, 0, 50, 50))
        imagep = imagefull.crop((50, 0, 100, 50))
        self.ButtonImage = ImageTk.PhotoImage(image)
        self.ButtonPressedImage = ImageTk.PhotoImage(imagep)

    def Run(self, params):
        pass

    @staticmethod
    def Attack(attacker, target, DmgModifier=0, StunModifier=0, CommandTvor=''):
        if target == None or target.Status == Dead:
            print(attacker, target)
            print("Нет цели")
            return
        Message = attacker.Type + ' бьет ' + target.TypeRod
        if attacker.Weapon is not None:
            Message = Message + " " + attacker.Weapon.NameTvor
        else:
            Message = Message + " " + CommandTvor
        print(Message)
        if target.Status == Live and randint(0, 100) <= target.EvadeChance:
            print(target.Type + ' увернулся')
        else:
            damage = round(randint(attacker.Damage[0], attacker.Damage[1]) * (1 + (DmgModifier / 100)))
            if isinstance(target.Weapon, Sword):
                if randint(0, 100) < target.ChanceToParry and target.Status != Stun:
                    print(target.Type + ' парировал удар!')
                    return
            else:
                if target.Shield is not None:
                    if randint(0, 100) < target.ChanceToBlock + target.Shield.ChanceToBlockModifier and target.Status != Stun:
                        print(target.Type + ' блокировал удар щитом!')
                        return
            if randint(0, 100) < attacker.CriticalChance:
                damage = damage * 2
                print("Критический удар!")
            armorModifier = 0
            if target.Helm is not None:
                armorModifier = armorModifier + target.Helm.ArmorModifier
            if target.Torso is not None:
                armorModifier = armorModifier + target.Torso.ArmorModifier
            damage = round(damage * (1 - armorModifier/100))
            if randint(0, 100) < attacker.ChanceToStun + StunModifier:
                if target.Helm is None or randint(0, 100) > target.Helm.StunChanceReduce:
                    target.Status = Stun
                    print(target.Type + " оглушен!")
            target.currentHealth -= damage
            print(attacker.Type + ' наносит ' + target.TypeDat + ' ' + str(damage) + ' урона')
            if target.currentHealth < 1:
                target.Status = Dead
                print(target.Type + ' погибает')

class Move(Command):
    Name = 'Идти'

    def __init__(self, entity):
        super().__init__(entity)
        self.SetImage("skillimage_Move.png")

    def Run(self, params):
        direction = params[0]
        x, y = self.entity.GetCoords(self.entity.x, self.entity.y, direction)
        if direction in [Left, Right]:
            self.entity.orientation = direction
        if self.entity.iCanMove(x, y):
            self.entity.Place(x, y)
            print(self.entity.Type, "идет в точку", self.entity.x, self.entity.y)
        else:
            print(self.entity.Type, "стоит в точке", self.entity.x, self.entity.y)

class Slash(Command):
    Name = 'Рубить'

    def __init__(self, hero):
        super().__init__(hero)
        self.SetImage("skillimageSlash.png")

    def Run(self, params):
        direction = params[0]
        enemy = self.entity.GetEnemyFrom(direction)
        Command.Attack(self.entity, enemy)

class Smash(Command):
    Name = 'Бить'

    def __init__(self, hero):
        super().__init__(hero)
        self.SetImage("skillimageSlash.png")

    def Run(self, params):
        direction = params[0]
        enemy = self.entity.GetEnemyFrom(direction)
        Command.Attack(self.entity, enemy)

class Stab(Command):
    Name = 'Колоть'

    def __init__(self, hero):
        super().__init__(hero)
        self.SetImage("skillimageStab.png")

    def Run(self, params):
        direction = params[0]
        enemy = self.entity.GetEnemyFrom(direction)
        Command.Attack(self.entity, enemy)

class LongStab(Command):
    Name = 'Выпад и укол'

    def __init__(self, hero):
        super().__init__(hero)
        self.SetImage("skillimageStab.png")

    def Run(self, params):
        direction = params[0]
        enemy = self.entity.GetEnemyFrom(direction)
        if enemy is None:
            enemy = self.entity.GetEnemyFrom(direction, distance=2)
        Command.Attack(self.entity, enemy)

class JumpStrike(Command):
    Name = 'Рубануть в прыжке'

    def __init__(self, hero):
        super().__init__(hero)
        self.SetImage("skillimageJumpStrike.png")

    def Run(self, params):
        direction = params[0]
        Move(self.entity).Run(params)
        enemy = self.entity.GetEnemyFrom(direction)
        Command.Attack(self.entity, enemy, DmgModifier=25)

class SmashHorn(Command):
    Name = 'Удар рогами'
    NameTvor = 'рогами'

    def Run(self, params):
        direction = params[0]
        enemy = self.entity.GetHeroFrom(direction)
        Command.Attack(self.entity, enemy, StunModifier=25, CommandTvor=self.NameTvor)

class SmashClaws(Command):
    Name = 'Удар когтями'
    NameTvor = 'когтями'

    def Run(self, params):
        direction = params[0]
        enemy = self.entity.GetHeroFrom(direction)
        Command.Attack(self.entity, enemy, CommandTvor=self.NameTvor)

class FangStrike(Command):
    Name = 'Удар клыками'
    NameTvor = 'клыками'

    def Run(self, params):
        direction = params[0]
        enemy = self.entity.GetHeroFrom(direction)
        Command.Attack(self.entity, enemy, CommandTvor=self.NameTvor)


from datetime import datetime
import math
import numpy as np
import random as rd
import sys

def anchor(year):
    century = math.floor(year/100)
    century_anchor = (5*(century%4))%7+2
    decade = year-century*100
    if decade%2 != 0: decade += 11
    decade /= 2
    if decade%2 != 0: decade += 11
    decade_anchor = 7-(decade%7)
    return int((decade_anchor+century_anchor)%7)

def is_leap(year):
    if year%4 != 0:
        return False
    if year%100 == 0 and year%400 != 0:
        return False
    return True

def rd_date(min_year, max_year):
    rd_year = rd.randint(min_year, max_year)
    rd_month = rd.randint(1,12)
    offset = 0
    if is_leap(rd_year):
        offset = 1
    if rd_month in [1, 3, 5, 7, 8, 10, 12]:
        rd_day = rd.randint(1,31)
    if rd_month in [4, 6, 9, 11]:
        rd_day = rd.randint(1,30)
    if rd_month == 2:
        rd_day = rd.randint(1, 28+offset)
    return [rd_day, rd_month, rd_year]

def print_date(date):
    print(str(date[0])+"."+str(date[1])+"."+str(date[2]))

def weekday(date):
    date = datetime(date[2], date[1], date[0])
    return date.weekday()

def get_seconds(time):
    time_digits = list(str(time))
    return int(time_digits[0])*60*60+int(time_digits[2])*10*60+int(time_digits[3])*60+int(time_digits[5])*10+int(time_digits[6])+int(time_digits[8])*0.1+int(time_digits[9])*0.01

def ask_date(max_attempts, min_year, max_year):
    weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
    times = []
    print("Push ENTER to start the timer!")
    inp = input()
    if len(inp) == 0:
        start_global = datetime.now()
        count = 0
        global_count = 0
        for i in range(max_attempts):
            date = rd_date(min_year, max_year)
            start = datetime.now()
            print_date(date)
            inp = input()
            try:
                test_int = int(inp)
            except:
                print("Attempt abortet! The correct weekday would have been:", weekdays[weekday(date)]+".", "Anchor:", str(anchor(date[2]))+".")
                break
            if int(inp) > 7 or int(inp) < 0:
                print("Attempt abortet! The correct weekday would have been:", weekdays[weekday(date)]+".", "Anchor:", str(anchor(date[2]))+".")
                break
            if int(inp)%7 == (weekday(date)+1)%7:
                stop = datetime.now()
                count += 1
                global_count += 1
                times.append(round(get_seconds(stop-start),2))
                print("Correct! It is a", weekdays[weekday(date)]+".", "You took:", round(get_seconds(stop-start),2), "seconds.", "Anchor:", str(anchor(date[2]))+".\n")
            else:
                stop = datetime.now()
                global_count += 1
                times.append("DNF")
                print("Wrong! The correct weekday is:", weekdays[weekday(date)]+".", "You took:", round(get_seconds(stop-start),2), "seconds.", "Anchor:", str(anchor(date[2]))+".\n")
        stop_global = datetime.now()
        print("\n")
        print("You got", count, "of", global_count, "correct in", stop_global-start_global)
        if count > 1:
            print("Ratio:", round(get_seconds(stop_global-start_global)/count, 2), "Seconds/Date")
            print("Average of", len(times), "is:", average(times))
    else: return print("Attempt aborted!")

def give_weekday(day, month, year):
    weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
    date = datetime(year, month, day)
    print("The", str(day)+"."+str(month)+"."+str(year), "is a", str(weekdays[date.weekday()])+".")

def count_list(lst, x):
    count = 0
    for ele in lst:
        if ele == x:
            count += 1
    return count

def average(values):
    length = len(values)
    number = math.ceil(0.05*length)
    num_dnf = count_list(values, "DNF")
    if num_dnf > number:
        return "DNF"
    if len(values) <= 2:
        return "To short for average."
    num_bad = number - num_dnf
    for i in range(num_dnf):
        values.remove("DNF")
    values.sort()
    for i in range(num_bad):
        values.pop(-1)
    for i in range(number):
        values.pop(0)
    return round(sum(values)/len(values),2)

def help():
    print("""Arguments must be passed in the order as shown:    [attempts] [min_year] [max_year]
             - [attempts] is the number of attempts (default=5).
             - [min_year] is the lower bound of the year interval (default=1600).
             - [max_year] is the upper bound of the year interval (default=2300).""")

if(len(sys.argv) == 4 and sys.argv[1].isdigit() and sys.argv[2].isdigit() and sys.argv[3].isdigit()):
    ask_date(int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]))
else:
    help()


import json_to_db
import os
from requests import get
import cairosvg
PATH = os.getcwd()+"/res/"
def printProgress (iteration, total, prefix = '', suffix = '', decimals = 1, barLength = 100):
	formatStr = "{0:." + str(decimals) + "f}"
	percent = formatStr.format(100 * (iteration / float(total)))
	filledLength = int(round(barLength * iteration / float(total)))
	bar = '#' * filledLength + '-' * (barLength - filledLength)
	sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percent, '%', suffix)),
	if iteration == total:
		sys.stdout.write('\n')
	sys.stdout.flush()

if __name__ == '__main__':
	db = json_to_db.Json()
	#db.createDB() #Only Once
	db.json_to_db()
	db.db_insert()
	print(db.file_list)
	for filepath in db.file_list:
		url = "https://worldvectorlogo.com/download/"+filepath+".svg"
		print(url)
		with open(PATH+filepath+".svg", "wb") as file:
			response = get(url)
			file.write(response.content)
		path = os.getcwd()+"/res/"
		file_name = filepath
		cairosvg.svg2png(url=path+file_name+".svg",
                		write_to=path+file_name+".png",
                		dpi = 100
               			)


import sys
sys.stdin = open('11967.txt')
import collections

dr = [0, 0, -1, 1]
dc = [-1, 1, 0, 0]
def bfs():
    deq = collections.deque()
    deq.append((1, 1))
    visit = [[0 for _ in range(N + 1)] for _ in range(N + 1)]
    visit[1][1] = 1
    result = 1
    while deq:
        row, col = deq.popleft()
        # print(row, col, on)
        # 1. 불켜기
        for w in range(len(on_off[row][col])):
            r, c = on_off[row][col][w]
            # print(r, c)
            if room[r][c] == 0:
                result += 1
                room[r][c] = 1
            visit = [[0 for _ in range(N + 1)] for _ in range(N + 1)]

        on_off[row][col].clear()
        # for i in room:
        #     print(*i)

        # 2. 이동하기
        for w in range(4):
            nr = row + dr[w]
            nc = col + dc[w]

            if 1 <= nr < N + 1 and 1 <= nc < N + 1 and visit[nr][nc] == 0:
                if room[nr][nc] == 1:
                    visit[nr][nc] = 1
                    deq.append((nr, nc))

    return result

N, M = map(int, input().split())

room = [[0 for _ in range(N + 1)] for _ in range(N + 1)]
on_off = [[[] for _ in range(N + 1)] for _ in range(N + 1)]
room[1][1] = 1

for _ in range(M):
    r1, c1, r2, c2 = map(int, input().split())

    on_off[r1][c1].append((r2, c2))

print(bfs())


import logging
import logging.handlers
import time
import datetime
import os
import random
import argparse
import sys
import re
# libs
import requests
from pyquery import PyQuery
# Local
import parse_post

class Topic():
    # Topic level stuff
    def get_topic_title(self):
        # Get the thread title
        thread_title_path = 'h2 > a'
        thread_title_element = self.d(thread_title_path)
        assert(thread_title_element)
        thread_title = thread_title_element.text()
        return thread_title

    def get_topic_id(self):
        # Get the thread ID
        thread_id_path = 'h2 > a'
        #print('get_topic_id() self.d: {0!r}'.format(self.d))
        thread_id_element = self.d(thread_id_path)
        thread_id_html = thread_id_element.outer_html()
        assert(thread_id_html)
        thread_id = re.search('<a\shref="./viewtopic\.php\?f=\d+&t=(\d+)(?:&start=\d+)?(?:&sid=\w+)?">', thread_id_html).group(1)
        return thread_id

    def get_board_id(self):
        # Get the board ID
        board_id_path = 'h2 > a'
        board_id_element = self.d(board_id_path)
        board_id_html = board_id_element.outer_html()
        assert(board_id_html)
        board_id = re.search('<a\shref="./viewtopic\.php\?f=(\d+)&t=\d+(?:&start=\d+)?(?:&sid=\w+)?">', board_id_html).group(1)
        return board_id

    def get_post_ids(self):
        # Get post IDs
        post_ids = re.findall('<div\sid="p(\d+)"\sclass="post\s(?:has-profile\s)?bg\d(?:\s*online\s*)?">', self.page_html)
        #print('Found {0} post_ids'.format(len(post_ids)))
        #print('post_ids: {0!r}'.format(post_ids))
        return post_ids
    # /Topic level stuff

    def parse_topic(self, page_html, board_id=None, topic_id=None, offset=None):
        """Parse a single page of posts
        return the information from the page as a dict"""
        self.page_html = page_html
        self.board_id = board_id
        self.topic_id = topic_id
        self.offset = offset

        self.d = PyQuery(self.page_html)

##        # Get thread level information
##        topic_dict = {}
##
##        topic_dict['title'] = self.get_topic_title()
##        topic_dict['thread_id'] = self.get_topic_id()
##        topic_dict['board_id'] = self.get_board_id()

        post_ids = self.get_post_ids()

        # Get post level information
        posts = []
        # With the post IDs, we can generate paths to the items we want
        for post_id in post_ids:
            #print('post_id: {0}'.format(post_id))
            # Lock ourselves to only this one post
            post_outer_html = self.d('#p{pid}'.format(pid=post_id)).outer_html()

            # Parse the post
            post = parse_post.Post()
            post_dict = post.parse_post(post_id, post_outer_html)
            # Store the post object away
            posts.append(post_dict)
    ##        if len(posts) == 200:# DEBUG
    ##            break# Stop at first post for debug
            continue
        #print('posts: {0!r}'.format(posts))

        #topic_dict['posts'] = posts

        #print('thread: {0!r}'.format(topic_dict))
        return posts

def parse_thread_page(page_html, board_id, topic_id, offset):
    """Parse a page of posts.
    Return a list of dicts, each dict being the values for a single post."""

    #return parse_viewtopic.parse_topic(page_html)

    topic = Topic()
    posts = topic.parse_topic(page_html)
    return posts

def split_thread_page_for_making_test_cases(topic_page_html_path, output_folder):
    with open(topic_page_html_path, 'r') as tf:
        topic_html = tf.read()
    post_ids = re.findall('<div\sid="p(\d+)"\sclass="post\s(?:has-profile\s)?bg\d(?:\s*online\s*)?">', topic_html)
    t = PyQuery(topic_html)
    for post_id in post_ids:
        post_outer_html = t('#p{pid}'.format(pid=post_id)).outer_html()
        post_filename = '{pid}.html'.format(pid=post_id)
        post_path = os.path.join(output_folder, post_filename)
        post_html = post_outer_html.encode('utf-8')# Fixes: UnicodeEncodeError: 'ascii' codec can't encode character u'\xbb' in position 537: ordinal not in range(128)
        with open(post_path, 'wb') as pf:
            pf.write(post_html)
        continue
    return

def main():
    pass

if __name__ == '__main__':
    main()
    # Test and debug stuff
    #file_path = os.path.join('debug','thread_page_response.htm')
    #file_path = os.path.join('debug','thread_page_response.b53.t2182.start2580.htm')
    #file_path = os.path.join('tests','aryion.b38.t45427.htm')
    #file_path = os.path.join('tests', 'phpbb.b64.t2377101.htm')
    #file_path = os.path.join('tests', 'aryion.b38.t44962.htm')
    #file_path = os.path.join('tests', 'phpbb.b64.t2103285.htm')
    #file_path = os.path.join('tests', 'electricalaudio.b5.t64830.htm')
    #file_path = os.path.join('tests', 'aryion.b53.t2182.offset2560.htm')
    ##file_path = os.path.join('tests', 'phpbb.b6.t362219.offset270.htm')
    file_path = os.path.join('tests', 'aryion.viewtopic.f38.t695.htm')
    #file_path = os.path.join('tests', 'phpbb.b6.t362219.offset270.htm')
    #file_path = os.path.join('tests', 'aryion.viewtopic.f38.t695.htm')
    #file_path = os.path.join('tests', 'phpbb.b6.t362219.offset270.htm')
    #file_path = os.path.join('tests', 'cichlid-forum.viewtopic.f4.t246181.htm')
    #file_path = os.path.join('tests', 'phpbb.b6.t2259706.offset15.htm')
    file_path = os.path.join('tests', 'aryion.viewtopic.f55.t11882.offset30.htm')# has swf attachment
    #file_path = os.path.join('tests', 'aryion.viewtopic.f79.t17592.htm')# has swf attachment
    #file_path = os.path.join('tests', 'phpbb.b64.t2377101.htm') # parse_s_thumbnails
    #file_path = os.path.join('tests', 'aryion.b53.t2182.offset2560.htm')

    split_thread_page_for_making_test_cases(
        topic_page_html_path=file_path,
        output_folder=os.path.join('tests', 'single_posts', 'aryion_new')
    )

    with open(file_path, 'r') as f:
        page_html = f.read()

    topic = Topic()
    result = topic.parse_topic(page_html)
    print('result: {0!r}'.format(result))


from datetime import datetime
import unittest
import asyncio
import time

from ocpp.routing import on, after
from ocpp.v16.enums import Action, RegistrationStatus
from ocpp.v16 import call_result, call
import ocpp.exceptions

import default_platform
import default_central

from test_runner import run_test

class TestChangeAvailability(unittest.TestCase):
    def test_set_unavailable_accepted(test):
        @default_central.addTester(test)
        class TestCP(default_central.DefaultChargePoint):
            @after(Action.BootNotification)
            async def after_boot_notification(self, *args, **kwargs):
                result = await self.call(call.ChangeAvailabilityPayload(1, "Inoperative"))
                test.assertTrue(result.status == "Accepted")
                self.done = True

        _, c = run_test(TestCP, sim_len_secs=2, speedup=100)
        test.assertTrue(c.done)

    """
    A Charge Point is considered unavailable when it does not
    allow any charging.
    """
    def test_no_charging_while_unavailable(test):
        @default_central.addTester(test)
        class TestCP(default_central.DefaultChargePoint):
            @after(Action.BootNotification)
            async def after_boot_notification(self, *args, **kwargs):
                result = await self.call(call.ChangeAvailabilityPayload(1, "Inoperative"))
                test.assertTrue(result.status == "Accepted")
                default_platform.show_tag(test, 1, "C0:FF:EE")
                default_platform.connector_state[1] = default_platform.CONNECTOR_STATE_CONNECTED

            @after(Action.StartTransaction)
            def after_start_transaction(self, *args, **kwargs):
                test.fail("Starting a transaction not allowed while unavailable!")

        _, c = run_test(TestCP, sim_len_secs=2, speedup=100)
        test.assertFalse(default_platform.connector_locked[1])
        test.assertEqual(default_platform.charging_current[1], 0)

    """
    A Charge Point is considered available
    (“operative”) when it is charging or ready for charging.
    """
    def test_charging_works_again_after_unavailable(test):
        @default_central.addTester(test)
        class TestCP(default_central.DefaultChargePoint):
            unavail_done = False
            @after(Action.BootNotification)
            async def after_boot_notification(self, *args, **kwargs):
                result = await self.call(call.ChangeAvailabilityPayload(1, "Inoperative"))
                test.assertTrue(result.status == "Accepted")
                default_platform.show_tag(test, 1, "C0:FF:EE")
                default_platform.connector_state[1] = default_platform.CONNECTOR_STATE_CONNECTED

            @after(Action.StatusNotification)
            async def after_status_notification(self, connector_id, error_code, status, **kwargs):
                print(connector_id, error_code, status)
                if connector_id == 1 and status == "Unavailable":
                    result = await self.call(call.ChangeAvailabilityPayload(1, "Operative"))
                    test.assertTrue(result.status == "Accepted")
                    self.unavail_done = True
                if self.unavail_done:
                    default_platform.show_tag(test, 1, "C0:FF:EE")

            @after(Action.StartTransaction)
            def after_start_transaction(self, *args, **kwargs):
                self.done = True

        _, c = run_test(TestCP, sim_len_secs=2, speedup=100)
        test.assertTrue(c.done)

    """
    In the event that Central System requests Charge Point to change to a status it is already in, Charge Point SHALL
    respond with availability status ‘Accepted’.
    """
    def test_accepted_if_already_in_state(test):
        @default_central.addTester(test)
        class TestCP(default_central.DefaultChargePoint):
            @after(Action.BootNotification)
            async def after_boot_notification(self, *args, **kwargs):
                result = await self.call(call.ChangeAvailabilityPayload(1, "Operative"))
                test.assertTrue(result.status == "Accepted")

                result = await self.call(call.ChangeAvailabilityPayload(1, "Inoperative"))
                test.assertTrue(result.status == "Accepted")

                result = await self.call(call.ChangeAvailabilityPayload(1, "Inoperative"))
                test.assertTrue(result.status == "Accepted")
                self.done = True

        _, c = run_test(TestCP, sim_len_secs=2, speedup=100)
        test.assertTrue(c.done)

    def test_state_stays_the_same_if_already_operative(test):
        @default_central.addTester(test)
        class TestCP(default_central.DefaultChargePoint):
            auth_responded = False
            @after(Action.BootNotification)
            def after_boot_notification(self, *args, **kwargs):
                default_platform.show_tag(test, 1, "C0:FF:EE")

            @after(Action.Authorize)
            async def after_authorize(self, *args, **kwargs):
                auth_responded = True
                result = await self.call(call.ChangeAvailabilityPayload(1, "Operative"))
                test.assertTrue(result.status == "Accepted")

        _, c = run_test(TestCP, sim_len_secs=2, speedup=100)
        test.assertEqual(c.status[1], ["Available", "Preparing"])

    """
    When a transaction is in progress Charge Point SHALL respond with availability status 'Scheduled' to
    indicate that it is scheduled to occur after the transaction has finished.

    When an availability change requested with a ChangeAvailability.req PDU has happened, the Charge Point SHALL
    inform Central System of its new availability status with a StatusNotification.req as described there.
    """
    def test_scheduled_if_txn_is_running(test):
        @default_central.addTester(test)
        class TestCP(default_central.DefaultChargePoint):
            txn_stopped = False
            @after(Action.BootNotification)
            def after_boot_notification(self, *args, **kwargs):
                default_platform.show_tag(test, 1, "C0:FF:EE")
                default_platform.connector_state[1] = default_platform.CONNECTOR_STATE_CONNECTED

            @after(Action.StartTransaction)
            async def after_start_transaction(self, *args, **kwargs):
                result = await self.call(call.ChangeAvailabilityPayload(1, "Inoperative"))
                test.assertTrue(result.status == "Scheduled")
                default_platform.show_tag(test, 1, "C0:FF:EE")
                default_platform.connector_state[1] = default_platform.CONNECTOR_STATE_PLUG_DETECTED

            @after(Action.StopTransaction)
            def after_stop_transaction(self, *args, **kwargs):
                self.txn_stopped = True

            @after(Action.StatusNotification)
            def after_status_notification(self, connector_id, error_code, status, **kwargs):
                if connector_id == 1 and status == "Unavailable":
                    test.assertTrue(self.txn_stopped)
                    self.done = True

        _, c = run_test(TestCP, sim_len_secs=3, speedup=100)
        test.assertTrue(c.done)

    """
    Tn the case the ChangeAvailability.req contains ConnectorId = 0, the status change applies to
    the Charge Point and all Connectors.
    """
    def test_connector_id_zero(test):
        @default_central.addTester(test)
        class TestCP(default_central.DefaultChargePoint):
            seen = 0
            num_connectors = 0
            @after(Action.BootNotification)
            async def after_boot_notification(self, *args, **kwargs):
                self.num_connectors = int((await self.call(call.GetConfigurationPayload(["NumberOfConnectors"]))).configuration_key[0]["value"])

                result = await self.call(call.ChangeAvailabilityPayload(0, "Inoperative"))
                test.assertTrue(result.status == "Accepted")

            @after(Action.StatusNotification)
            def after_status_notification(self, connector_id, error_code, status, **kwargs):
                if status == "Unavailable":
                    self.seen += 1 << connector_id
                    print(self.seen)
                if self.seen == (1 << (self.num_connectors + 1)) - 1:
                    self.done = True

        _, c = run_test(TestCP, sim_len_secs=10, speedup=100)
        test.assertTrue(c.done)


"""
Given a list of integers, write a function that returns the largest sum of non-adjacent numbers.
Numbers can be 0 or negative.

For example, [2, 4, 6, 2, 5] should return 13, since we pick 2, 6, and 5. [5, 1, 1, 5]
should return 10, since we pick 5 and 5.

Follow-up: Can you do this in O(N) time and constant space?
"""
import pytest

from daily_problems.problem_0_to_100.problem_9 import largest_non_adjacent_sum

class TestLargestSum:
    def test_empty_list_should_return_null(self):
        assert largest_non_adjacent_sum([]) is None

    @pytest.mark.parametrize(
        "input_list,expected",
        [([1], 1), ([2], 2), ([3], 3), ([-10], -10), ([-20], -20), ([40], 40)],
    )
    def test_list_with_one_integer_should_return_the_integer(
        self, input_list, expected
    ):
        assert largest_non_adjacent_sum(input_list) == expected

    @pytest.mark.parametrize(
        "given_input,expected",
        [([1, 2], 2), ([5, -1], 5), ([10, 15], 15), ([30, 12], 30)],
    )
    def test_list_with_two_integers_should_return_the_higher_value(
        self, given_input, expected
    ):
        assert largest_non_adjacent_sum(given_input) == expected

    @pytest.mark.parametrize(
        "given_input,expected", [([1, 2, 3], 4), ([3, 5, 1], 5), ([-10, 15, 20], 20)]
    )
    def test_list_of_3_ints_should_return_max_non_adjacent_sum(
        self, given_input, expected
    ):
        assert largest_non_adjacent_sum(given_input) == expected

    @pytest.mark.parametrize(
        "given_input,expected",
        [([1, 2, 3, 4], 6), ([3, 1, 2, 4], 7), ([3, 4, -1, -3], 4)],
    )
    def test_list_of_4_ints_should_return_max_non_adjacent_sum(
        self, given_input, expected
    ):
        assert largest_non_adjacent_sum(given_input) == expected

    def test_sample_problem(self):
        assert largest_non_adjacent_sum([2, 4, 6, 2, 5]) == 13


from flask import Flask, request, Response
import jsonpickle
import numpy as np
app = Flask(__name__)
import requests as req

url = 'http://localhost:80/Edge/3/1'

@app.route("/capture/", methods=['POST'])
def capture():

    r = request
    # convert string of image data to uint8
    nparr = np.fromstring(r.data, np.uint8)
    # decode image
    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    img_name = "opencv_frame_{}.png".format(0)
    # do some fancy processing here....
    cv2.imwrite(img_name, img)

    # build a response dict to send back to client
    response = {'message': 'image received. size={}x{}'.format(img.shape[1], img.shape[0])
                }
    # encode response using jsonpickle
    response_pickled = jsonpickle.encode(response)

    return Response(response=response_pickled, status=200, mimetype="application/json")

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=90)


#!/usr/bin/python

# Python中列表是可变的，这是它区别于字符串和元组的最重要的特点，一句话概括即：列表可以修改，而字符串和元组不能。
a = [66.25, 333, 333, 1, 1234.5]
print(a.count(333), a.count(66.25), a.count('x'))
a.insert(2, -1)
a.append(333)
a.index(333)
a.remove(333)
a.reverse()
a.sort()

'''list.append(x)	把一个元素添加到列表的结尾，相当于 a[len(a):] = [x]。
list.extend(L)	通过添加指定列表的所有元素来扩充列表，相当于 a[len(a):] = L。
list.insert(i, x)	在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引，例如 a.insert(0, x) 会插入到整个列表之前，而 a.insert(len(a), x) 相当于 a.append(x) 。
list.remove(x)	删除列表中值为 x 的第一个元素。如果没有这样的元素，就会返回一个错误。
list.pop([i])	从列表的指定位置删除元素，并将其返回。如果没有指定索引，a.pop()返回最后一个元素。元素随即从列表中被删除。（方法中 i 两边的方括号表示这个参数是可选的，而不是要求你输入一对方括号，你会经常在 Python 库参考手册中遇到这样的标记。）
list.clear()	移除列表中的所有项，等于del a[:]。
list.index(x)	返回列表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。
list.count(x)	返回 x 在列表中出现的次数。
list.sort()	对列表中的元素进行排序。
list.reverse()	倒排列表中的元素。
list.copy()	返回列表的浅复制，等于a[:]。'''

'''注意：类似 insert, remove 或 sort 等修改列表的方法没有返回值'''

# 将列表当做堆栈使用
'''列表方法使得列表可以很方便的作为一个堆栈来使用，堆栈作为特定的数据结构，最先进入的元素最后一个被释放（后进先出）。
用 append() 方法可以把一个元素添加到堆栈顶。用不指定索引的 pop() 方法可以把一个元素从堆栈顶释放出来。'''

stack = [3, 4, 5]
stack.append(6)
stack.append(7)
print(stack)
print(stack.pop())
print(stack.pop())
print(stack.pop())

# 将列表当作队列使用
'''可以把列表当做队列用，只是在队列里第一加入的元素，第一个取出来；但是拿列表用作这样的目的效率不高。
在列表的最后添加或者弹出元素速度快，然而在列表里插入或者从头部弹出速度却不快（因为所有其他的元素都得一个一个地移动）。'''
from collections import deque
queue = deque(["Eric", "John", "Michael"])
queue.append("Terry")           # Terry arrives
queue.append("Graham")          # Graham arrives
queue.popleft()                 # The first to arrive now leaves
queue.popleft()                 # The second to arrive now leaves
print(queue)                    # Remaining queue in order of arrival

# 列表推导式
'''列表推导式提供了从序列创建列表的简单途径。通常应用程序将一些操作应用于某个序列的每个元素，用其获得的结果作为生成新列表的元素，
或者根据确定的判定条件创建子序列。
每个列表推导式都在 for 之后跟一个表达式，然后有零到多个 for 或 if 子句。
返回结果是一个根据表达从其后的 for 和 if 上下文环境中生成出来的列表。如果希望表达式推导出一个元组，就必须使用括号。'''
vec = [2, 4, 6]
print([3 * x for x in vec])

print([[x, x ** 2] for x in vec])
''''''
freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
print([weapon.strip() for weapon in freshfruit])

''''''
print([3 * x for x in vec if x > 3])

''''''
vec1 = [2, 4, 6]
vec2 = [4, 3, -9]
print([x * y for x in vec1 for y in vec2])
# [8, 6, -18, 16, 12, -36, 24, 18, -54]
print([x + y for x in vec1 for y in vec2])
# [6, 5, -7, 8, 7, -5, 10, 9, -3]
print([vec1[i] * vec2[i] for i in range(len(vec1))])
# [8, 12, -54]

''''''
print([str(round(355/113, i)) for i in range(1, 6)])
# ['3.1', '3.14', '3.142', '3.1416', '3.14159']

# 嵌套列表解析

# 3X4的矩阵列表
matrix = [[1, 2, 3, 4],
          [5, 6, 7, 8],
          [9, 10, 11, 12],
          ]

# 将3X4的矩阵列表转换为4X3列表
print([[row[i] for row in matrix] for i in range(4)])
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

# 也可以使用以下方法来实现
transposed = []
for i in range(4):
    transposed.append([row[i] for row in matrix])

print(transposed)
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

# 另外一种实现方法
transposed = []
for i in range(4):
    # the following 3 lines implement the nested listcomp
    transposed_row = []
    for row in matrix:
        transposed_row.append(row[i])
        transposed.append(transposed_row)

print(transposed)
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

'''del 语句
使用 del 语句可以从一个列表中依索引而不是值来删除一个元素。这与使用 pop() 返回一个值不同。
可以用 del 语句从列表中删除一个切割，或清空整个列表（我们以前介绍的方法是给该切割赋一个空列表）'''

a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0]
print(a)
# [1, 66.25, 333, 333, 1234.5]
del a[2:4]
print(a)
# [1, 66.25, 1234.5]
del a[:]
print(a)
# []

# 也可以用 del 删除实体变量：
del a

'''元组和序列
元组由若干逗号分隔的值组成'''
t = 12345, 54321, 'hello!'
print(t[0])
# 12345
print(t)
# (12345, 54321, 'hello!')
# Tuples may be nested:
u = t, (1, 2, 3, 4, 5)
print(u)
# ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))

'''如你所见，元组在输出时总是有括号的，以便于正确表达嵌套结构。
在输入时可能有或没有括号， 不过括号通常是必须的（如果元组是更大的表达式的一部分）。'''

# 集合
a = {x for x in 'abracadabra' if x not in 'abc'}
print(a)
# {'r', 'd'}

# 字典

# 遍历技巧
'''在字典中遍历时，关键字和对应的值可以使用 items() 方法同时解读出来'''
knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
    print(k, v)

'''同时遍历两个或更多的序列，可以使用 zip() 组合'''
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):
    print('What is your {0}?  It is {1}.'.format(q, a))

'''要反向遍历一个序列，首先指定这个序列，然后调用 reversed() 函数'''
for i in reversed(range(1, 10, 2)):
    print(i)

'''要按顺序遍历一个序列，使用 sorted() 函数返回一个已排序的序列，并不修改原值'''
basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
for f in sorted(set(basket)):
    print(f)


# jukebox.py

# A GUI interface that shows you artists, their albums,
# and the songs in those albums.

# Lecture: 221. Simple Database Browser

# I left is debug print statements.

# - Jim

import sqlite3
import tkinter

# Connect to the DB:
music_db_conn = sqlite3.connect("./music.sqlite")

# Which artist is presently selected?
# I'm not a fan of global variables.
# However, some artists may have albums of the same name.
# So, we need to know the artist's name in order to get the
# right album to display the songs.
# There's no way to pass the artist info to the function that
# pulls the songs from an album (at least there is no way
# that I know of w/ my current python skill.)
artist_id: None = None

# We are creating own class for the listbox + scrolling:

class Scrollbox(tkinter.Listbox):

    # This class has a listbox + a scrollbar.
    # It's parent's class is a listbox.  We add
    # in a scrollbar.

    def __init__(self, window, **kwargs):
        # Create the list box.
        super().__init__(window, **kwargs)

        # Add the scroll bar, and associate its movement to the list box.
        self.scrollbar = tkinter.Scrollbar(window, orient=tkinter.VERTICAL, command=self.yview)

        # Associate movement w/i the list box with the scroll bar's location.
        self["yscrollcommand"] = self.scrollbar.set

    # Overwriting the parent's (Listbox)  "grid" function.
    def grid(self, row, column, sticky="nsw", rowspan=1, columnspan=1, **kwargs):
        # Put the listbox on the left, and the scrollbar on the right.
        super().grid(row=row, column=column, sticky=sticky, rowspan=rowspan, columnspan=columnspan, **kwargs)
        self.scrollbar.grid(row=row, column=column, sticky="nse", rowspan=rowspan)

# End of classes

# Start of functions:

def get_albums(event):

    # global artist_id: We need to share this info so that we can
    # select the correct album.  Some album names are common like
    # "Greatest Hits".
    global artist_id

    # Get the artist name from the selection.
    list_box = event.widget
    # "list_box" is the list box had is calling
    # this function.  It has a function called
    # "curselection()".  It returns a tuple of
    # the currently selected items - their indexes.  (You can
    # have more than 1 selection.)  So, we are
    # just going to take item [0] in the tuple
    # b/c mostly likely we only selected 1 item.
    # curselection --> I guess it stands for "current selection".

    # The new version of tkinter has the "binding()" function
    # send an event, to the bound function, when you select a
    # different list box (call it ListBoxB).  Let's call this a
    # "deselect" of your original list box.  So, ListBoxA will send
    # a "ListboxSelect" event to its attached function.  And,
    # "curselection" will contain an empty tuple b/c no items,
    # in ListBoxA, are activitely/currently selected.  You need
    # to deal with that:  (Really dumb design in my opinion!
    # However, m/b I'll learn it's usefulness in time.)  You,
    # now, need to test for that!

    print("curselection is '{}'.".format(list_box.curselection()))
    if not list_box.curselection():
        # We have a deselection.
        # False call.
        # Do nothing.  Return.
        print("it's null")
        return

    # If you are here, then you have a value from "curselection()".
    # This time, the function was not called as a result of a deselection!

    # Get the albums for the artist, and populate the list box:
    index = list_box.curselection()[0]
    print("index is: '{}', type: '{}'.".format(index, type(index)))

    # This will be the actual value at that index aka the
    # value that was populated into it: the name of the band (as a string).

    # Either form below is fine.
    # selected_artist_name = tuple([list_box.get(album_index)])
    selected_artist_name = list_box.get(index)
    print("selected_artist_name is: '{}', type: '{}'.".format(selected_artist_name, type(selected_artist_name)))

    # We want the id number associated w/ the artist name.
    # This way we can use it to find the artist's albums in the album table.
    # The ".execute" statement returns an iterable.
    # We want grab the 1 result from it --> .fetchone()
    # Note: The replacement field list needs to be a tuple.
    # Also, is you just do tuple(some_string), the tuple will make each
    # character 1 element in the tuple.  Hence, we need to declare the
    # contents of "selected_artist_name" as 1 item in the tuple via [...].
    artist_id = music_db_conn.execute("SELECT artists._id FROM artists WHERE artists.name=?",
                                      tuple([selected_artist_name])).fetchone()

    print("Artist ID is: ", artist_id)

    # Get the artist's albums from the DB:
    albums_for_artist = []

    # The "?" replacement expects a tuple.  "artist_id" is a tuple b/c that is
    # what ".execute" returns.
    for row in music_db_conn.execute("SELECT albums.name FROM albums WHERE "
                                     "albums.artist = ? ORDER BY albums.name",  artist_id):
        # Remember ".execute" returns a tuple even when
        # you only get 1 item per row returned.  So, you
        # specifically have to get access the item via row[0].
        print(row[0])
        albums_for_artist.append(row[0])

    # Display the albums in the album scroll box:
    # (The "album_label_var" holds what is displayed
    # in the list box.
    album_label_var.set(tuple(albums_for_artist))

    # Reset the songs list as we are now onto a new artist.
    song_label_var.set(("Choose an album",))

    print("-----------------------------")

def get_songs(song_event):
    print("In 'get_songs'.")
    # What list box called this function?
    list_box = song_event.widget

    # The new version of tkinter has the "binding()" function
    # send an event, to the bound function, when you select a
    # different list box (call it ListBoxB).  Let's call this a
    # "deselect" of your original list box.  So, ListBoxA will send
    # a "ListboxSelect" event to its attached function.  And,
    # "curselection" will contain an empty tuple b/c no items,
    # in ListBoxA, are activitely/currently selected.  You need
    # to deal with that:  (Really dumb design in my opinion!
    # However, m/b I'll learn it's usefulness in time.)  You,
    # now, need to test for that!

    print("curselection is '{}'.".format(list_box.curselection()))
    if not list_box.curselection():
        # We have a deselection.
        # False call.
        # Do nothing.  Return.
        print("it's null")
        return

    # Get the item selected:
    # Note: > 1 item can be selected.  We are only
    # taking the 1st.
    # "curselection" = current selection
    # We are getting the index number not the album title.
    index = list_box.curselection()[0]
    print("index is: '{}', type: '{}'.".format(index, type(index)))

    # Get the album name.
    album_name = list_box.get(index)

    print(album_name, type(album_name))

    # Get the album id from the DB.
    # .execute creates an iterable.  Assume 1 entry selected (or if > 1, just
    # grab the 1st).  Use "fetchone()" to get 1 row.
    # .execute returns a tuple for each row.
    print("album name is: '{}'.  artist_id is '{}'.  artist_id type is: '{}'."
          .format(album_name, artist_id, type(artist_id)))

    # Note: artist_id is a tuple (w/ 1 entry).  It needs to be a single entry.
    # As a result, using: artist_id[0]
    album_id = music_db_conn.execute("SELECT _id FROM albums WHERE albums.name=? "
                                     "AND albums.artist=?", (album_name, artist_id[0])).fetchone()

    # sql_where = " WHERE " + self.field + "=? AND " + self.link_field + "=?"

    # Get the songs for this album:
    songs_for_album = []

    for row in music_db_conn.execute("SELECT songs.title FROM songs WHERE "
                                     "songs.album  = ? ORDER BY songs.track",  album_id):
        # .execute returns a tuple (of 1 element in this case).
        # Grab element 0
        songs_for_album.append(row[0])

        print(row)

    song_label_var.set(tuple(songs_for_album))
    print("*********************************")

# ===== Main Window =====
# Create the main GUI window:
main_window = tkinter.Tk()
main_window.title("Music DB Browser")
main_window.geometry('1024x768')

# Add the 4 columns.  Let 3 will have data.
# The last column, the one on the right will hold no data.
# It's just to put some space b/t the 3rd column, and the right
# side of the GUI.
main_window.columnconfigure(0, weight=2)    # Artists
main_window.columnconfigure(1, weight=2)    # Records
main_window.columnconfigure(2, weight=3)    # Songs
main_window.columnconfigure(3, weight=1)    # Just a spacer

# Add teh 4 rows.
main_window.rowconfigure(0, weight=1)    # For Column Headings
main_window.rowconfigure(1, weight=5)    # Artists, Records, & Songs
main_window.rowconfigure(2, weight=5)    # Artists cont.
main_window.rowconfigure(3, weight=1)    # Just a spacer

# Headings for the columns:
# (The teacher didn't see a need for assigning these to variables.
# I guess they won't change.)
tkinter.Label(main_window, text="Artists").grid(row=0, column=0)
tkinter.Label(main_window, text="Albums").grid(row=0, column=1)
tkinter.Label(main_window, text="Songs").grid(row=0, column=2)

# =====  Artists Scrollbox =====
# Remember: the artist listbox spans to 2 rows.
artist_listbox = Scrollbox(main_window)
# padx=(30, 0) --> Pad the left side by 30 pixels.
artist_listbox.grid(row=1, column=0, sticky='nsew', rowspan=2, padx=(30, 0))
artist_listbox.config(border=2, relief="sunken")

# Populate the list:
# (Put them in alphabetically.)
for artist_name in music_db_conn.execute("SELECT artists.name FROM artists ORDER BY  artists.name"):
    # You are iterating through a sorted list of the artists.
    # Add each 1 to the end of the list.
    # Note: The execute statement returns tuples!
    # So, you need to do artist_name[0].  Otherwise,
    # a tuple is put into the list box.
    artist_listbox.insert(tkinter.END, artist_name[0])

# When an artist is selected, call the "get_albums" function
# which will populate the Albums Scroll Box w/ their albums:
# Notes:
# 1) 'get_albums' is it's own function, and not part of the
# "Scrollbox" class.
# 2)  The "binding" will pass the event: "ListboxSelect" to
# the function.  It will pass the list box that triggered the
# function call.  (At that point (when the function is called),
# the list box will have a function called "curselection()"
# which will return a list of the currently selected items.
# (More than 1 item can be selected.)
# 3) curselection -> returns a list of the indexes!
# 4) IMPORTANT: new version of tkinter will cause:
# Unfortunately, the function "get_albums" will also get
# called on a deselect of this list box.  (When you
# select an element in another list box.)  And, we need to
# handle that in "get_songs".
artist_listbox.bind("<<ListboxSelect>>", get_albums)

# ===== Albums Scrollbox =====
# Initial text to display at the top of the scroll box.
# Later, it will hold/display a list of albums for the selected artist.
album_label_var = tkinter.Variable(main_window)
album_label_var.set(("Choose an artist",))

album_listbox = Scrollbox(main_window, listvariable=album_label_var)
album_listbox.grid(row=1, column=1, sticky="nsew", padx=(30, 0))
album_listbox.config(border=2, relief="sunken")

# Whenever an album is selected, call the "get_songs" function.
# Unfortunately, it will also get called on a deselect of this list box.
# (When you select an element in another list box.)
# And, we need to handle that in "get_songs".
album_listbox.bind("<<ListboxSelect>>", get_songs)

# ===== Song Scrollbox =====
# Initial text to display at the top of the scroll box.
# Later, it will hold/display a list of songs for the selected album.
song_label_var = tkinter.Variable(main_window)
song_label_var.set(("Choose an album",))

song_listbox = Scrollbox(main_window, listvariable=song_label_var)
song_listbox.grid(row=1, column=2, sticky="nsew", padx=(30, 0))
song_listbox.config(border=2, relief="sunken")

# ====== Main Loop =====
# Run the GUI:
main_window.mainloop()

# Done w/ the GUI.  Close the DB connection:
print("Closing the DB connection.")
music_db_conn.close()


import random
import argparse
import random
import time
import uuid
import json

import faker
import numpy as np

def random_timestamp_within(start, end, format, prop):
    """Get a time at a proportion of a range of two formatted times.

    start and end should be strings specifying times formated in the
    given format (strftime-style), giving an interval [start, end].
    prop specifies how a proportion of the interval to be taken after
    start.  The returned time will be in the specified format.
    """

    stime = time.mktime(time.strptime(start, format))
    etime = time.mktime(time.strptime(end, format))

    ptime = stime + prop * (etime - stime)
    return ptime

EVENTS = ['view', 'rate', 'buy']
RATINGS = [x for x in range(1, 5+1)]

def get_event():

    event = np.random.choice(EVENTS, 1, p=[0.6, 0.1, 0.3])[0]

    context = None

    if event == 'rate':
        return event, np.random.choice(RATINGS, 1)[0], context
    else:
        return event, None, context

def main(n_users, n_products, n_events, start_date, end_date, output):

    with open(output, 'w') as fp:

        for _ in range(n_events):

            event_type, weight, context = get_event()

            event = {
                'id': str(uuid.uuid4()),
                'type': event_type,
                'weight': weight,
                'timestamp': int(random_timestamp_within(start_date, end_date, '%Y-%m-%d', np.random.uniform(0, 1))),
                'context': context,
                'agent': {
                    'id': 'user-%d' % np.random.randint(1, n_users),
                    'type': 'user'
                },
                'element': {
                    'id': 'item-%d' % np.random.randint(1, n_products),
                    'type': 'product'
                }
            }

            fp.write(json.dumps(event))
            fp.write('\n')

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Generate fake ecommerce data.')
    parser.add_argument('--nusers', type=int, required=True, help='Number of users to generate')
    parser.add_argument('--nproducts', type=int, required=True, help='Number of users to generate')
    parser.add_argument('--nevents', type=int, required=True, help='Number of events to generate')
    parser.add_argument('--start_date', type=str, required=True, help='Starting date, format: yyyy-mm-dd')
    parser.add_argument('--end_date', type=str, required=True, help='End date, format: yyyy-mm-dd')
    parser.add_argument('--output', type=str, required=True, help='Output file, e.g. data.json')

    args = parser.parse_args()

    fake = faker.Factory()

    main(args.nusers, args.nproducts, args.nevents, args.start_date, args.end_date, args.output)


from tinydb import Query, where

from time import time
import config
import random

from fastapi import APIRouter, Request, Response

from core_common import core_process_request, core_prepare_response, E
from core_database import get_db

router = APIRouter(prefix="/local2", tags=["local2"])
router.model_whitelist = ["LDJ"]

def get_profile(cid):
    return get_db().table("iidx_profile").get(where("card") == cid)

def get_profile_by_id(iidx_id):
    return get_db().table("iidx_profile").get(where("iidx_id") == iidx_id)

def get_game_profile(cid, game_version):
    profile = get_profile(cid)

    return profile["version"].get(str(game_version), None)

def get_game_profile_by_id(iidx_id, game_version):
    profile = get_profile_by_id(iidx_id)

    return profile["version"].get(str(game_version), None)

def get_id_from_profile(cid):
    profile = get_db().table("iidx_profile").get(where("card") == cid)

    djid = "%08d" % profile["iidx_id"]
    djid_split = "-".join([djid[:4], djid[4:]])

    return profile["iidx_id"], djid_split

def calculate_folder_mask(profile):
    return (
        profile.get("_show_category_grade", 0) << 0
        | (profile.get("_show_category_status", 0) << 1)
        | (profile.get("_show_category_difficulty", 0) << 2)
        | (profile.get("_show_category_alphabet", 0) << 3)
        | (profile.get("_show_category_rival_play", 0) << 4)
        | (profile.get("_show_category_rival_winlose", 0) << 6)
        | (profile.get("_show_rival_shop_info", 0) << 7)
        | (profile.get("_hide_play_count", 0) << 8)
        | (profile.get("_show_score_graph_cutin", 0) << 9)
        | (profile.get("_classic_hispeed", 0) << 10)
        | (profile.get("_hide_iidx_id", 0) << 12)
    )

@router.post("/{gameinfo}/IIDX30pc/get")
async def iidx30pc_get(request: Request):
    request_info = await core_process_request(request)
    game_version = request_info["game_version"]

    cid = request_info["root"][0].attrib["cid"]
    profile = get_game_profile(cid, game_version)
    djid, djid_split = get_id_from_profile(cid)

    rival_ids = [
        profile.get("sp_rival_1_iidx_id", 0),
        profile.get("sp_rival_2_iidx_id", 0),
        profile.get("sp_rival_3_iidx_id", 0),
        profile.get("sp_rival_4_iidx_id", 0),
        profile.get("sp_rival_5_iidx_id", 0),
        profile.get("sp_rival_6_iidx_id", 0),
        profile.get("dp_rival_1_iidx_id", 0),
        profile.get("dp_rival_2_iidx_id", 0),
        profile.get("dp_rival_3_iidx_id", 0),
        profile.get("dp_rival_4_iidx_id", 0),
        profile.get("dp_rival_5_iidx_id", 0),
        profile.get("dp_rival_6_iidx_id", 0),
    ]
    rivals = {}
    for idx, r in enumerate(rival_ids):
        if r == 0:
            continue
        rivals[idx] = {}
        rivals[idx]["spdp"] = 1 if idx < 6 else 2

        rival_profile = get_game_profile_by_id(r, game_version)
        rdjid = "%08d" % r
        rdjid_split = "-".join([rdjid[:4], rdjid[4:]])

        rivals[idx]["djid"] = rdjid
        rivals[idx]["djid_split"] = rdjid_split
        rivals[idx]["djname"] = rival_profile["djname"]
        rivals[idx]["region"] = rival_profile["region"]
        rivals[idx]["sa"] = rival_profile["sach"]
        rivals[idx]["sg"] = rival_profile["grade_single"]
        rivals[idx]["da"] = rival_profile["dach"]
        rivals[idx]["dg"] = rival_profile["grade_double"]
        rivals[idx]["body"] = rival_profile["body"]
        rivals[idx]["face"] = rival_profile["face"]
        rivals[idx]["hair"] = rival_profile["hair"]
        rivals[idx]["hand"] = rival_profile["hand"]
        rivals[idx]["head"] = rival_profile["head"]

    current_time = round(time())

    response = E.response(
        E.IIDX30pc(
            E.pcdata(
                d_auto_adjust=profile["d_auto_adjust"],
                d_auto_scrach=profile["d_auto_scrach"],
                d_camera_layout=profile["d_camera_layout"],
                d_disp_judge=profile["d_disp_judge"],
                d_exscore=profile["d_exscore"],
                d_gauge_disp=profile["d_gauge_disp"],
                d_ghost_score=profile["d_ghost_score"],
                d_gno=profile["d_gno"],
                d_graph_score=profile["d_graph_score"],
                d_gtype=profile["d_gtype"],
                d_hispeed=profile["d_hispeed"],
                d_judge=profile["d_judge"],
                d_judgeAdj=profile["d_judgeAdj"],
                d_lane_brignt=profile["d_lane_brignt"],
                d_liflen=profile["d_liflen"],
                d_notes=profile["d_notes"],
                d_opstyle=profile["d_opstyle"],
                d_pace=profile["d_pace"],
                d_sdlen=profile["d_sdlen"],
                d_sdtype=profile["d_sdtype"],
                d_sorttype=profile["d_sorttype"],
                d_sub_gno=profile["d_sub_gno"],
                d_timing=profile["d_timing"],
                d_timing_split=profile["d_timing_split"],
                d_tsujigiri_disp=profile["d_tsujigiri_disp"],
                d_tune=profile["d_tune"],
                d_visualization=profile["d_visualization"],
                dach=profile["dach"],
                dp_opt=profile["dp_opt"],
                dp_opt2=profile["dp_opt2"],
                dpnum=profile["dpnum"],
                gpos=profile["gpos"],
                id=djid,
                idstr=djid_split,
                mode=profile["mode"],
                name=profile["djname"],
                ngrade=profile["ngrade"],
                pid=profile["region"],
                pmode=profile["pmode"],
                rtype=profile["rtype"],
                s_auto_adjust=profile["s_auto_adjust"],
                s_auto_scrach=profile["s_auto_scrach"],
                s_camera_layout=profile["s_camera_layout"],
                s_disp_judge=profile["s_disp_judge"],
                s_exscore=profile["s_exscore"],
                s_gauge_disp=profile["s_gauge_disp"],
                s_ghost_score=profile["s_ghost_score"],
                s_gno=profile["s_gno"],
                s_graph_score=profile["s_graph_score"],
                s_gtype=profile["s_gtype"],
                s_hispeed=profile["s_hispeed"],
                s_judge=profile["s_judge"],
                s_judgeAdj=profile["s_judgeAdj"],
                s_lane_brignt=profile["s_lane_brignt"],
                s_liflen=profile["s_liflen"],
                s_notes=profile["s_notes"],
                s_opstyle=profile["s_opstyle"],
                s_pace=profile["s_pace"],
                s_sdlen=profile["s_sdlen"],
                s_sdtype=profile["s_sdtype"],
                s_sorttype=profile["s_sorttype"],
                s_sub_gno=profile["s_sub_gno"],
                s_timing=profile["s_timing"],
                s_timing_split=profile["s_timing_split"],
                s_tsujigiri_disp=profile["s_tsujigiri_disp"],
                s_tune=profile["s_tune"],
                s_visualization=profile["s_visualization"],
                sach=profile["sach"],
                sp_opt=profile["sp_opt"],
                spnum=profile["spnum"],
            ),
            E.qprodata(
                [
                    profile["head"],
                    profile["hair"],
                    profile["face"],
                    profile["hand"],
                    profile["body"],
                ],
                __type="u32",
                __size=5 * 4,
            ),
            E.skin(
                [
                    profile["frame"],
                    profile["turntable"],
                    profile["explosion"],
                    profile["bgm"],
                    calculate_folder_mask(profile),
                    profile["sudden"],
                    profile["judge_pos"],
                    profile["categoryvoice"],
                    profile["note"],
                    profile["fullcombo"],
                    profile["keybeam"],
                    profile["judgestring"],
                    -1,
                    profile["soundpreview"],
                    profile["grapharea"],
                    profile["effector_lock"],
                    profile["effector_type"],
                    profile["explosion_size"],
                    profile["alternate_hcn"],
                    profile["kokokara_start"],
                ],
                __type="s16",
            ),
            E.spdp_rival(
                flg=-1
            ),  # required for rivals to load after switching spdp in music select
            E.rlist(
                *[
                    E.rival(
                        E.is_robo(0, __type="bool"),
                        E.shop(name=config.arcade),
                        E.qprodata(
                            body=rivals[r]["body"],
                            face=rivals[r]["face"],
                            hair=rivals[r]["hair"],
                            hand=rivals[r]["hand"],
                            head=rivals[r]["head"],
                        ),
                        da=rivals[r]["da"],
                        dg=rivals[r]["dg"],
                        djname=rivals[r]["djname"],
                        id=rivals[r]["djid"],
                        id_str=rivals[r]["djid_split"],
                        pid=rivals[r]["region"],
                        sa=rivals[r]["sa"],
                        sg=rivals[r]["sg"],
                        spdp=rivals[r]["spdp"],
                    )
                    for r in rivals
                ],
            ),
            E.ir_data(),
            E.secret_course_data(),
            E.deller(deller=profile["deller"], rate=0),
            E.secret(
                E.flg1(profile.get("secret_flg1", [-1, -1, -1]), __type="s64"),
                E.flg2(profile.get("secret_flg2", [-1, -1, -1]), __type="s64"),
                E.flg3(profile.get("secret_flg3", [-1, -1, -1]), __type="s64"),
                E.flg4(profile.get("secret_flg4", [-1, -1, -1]), __type="s64"),
            ),
            E.join_shop(join_cflg=1, join_id=10, join_name=config.arcade, joinflg=1),
            E.leggendaria(
                E.flg1(profile.get("leggendaria_flg1", [-1, -1, -1]), __type="s64")
            ),
            E.grade(
                *[E.g(x, __type="u8") for x in profile["grade_values"]],
                dgid=profile["grade_double"],
                sgid=profile["grade_single"],
            ),
            E.world_tourism_secret_flg(
                E.flg1(profile.get("wt_flg1", [-1, -1, -1]), __type="s64"),
                E.flg2(profile.get("wt_flg2", [-1, -1, -1]), __type="s64"),
            ),
            E.world_tourism(
                *[
                    E.tour_data(
                        tour_id=i,
                        progress=50,  # set to 49 to see WT folders, 50 is completed/hidden
                    )
                    for i in range(16)
                ],
            ),
            E.lightning_setting(
                E.slider(
                    profile.get("lightning_setting_slider", [0] * 7), __type="s32"
                ),
                E.light(
                    profile.get("lightning_setting_light", [1] * 10), __type="bool"
                ),
                E.concentration(
                    profile.get("lightning_setting_concentration", 0), __type="bool"
                ),
                headphone_vol=profile.get("lightning_setting_headphone_vol", 0),
                resistance_sp_left=profile.get(
                    "lightning_setting_resistance_sp_left", 0
                ),
                resistance_sp_right=profile.get(
                    "lightning_setting_resistance_sp_right", 0
                ),
                resistance_dp_left=profile.get(
                    "lightning_setting_resistance_dp_left", 0
                ),
                resistance_dp_right=profile.get(
                    "lightning_setting_resistance_dp_right", 0
                ),
                skin_0=profile.get("lightning_setting_skin_0", 0),
                flg_skin_0=profile.get("lightning_setting_flg_skin_0", 0),
            ),
            E.arena_data(
                E.achieve_data(
                    play_style=0,
                    arena_class=19,
                    rating_value=90,
                    win_count=0,
                    now_winning_streak_count=0,
                    best_winning_streak_count=0,
                    perfect_win_count=0,
                    counterattack_num=0,
                    mission_clear_num=0,
                ),
                E.achieve_data(
                    play_style=1,
                    arena_class=19,
                    rating_value=90,
                    win_count=0,
                    now_winning_streak_count=0,
                    best_winning_streak_count=0,
                    perfect_win_count=0,
                    counterattack_num=0,
                    mission_clear_num=0,
                ),
                E.cube_data(
                    cube=200,
                    season_id=0,
                ),
                E.ranker_data(
                    play_style=0,
                    pref_id=0,
                    rank_num=(random.choice([random.randint(1, 5), 573])),
                ),
                E.ranker_data(
                    play_style=1,
                    pref_id=0,
                    rank_num=(random.choice([random.randint(1, 5), 573])),
                ),
                E.lose_data(
                    play_style=0,
                    lose_value=0,
                ),
                E.lose_data(
                    play_style=1,
                    lose_value=0,
                ),
                E.chat_data(
                    E.is_chat_0(1, __type="bool"),
                    E.is_chat_1(1, __type="bool"),
                    E.is_chat_2(1, __type="bool"),
                    E.is_chat_3(1, __type="bool"),
                    chat_type_0="hi",
                    chat_type_1="やあ",
                    chat_type_2="こんにちは",
                    chat_type_3="おす",
                ),
                E.tendency(
                    play_style=0,
                    rank0=1,
                    rank1=2,
                    rank2=3,
                    rank3=4,
                    rank4=3,
                    rank5=1,
                ),
                E.player_kind_data(
                    kind=(random.choice([random.randint(0, 13), 0])),
                ),
                E.setting(
                    stats_type=0,
                ),
                E.qpro_motion(
                    motion_0=1,
                    motion_1=2,
                    motion_2=3,
                ),
                play_num=6,
                play_num_dp=3,
                play_num_sp=3,
                prev_best_class_sp=18,
                prev_best_class_dp=18,
            ),
            E.follow_data(),
            E.classic_course_data(),
            E.bind_eaappli(),
            E.ea_premium_course(),
            E.enable_qr_reward(),
            E.nostalgia_open(),
            E.language_setting(language=profile["language_setting"]),
            E.movie_agreement(agreement_version=profile["movie_agreement"]),
            E.bpl_virtual(),
            E.lightning_play_data(
                spnum=profile["lightning_play_data_spnum"],
                dpnum=profile["lightning_play_data_dpnum"],
            ),
            E.weekly(
                mid=-1,
                wid=1,
            ),
            E.packinfo(
                music_0=-1,
                music_1=-1,
                music_2=-1,
                pack_id=1,
            ),
            E.kac_entry_info(
                E.enable_kac_deller(),
                E.disp_kac_mark(),
                E.open_kac_common_music(),
                E.open_kac_new_a12_music(),
                E.is_kac_entry(),
                E.is_kac_evnet_entry(),
            ),
            E.orb_data(rest_orb=100, present_orb=100),
            E.visitor(anum=1, pnum=2, snum=1, vs_flg=1),
            E.tonjyutsu(black_pass=-1, platinum_pass=-1),
            E.pay_per_use(item_num=99),
            E.old_linkage_secret_flg(
                song_battle=-1,
            ),
            E.floor_infection4(music_list=-1),
            E.bemani_vote(music_list=-1),
            E.bemani_janken_meeting(music_list=-1),
            E.bemani_rush(music_list_ichika=-1, music_list_nono=-1),
            E.ultimate_mobile_link(E.link_flag(), music_list=-1),
            E.bemani_musiq_fes(music_list=-1),
            E.busou_linkage(music_list=-1),
            E.busou_linkage_2(music_list=-1),
            E.valkyrie_linkage(music_list_1=-1, music_list_2=-1, music_list_3=-1),
            E.bemani_song_battle(music_list=-1),
            E.bemani_mixup(music_list=-1),
            E.ccj_linkage(music_list=-1),
            E.triple_tribe(music_list=-1),
            E.achievements(
                E.trophy(profile.get("achievements_trophy", [])[:10], __type="s64"),
                pack=profile.get("achievements_pack_id", 0),
                pack_comp=profile.get("achievements_pack_comp", 0),
                last_weekly=profile.get("achievements_last_weekly", 0),
                weekly_num=profile.get("achievements_weekly_num", 0),
                visit_flg=profile.get("achievements_visit_flg", 0),
                rival_crush=0,
            ),
            E.notes_radar(
                E.radar_score(
                    profile["notes_radar_single"],
                    __type="s32",
                ),
                style=0,
            ),
            E.notes_radar(
                E.radar_score(
                    profile["notes_radar_double"],
                    __type="s32",
                ),
                style=1,
            ),
            E.dj_rank(
                E.rank(
                    profile["dj_rank_single_rank"],
                    __type="s32",
                ),
                E.point(
                    profile["dj_rank_single_point"],
                    __type="s32",
                ),
                style=0,
            ),
            E.dj_rank(
                E.rank(
                    profile["dj_rank_double_rank"],
                    __type="s32",
                ),
                E.point(
                    profile["dj_rank_double_point"],
                    __type="s32",
                ),
                style=1,
            ),
            E.step(
                E.is_track_ticket(
                    profile["stepup_is_track_ticket"],
                    __type="bool",
                ),
                dp_fluctuation=profile["stepup_dp_fluctuation"],
                dp_level=profile["stepup_dp_level"],
                dp_mplay=profile["stepup_dp_mplay"],
                enemy_damage=profile["stepup_enemy_damage"],
                enemy_defeat_flg=profile["stepup_enemy_defeat_flg"],
                mission_clear_num=profile["stepup_mission_clear_num"],
                progress=profile["stepup_progress"],
                sp_fluctuation=profile["stepup_sp_fluctuation"],
                sp_level=profile["stepup_sp_level"],
                sp_mplay=profile["stepup_sp_mplay"],
                tips_read_list=profile["stepup_tips_read_list"],
                total_point=profile["stepup_total_point"],
            ),
            E.tsujigiri(total_num_sp=287, total_num_dp=286),
            E.tsujigiri_hidden_chara(
                E.appearance_info(
                    appearance_id=1,
                    music_0=-1,
                    music_1=-1,
                    music_2=-1,
                    chara_0=-1,
                    chara_1=-1,
                    chara_2=-1,
                ),
                E.defeat(defeat_flg=0),
                E.total_defeat(
                    E.chara(
                        id=0,
                        num=0,
                    )
                ),
            ),
            E.skin_customize_flg(
                skin_frame_flg=profile["skin_customize_flag_frame"],
                skin_bgm_flg=profile["skin_customize_flag_bgm"],
                skin_lane_flg3=profile["skin_customize_flag_lane"],
            ),
            # E.event_1(
            #     E.flyer_data(
            #         flyer_id=0,
            #         play_num=0,
            #         last_select_genre=0,
            #         flyer_prog=0,
            #         skill_param=0,
            #     ),
            #     E.genre_data(
            #         E.is_complete(0, __type="bool"),
            #         flyer_id=0,
            #         genre_id=0,
            #         play_num=0,
            #         gauge=0,
            #     ),
            #     event_play_num=0,
            #     last_select_flyer_id=0,
            # ),
            # E.player_compe(
            #     E.compe_data(
            #         E.compe_music(
            #             index=0,
            #             music_id=26029,
            #             style_id=0,
            #             note_grade_id=3,
            #             ex_score=6,
            #             pgreat_num=6,
            #             great_num=6,
            #             miss_num=6,
            #         ),
            #         E.compe_music(
            #             index=1,
            #             music_id=29057,
            #             style_id=0,
            #             note_grade_id=3,
            #             ex_score=6,
            #             pgreat_num=6,
            #             great_num=6,
            #             miss_num=6,
            #         ),
            #         E.compe_music(
            #             index=2,
            #             music_id=8027,
            #             style_id=0,
            #             note_grade_id=3,
            #             ex_score=6,
            #             pgreat_num=6,
            #             great_num=6,
            #             miss_num=6,
            #         ),
            #         E.compe_music(
            #             index=3,
            #             music_id=16027,
            #             style_id=0,
            #             note_grade_id=3,
            #             ex_score=6,
            #             pgreat_num=6,
            #             great_num=6,
            #             miss_num=6,
            #         ),
            #         compe_id=0,
            #         forced_option=1,
            #         compe_end_time=current_time,
            #         compe_name="Monkey Business",
            #         maker_name="kors k",
            #         ex_score=666,
            #         pgreat_num=666,
            #         great_num=666,
            #         my_rank=1,
            #         total_join=1,
            #     ),
            # ),
            # E.news(
            #     *[E(x,
            #         E.detail(
            #             music_id=1000,
            #             class_id=3,
            #             news_type=0,
            #             news_data=1572,
            #             news_time=current_time,
            #             dj_name="TEST",
            #         ),
            #         last_read_time=current_time,
            #     )for x in ("news_data_all", "news_data_shop", "news_data_grade", "news_data_rival", "news_data_all_top", "news_data_area_top", "news_data_shop_top")],
            # disp_score_type=0,
            # ),
            # E.exam_data(
            #     E.music(
            #         radar_type=1,
            #         index=0,
            #         music_id=26029,
            #         class_id=3,
            #         rank=1,
            #         score=1,
            #     ),
            #     E.music(
            #         radar_type=1,
            #         index=1,
            #         music_id=29057,
            #         class_id=3,
            #         rank=1,
            #         score=1,
            #     ),
            #     E.music(
            #         radar_type=1,
            #         index=2,
            #         music_id=8027,
            #         class_id=3,
            #         rank=1,
            #         score=1,
            #     ),
            #     E.music(
            #         radar_type=1,
            #         index=3,
            #         music_id=16027,
            #         class_id=3,
            #         rank=1,
            #         score=1,
            #     ),
            #     exam_id=0,
            #     entry_num=1,
            #     rank=1,
            #     total_score=1,
            #     end_time=current_time,
            # ),
            # E.questionnaire(
            #     *[E.questionnaire_data(
            #         questionnaire_id=i,
            #         answer="what is this?"
            #     )for i in range(10)],
            # ),
            E.badge(
                # Base
                E.badge_data(
                    category_id=1,
                    badge_flg_id=23,
                    badge_flg=554321,
                ),
                E.badge_data(
                    category_id=1,
                    badge_flg_id=11,
                    badge_flg=554321,
                ),
                E.badge_equip(
                    E.equip_flg(1, __type="bool"),
                    category_id=1,
                    badge_flg_id=23,
                    index=5,
                    slot=0,
                ),
                E.badge_equip(
                    E.equip_flg(1, __type="bool"),
                    category_id=1,
                    badge_flg_id=11,
                    index=5,
                    slot=4,
                ),
                # Class
                E.badge_data(
                    category_id=2,
                    badge_flg_id=0,
                    badge_flg=191919191919,
                ),
                E.badge_equip(
                    E.equip_flg(1, __type="bool"),
                    category_id=2,
                    badge_flg_id=0,
                    index=2,
                    slot=3,
                ),
                E.badge_equip(
                    E.equip_flg(1, __type="bool"),
                    category_id=2,
                    badge_flg_id=0,
                    index=5,
                    slot=1,
                ),
                # Radar Rank
                E.badge_data(
                    category_id=7,
                    badge_flg_id=1,
                    badge_flg=1010101010,
                ),
                E.badge_equip(
                    E.equip_flg(1, __type="bool"),
                    category_id=7,
                    badge_flg_id=1,
                    index=2,
                    slot=2,
                ),
            ),
        )
    )

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/common")
async def iidx30pc_common(request: Request):
    request_info = await core_process_request(request)

    response = E.response(
        E.IIDX30pc(
            E.monthly_mranking(
                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                __type="u16",
            ),
            E.total_mranking(
                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                __type="u16",
            ),
            # E.internet_ranking(),
            # E.secret_ex_course(),
            E.kac_mid([-1, -1, -1, -1, -1], __type="s32"),
            E.kac_clid([2, 2, 2, 2, 2], __type="s32"),
            E.ir(beat=3),
            E.cm(compo="cm_ultimate", folder="cm_ultimate", id=0),
            E.tdj_cm(
                E.cm(filename="cm_bn_001", id=0),
                E.cm(filename="cm_bn_002", id=1),
                E.cm(filename="event_bn_001", id=2),
                E.cm(filename="event_bn_004", id=3),
                E.cm(filename="event_bn_006", id=4),
                E.cm(filename="fipb_001", id=5),
                E.cm(filename="year_bn_004", id=6),
                E.cm(filename="year_bn_005", id=7),
                E.cm(filename="year_bn_006_2", id=8),
                E.cm(filename="year_bn_007", id=9),
            ),
            # E.playvideo_disable_music(E.music(musicid=-1)),
            # E.music_movie_suspend(E.music(music_id=-1, kind=0, name='')),
            # E.bpl_virtual(),
            E.movie_agreement(version=1),
            E.license("None", __type="str"),
            E.file_recovery(url=str(config.ip)),
            E.movie_upload(url=str(config.ip)),
            # E.button_release_frame(frame=''),
            # E.trigger_logic_type(type=''),
            # E.cm_movie_info(type=''),
            E.escape_package_info(),
            # E.expert(phase=1),
            # E.expert_random_secret(phase=1),
            E.boss(phase=0),  # disable event
            E.vip_pass_black(),
            E.eisei(open=1),
            E.deller_bonus(open=1),
            E.newsong_another(open=1),
            # E.pcb_check(flg=0)
            E.expert_secret_full_open(),
            E.eaorder_phase(phase=-1),
            E.common_evnet(flg=-1),
            E.system_voice_phase(phase=random.randint(1, 10)),  # TODO: Figure out range
            E.extra_boss_event(phase=6),
            E.event1_phase(phase=4),
            E.premium_area_news(open=1),
            E.premium_area_qpro(open=1),
            # E.disable_same_triger(frame=-1),
            E.play_video(),
            E.world_tourism(open_list=1),
            E.bpl_battle(phase=1),
            E.display_asio_logo(),
            # E.force_rom_check(),
            E.lane_gacha(),
            # E.fps_fix(),
            # E.save_unsync_log(),
            E.tourism_booster(),
            E.ameto_event(),
            expire=600,
        )
    )

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/save")
async def iidx30pc_save(request: Request):
    request_info = await core_process_request(request)
    game_version = request_info["game_version"]

    xid = int(request_info["root"][0].attrib["iidxid"])
    cid = request_info["root"][0].attrib["cid"]
    clt = int(request_info["root"][0].attrib["cltype"])

    profile = get_profile(cid)
    game_profile = profile["version"].get(str(game_version), {})

    for k in [
        "d_auto_adjust",
        "d_auto_scrach",
        "d_camera_layout",
        "d_disp_judge",
        "d_gauge_disp",
        "d_ghost_score",
        "d_gno",
        "d_graph_score",
        "d_gtype",
        "d_hispeed",
        "d_judge",
        "d_judgeAdj",
        "d_lane_brignt",
        "d_notes",
        "d_opstyle",
        "d_pace",
        "d_sdlen",
        "d_sdtype",
        "d_sorttype",
        "d_sub_gno",
        "d_timing",
        "d_timing_split",
        "d_tsujigiri_disp",
        "d_visualization",
        "dp_opt",
        "dp_opt2",
        "gpos",
        "mode",
        "ngrade",
        "pmode",
        "rtype",
        "s_auto_adjust",
        "s_auto_scrach",
        "s_camera_layout",
        "s_disp_judge",
        "s_gauge_disp",
        "s_ghost_score",
        "s_gno",
        "s_graph_score",
        "s_gtype",
        "s_hispeed",
        "s_judge",
        "s_judgeAdj",
        "s_lane_brignt",
        "s_notes",
        "s_opstyle",
        "s_pace",
        "s_sdlen",
        "s_sdtype",
        "s_sorttype",
        "s_sub_gno",
        "s_timing",
        "s_timing_split",
        "s_tsujigiri_disp",
        "s_visualization",
        "sp_opt",
    ]:
        if k in request_info["root"][0].attrib:
            game_profile[k] = request_info["root"][0].attrib[k]

    for k in [
        ("d_liflen", "d_lift"),
        ("dach", "d_achi"),
        ("s_liflen", "s_lift"),
        ("sach", "s_achi"),
    ]:
        if k[1] in request_info["root"][0].attrib:
            game_profile[k[0]] = request_info["root"][0].attrib[k[1]]

    lightning_setting = request_info["root"][0].find("lightning_setting")
    if lightning_setting is not None:
        for k in [
            "headphone_vol",
            "resistance_dp_left",
            "resistance_dp_right",
            "resistance_sp_left",
            "resistance_sp_right",
        ]:
            game_profile["lightning_setting_" + k] = int(lightning_setting.attrib[k])

        slider = lightning_setting.find("slider")
        if slider is not None:
            game_profile["lightning_setting_slider"] = [
                int(x) for x in slider.text.split(" ")
            ]

        light = lightning_setting.find("light")
        if light is not None:
            game_profile["lightning_setting_light"] = [
                int(x) for x in light.text.split(" ")
            ]

        concentration = lightning_setting.find("concentration")
        if concentration is not None:
            game_profile["lightning_setting_concentration"] = int(concentration.text)

    lightning_customize_flg = request_info["root"][0].find("lightning_customize_flg")
    if lightning_customize_flg is not None:
        for k in [
            "flg_skin_0",
        ]:
            game_profile["lightning_setting_" + k] = int(
                lightning_customize_flg.attrib[k]
            )

    secret = request_info["root"][0].find("secret")
    if secret is not None:
        for k in ["flg1", "flg2", "flg3", "flg4"]:
            flg = secret.find(k)
            if flg is not None:
                game_profile["secret_" + k] = [int(x) for x in flg.text.split(" ")]

    leggendaria = request_info["root"][0].find("leggendaria")
    if leggendaria is not None:
        for k in ["flg1"]:
            flg = leggendaria.find(k)
            if flg is not None:
                game_profile["leggendaria_" + k] = [int(x) for x in flg.text.split(" ")]

    step = request_info["root"][0].find("step")
    if step is not None:
        for k in [
            "dp_fluctuation",
            "dp_level",
            "dp_mplay",
            "enemy_damage",
            "enemy_defeat_flg",
            "mission_clear_num",
            "progress",
            "sp_fluctuation",
            "sp_level",
            "sp_mplay",
            "tips_read_list",
            "total_point",
        ]:
            game_profile["stepup_" + k] = int(step.attrib[k])

        is_track_ticket = step.find("is_track_ticket")
        if is_track_ticket is not None:
            game_profile["stepup_is_track_ticket"] = int(is_track_ticket.text)

    dj_ranks = request_info["root"][0].findall("dj_rank")
    dj_ranks = [] if dj_ranks is None else dj_ranks
    for dj_rank in dj_ranks:
        style = int(dj_rank.attrib["style"])

        rank = dj_rank.find("rank")
        game_profile["dj_rank_" + ["single", "double"][style] + "_rank"] = [
            int(x) for x in rank.text.split(" ")
        ]

        point = dj_rank.find("point")
        game_profile["dj_rank_" + ["single", "double"][style] + "_point"] = [
            int(x) for x in point.text.split(" ")
        ]

    notes_radars = request_info["root"][0].findall("notes_radar")
    notes_radars = [] if notes_radars is None else notes_radars
    for notes_radar in notes_radars:
        style = int(notes_radar.attrib["style"])
        score = notes_radar.find("radar_score")
        game_profile["notes_radar_" + ["single", "double"][style]] = [
            int(x) for x in score.text.split(" ")
        ]

    achievements = request_info["root"][0].find("achievements")
    if achievements is not None:
        for k in [
            "last_weekly",
            "pack_comp",
            "pack_flg",
            "pack_id",
            "play_pack",
            "visit_flg",
            "weekly_num",
        ]:
            game_profile["achievements_" + k] = int(achievements.attrib[k])

        trophy = achievements.find("trophy")
        if trophy is not None:
            game_profile["achievements_trophy"] = [
                int(x) for x in trophy.text.split(" ")
            ]

    grade = request_info["root"][0].find("grade")
    if grade is not None:
        grade_values = []
        for g in grade.findall("g"):
            grade_values.append([int(x) for x in g.text.split(" ")])

        profile["grade_single"] = int(grade.attrib["sgid"])
        profile["grade_double"] = int(grade.attrib["dgid"])
        profile["grade_values"] = grade_values

    deller_amount = game_profile.get("deller", 0)
    deller = request_info["root"][0].find("deller")
    if deller is not None:
        deller_amount = int(deller.attrib["deller"])
    game_profile["deller"] = deller_amount

    language = request_info["root"][0].find("language_setting")
    if language is not None:
        language_value = int(language.attrib["language"])
        game_profile["language_setting"] = language_value

    game_profile["spnum"] = game_profile.get("spnum", 0) + (1 if clt == 0 else 0)
    game_profile["dpnum"] = game_profile.get("dpnum", 0) + (1 if clt == 1 else 0)

    if request_info["model"] == "TDJ":
        game_profile["lightning_play_data_spnum"] = game_profile.get(
            "lightning_play_data_spnum", 0
        ) + (1 if clt == 0 else 0)
        game_profile["lightning_play_data_dpnum"] = game_profile.get(
            "lightning_play_data_dpnum", 0
        ) + (1 if clt == 1 else 0)

    profile["version"][str(game_version)] = game_profile

    get_db().table("iidx_profile").upsert(profile, where("card") == cid)

    response = E.response(E.IIDX30pc(iidxid=xid, cltype=clt))

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/visit")
async def iidx30pc_visit(request: Request):
    request_info = await core_process_request(request)

    response = E.response(
        E.IIDX30pc(
            aflg=1,
            anum=1,
            pflg=1,
            pnum=1,
            sflg=1,
            snum=1,
        )
    )

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/reg")
async def iidx30pc_reg(request: Request):
    request_info = await core_process_request(request)
    game_version = request_info["game_version"]

    cid = request_info["root"][0].attrib["cid"]
    name = request_info["root"][0].attrib["name"]
    pid = request_info["root"][0].attrib["pid"]

    db = get_db().table("iidx_profile")
    all_profiles_for_card = db.get(Query().card == cid)

    if all_profiles_for_card is None:
        all_profiles_for_card = {"card": cid, "version": {}}

    if "iidx_id" not in all_profiles_for_card:
        iidx_id = random.randint(10000000, 99999999)
        all_profiles_for_card["iidx_id"] = iidx_id

    all_profiles_for_card["version"][str(game_version)] = {
        "game_version": game_version,
        "djname": name,
        "region": int(pid),
        "head": 0,
        "hair": 0,
        "face": 0,
        "hand": 0,
        "body": 0,
        "frame": 0,
        "turntable": 0,
        "explosion": 0,
        "bgm": 0,
        "folder_mask": 0,
        "sudden": 0,
        "judge_pos": 0,
        "categoryvoice": 0,
        "note": 0,
        "fullcombo": 0,
        "keybeam": 0,
        "judgestring": 0,
        "soundpreview": 0,
        "grapharea": 0,
        "effector_lock": 0,
        "effector_type": 0,
        "explosion_size": 0,
        "alternate_hcn": 0,
        "kokokara_start": 0,
        "d_auto_adjust": 0,
        "d_auto_scrach": 0,
        "d_camera_layout": 0,
        "d_disp_judge": 0,
        "d_exscore": 0,
        "d_gauge_disp": 0,
        "d_ghost_score": 0,
        "d_gno": 0,
        "d_graph_score": 0,
        "d_gtype": 0,
        "d_hispeed": 0.000000,
        "d_judge": 0,
        "d_judgeAdj": 0,
        "d_lane_brignt": 0,
        "d_liflen": 0,
        "d_notes": 0.000000,
        "d_opstyle": 0,
        "d_pace": 0,
        "d_sdlen": 0,
        "d_sdtype": 0,
        "d_sorttype": 0,
        "d_sub_gno": 0,
        "d_timing": 0,
        "d_timing_split": 0,
        "d_tsujigiri_disp": 0,
        "d_tune": 0,
        "d_visualization": 0,
        "dach": 0,
        "dp_opt": 0,
        "dp_opt2": 0,
        "dpnum": 0,
        "gpos": 0,
        "mode": 0,
        "ngrade": 0,
        "pmode": 0,
        "rtype": 0,
        "s_auto_adjust": 0,
        "s_auto_scrach": 0,
        "s_camera_layout": 0,
        "s_disp_judge": 0,
        "s_exscore": 0,
        "s_gauge_disp": 0,
        "s_ghost_score": 0,
        "s_gno": 0,
        "s_graph_score": 0,
        "s_gtype": 0,
        "s_hispeed": 0.000000,
        "s_judge": 0,
        "s_judgeAdj": 0,
        "s_lane_brignt": 0,
        "s_liflen": 0,
        "s_notes": 0.000000,
        "s_opstyle": 0,
        "s_pace": 0,
        "s_sdlen": 0,
        "s_sdtype": 0,
        "s_sorttype": 0,
        "s_sub_gno": 0,
        "s_timing": 0,
        "s_timing_split": 0,
        "s_tsujigiri_disp": 0,
        "s_tune": 0,
        "s_visualization": 0,
        "sach": 0,
        "sp_opt": 0,
        "spnum": 0,
        "deller": 0,
        # Step up mode
        "stepup_dp_fluctuation": 0,
        "stepup_dp_level": 0,
        "stepup_dp_mplay": 0,
        "stepup_enemy_damage": 0,
        "stepup_enemy_defeat_flg": 0,
        "stepup_mission_clear_num": 0,
        "stepup_progress": 0,
        "stepup_sp_fluctuation": 0,
        "stepup_sp_level": 0,
        "stepup_sp_mplay": 0,
        "stepup_tips_read_list": 0,
        "stepup_total_point": 0,
        "stepup_is_track_ticket": 0,
        # DJ Rank
        "dj_rank_single_rank": [0] * 15,
        "dj_rank_double_rank": [0] * 15,
        "dj_rank_single_point": [0] * 15,
        "dj_rank_double_point": [0] * 15,
        # Notes Radar
        "notes_radar_single": [0] * 6,
        "notes_radar_double": [0] * 6,
        # Grades
        "grade_single": -1,
        "grade_double": -1,
        "grade_values": [],
        # Achievements
        "achievements_trophy": [0] * 160,
        "achievements_last_weekly": 0,
        "achievements_pack_comp": 0,
        "achievements_pack_flg": 0,
        "achievements_pack_id": 0,
        "achievements_play_pack": 0,
        "achievements_visit_flg": 0,
        "achievements_weekly_num": 0,
        # Other
        "language_setting": 0,
        "movie_agreement": 0,
        "lightning_play_data_spnum": 0,
        "lightning_play_data_dpnum": 0,
        # Lightning model settings
        "lightning_setting_slider": [0] * 7,
        "lightning_setting_light": [1] * 10,
        "lightning_setting_concentration": 0,
        "lightning_setting_headphone_vol": 0,
        "lightning_setting_resistance_sp_left": 0,
        "lightning_setting_resistance_sp_right": 0,
        "lightning_setting_resistance_dp_left": 0,
        "lightning_setting_resistance_dp_right": 0,
        "lightning_setting_skin_0": 0,
        "lightning_setting_flg_skin_0": 0,
        # Web UI/Other options
        "_show_category_grade": 0,
        "_show_category_status": 1,
        "_show_category_difficulty": 1,
        "_show_category_alphabet": 1,
        "_show_category_rival_play": 0,
        "_show_category_rival_winlose": 1,
        "_show_category_all_rival_play": 0,
        "_show_category_arena_winlose": 1,
        "_show_rival_shop_info": 1,
        "_hide_play_count": 0,
        "_show_score_graph_cutin": 1,
        "_hide_iidx_id": 0,
        "_classic_hispeed": 0,
        "_beginner_option_swap": 1,
        "_show_lamps_as_no_play_in_arena": 0,
        "skin_customize_flag_frame": 0,
        "skin_customize_flag_bgm": 0,
        "skin_customize_flag_lane": 0,
        # Rivals
        "sp_rival_1_iidx_id": 0,
        "sp_rival_2_iidx_id": 0,
        "sp_rival_3_iidx_id": 0,
        "sp_rival_4_iidx_id": 0,
        "sp_rival_5_iidx_id": 0,
        "sp_rival_6_iidx_id": 0,
        "dp_rival_1_iidx_id": 0,
        "dp_rival_2_iidx_id": 0,
        "dp_rival_3_iidx_id": 0,
        "dp_rival_4_iidx_id": 0,
        "dp_rival_5_iidx_id": 0,
        "dp_rival_6_iidx_id": 0,
    }
    db.upsert(all_profiles_for_card, where("card") == cid)

    card, card_split = get_id_from_profile(cid)

    response = E.response(E.IIDX30pc(id=card, id_str=card_split))

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/getLaneGachaTicket")
async def iidx30pc_getlanegachaticket(request: Request):
    request_info = await core_process_request(request)

    response = E.response(
        E.IIDX30pc(
            *[
                E.ticket(
                    ticket_id=i,
                    arrange_id=i,
                    expire_date=0,
                )
                for i in range(5040)
            ],
            E.setting(
                sp=-1,
                dp_left=-1,
                dp_right=-1,
            ),
            E.info(
                last_page=1,
            ),
            E.free(
                num=10,
            ),
            E.favorite(
                arrange=1234567,
            ),
        )
    )

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/consumeLaneGachaTicket")
async def iidx30pc_consumelanegachaticket(request: Request):
    request_info = await core_process_request(request)

    response = E.response(E.IIDX30pc())

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/drawLaneGacha")
async def iidx30pc_drawlanegacha(request: Request):
    request_info = await core_process_request(request)

    response = E.response(
        E.IIDX30pc(
            E.ticket(
                ticket_id=1,
                arrange_id=1,
                expire_date=0,
            ),
            E.session(session_id=1),
            status=0,
        )
    )

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/eaappliresult")
async def iidx30pc_eaappliresult(request: Request):
    request_info = await core_process_request(request)

    response = E.response(E.IIDX30pc())

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)

@router.post("/{gameinfo}/IIDX30pc/logout")
async def iidx30pc_logout(request: Request):
    request_info = await core_process_request(request)

    response = E.response(E.IIDX30pc())

    response_body, response_headers = await core_prepare_response(request, response)
    return Response(content=response_body, headers=response_headers)