import ipaddress

from django import forms
from django.core.validators import RegexValidator

from isucon.portal.authentication.decorators import is_registration_available
from isucon.portal.authentication.models import Team, User
from isucon.portal.contest.models import Server

def global_ip_validator(value):
    try:
        address = ipaddress.ip_address(value)
    except ValueError:
        raise forms.ValidationError("IPv4アドレスではありません")
    if address.is_global:
        return value
    raise forms.ValidationError("グローバルIPではありません")

def private_ip_validator(value):
    try:
        address = ipaddress.ip_address(value)
    except ValueError:
        raise forms.ValidationError("IPv4アドレスではありません")
    if address.is_private:
        return value
    raise forms.ValidationError("プライベートIPではありません")

class TeamForm(forms.ModelForm):
    class Meta:
        model = Team
        fields = ("name", "participate_at", )

    participate_at = forms.DateField(
        label="参加日選択",
        input_formats=["%Y-%m-%d"],
        widget=forms.Select(choices=Team.PARTICIPATE_AT_CHOICES),
        required=True,
    )

    def __init__(self, *args, **kwargs):
        self.is_registration_available = is_registration_available()

        super().__init__(*args, **kwargs)

        if not self.is_registration_available:
            self.fields['name'].widget.attrs['readonly'] = True
            self.fields['name'].widget.attrs['class'] = 'is-static'

    def clean_name(self):
        if not self.is_registration_available:
            return self.instance.name
        return self.cleaned_data.get("name", "")

    def clean_participate_at(self):
        if not self.is_registration_available:
            return self.instance.participate_at
        return self.cleaned_data.get("participate_at", None)

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ["display_name", ]

class UserIconForm(forms.Form):
    icon = forms.ImageField(label="アイコン", required=True)

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop("user")
        super().__init__(*args, **kwargs)

    def save(self):
        self.user.icon = self.cleaned_data['icon']
        self.user.save()
        return self.user

class ServerTargetForm(forms.Form):

    target = forms.IntegerField(label="対象サーバID")

    def __init__(self, *args, **kwargs):
        self.team = kwargs.pop("team")
        super().__init__(*args, **kwargs)

    def clean_target(self):
        data = self.cleaned_data['target']

        if not Server.objects.filter(id=data, team=self.team).exists():
            raise forms.ValidationError("Invalid Server ID")

        return data

    def save(self):
        Server.objects.filter(team=self.team).update(is_bench_target=False)
        Server.objects.filter(id=self.cleaned_data["target"], team=self.team).update(is_bench_target=True)

class ServerAddForm(forms.ModelForm):
    class Meta:
        model = Server
        fields = ("hostname", "global_ip", "private_ip")

    global_ip = forms.CharField(required=True, validators=[global_ip_validator], )
    private_ip = forms.CharField(required=False, validators=[private_ip_validator], )

    def __init__(self, *args, **kwargs):
        self.team = kwargs.pop("team")
        super().__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super().clean()

        if Server.objects.filter(team=self.team).count() >= 3:
            raise forms.ValidationError("すでに3台登録されています")

        return cleaned_data

    def clean_hostname(self):
        hostname = self.cleaned_data.get("hostname", "")

        if len(hostname) == 0:
            raise forms.ValidationError("ホスト名が空です")

        if Server.objects.filter(team=self.team, hostname=hostname).exists():
            raise forms.ValidationError("同一ホスト名のサーバが登録済みです")

        return hostname

    def save(self):
        instance = super().save(commit=False)
        instance.team = self.team
        instance.save()
        return instance


import cv2
img = cv2.imread("shape.jpg")
imgray=cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)

# to find the thresh max and min valuse use trackbar

_, thresh = cv2.threshold(imgray, 226, 255, cv2.THRESH_BINARY)

#For finding the Contours of all image

contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#print(len(contours))

# for shape edags
for i in contours:
    a = cv2.approxPolyDP(i, 0.01 * cv2.arcLength(i, True), True)
    cv2.drawContours(img, [a], 0, (0, 255, 0), 2)
    x=a.ravel()[0]
    y=a.ravel()[1]-10
    if len(a) == 3:
        cv2.putText(img,"Triange",(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,5))
    elif len(a) == 4:
        x1,y1,w,h=cv2.boundingRect(a)
        aspectRatio=float(w)/h
        if aspectRatio >=1 and  aspectRatio<=1:
            print("")
            cv2.putText(img,"square",(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,5))
        else:
            cv2.putText(img, "square", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 5))
    elif len(a) == 5:
        cv2.putText(img,"pentgon",(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,5))
    else:
        cv2.putText(img,"circle",(x,y),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,5))

cv2.imshow("frame", img)
cv2.waitKey(0)


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

data=pd.read_csv('creditcard.csv')
data=data.sample(frac=0.1,random_state=1)
print(data.columns)
print(data.describe())

#Data Analysis
data.hist(figsize=(20,20))
plt.show()

Fraud=data[data['Class']==1]
Valid=data[data['Class']==0]
print(len(Fraud))
print(len(Valid))
outlier_fraction=len(Fraud)/len(Valid)
print(len(Fraud)/len(Valid))

#Correlation Matrix
corrmat=data.corr()
fig=plt.figure(figsize=(12,9))
sns.heatmap(corrmat,vmax=.8,square=True)
plt.show()

#Data Preprocessing
columns=data.columns.tolist()
columns=[c for c in columns if c not in ['Class']]
target='Class'
X=data[columns]
y=data[target]

#Building Models
from sklearn.metrics import classification_report, accuracy_score
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor

#define random state
state=1
#define outlier detection methods
classifiers={
        'Isolation Forest': IsolationForest(max_samples=len(X),contamination=outlier_fraction,
                                            random_state=state),
        'Local Outlier Factor':LocalOutlierFactor(n_neighbors=20,contamination=outlier_fraction)
}

#fit the model
n_outliers=len(Fraud)

for i,(clf_name,clf) in enumerate(classifiers.items()):
    #fit the data and tag outliers
    if clf_name=='Local Outlier Factor':
        y_pred=clf.fit_predict(X)
        scores_pred=clf.negative_outlier_factor_
    else:
        clf.fit(X)
        scores_pred=clf.decision_function(X)
        y_pred=clf.predict(X)

    #Reshape prediction values 0 for inliers(valid) and 1 for outliers(fraud)
    y_pred[y_pred==1]=0
    y_pred[y_pred==-1]=1
    n_errors=(y_pred!=y).sum()
    #Run classification metrics
    print('{}:{}'.format(clf_name,n_errors))
    print(accuracy_score(y,y_pred))
    print(classification_report(y,y_pred))


from django.utils import timezone
from rest_framework.test import APITestCase
from vulnman.core.test import VulnmanAPITestCaseMixin
from apps.projects import models

class ProjectAPITokenTestCase(APITestCase, VulnmanAPITestCaseMixin):
    def setUp(self):
        self.init_mixin()

    def test_token_expired(self):
        date_valid = timezone.now() + timezone.timedelta(days=3)
        token = self.create_instance(models.ProjectAPIToken, project=self.project1, date_valid=date_valid)
        url = self.get_url("api:v1:assets:host-list")
        response = self.client.get(url)
        self.assertEqual(response.status_code, 401)
        response = self.client.get(url, HTTP_AUTHORIZATION="Token %s" % token.key)
        self.assertEqual(response.status_code, 200)
        date_valid = timezone.now() - timezone.timedelta(days=3)
        token = self.create_instance(models.ProjectAPIToken, project=self.project1, date_valid=date_valid)
        response = self.client.get(url, HTTP_AUTHORIZATION="Token %s" % token.key)
        self.assertEqual(response.status_code, 401)
        self.assertEqual("expired" in response.json().get("detail"), True)


import sys
import time
import datetime

class Counter:

    def __init__(self, counter, total_iterations, show_every=1):

        self.counter = counter
        self.current_iteration = 0
        self.total_iterations = int(total_iterations)
        self.start_time = time.time()
        self.show = 0
        self.show_every = int(show_every)

    def update(self, message=''):

        self.current_iteration += 1
        self.show += 1.0 / self.show_every

        out_of = '{0}{1} / {2} {3}'.format(' ' * (len(str(self.total_iterations)) - len(str(self.current_iteration))),
                                           str(self.current_iteration), str(self.total_iterations), message)

        delta_time = time.time() - self.start_time

        time_left = str(datetime.timedelta(
            seconds=int((self.total_iterations - self.current_iteration) * delta_time / self.current_iteration)))

        total_time = str(datetime.timedelta(seconds=int(delta_time)))

        if int(self.show):

            sys.stdout.write('\r\033[K')
            sys.stdout.write('{0}{1}: {2}   time left: {3}   total time: {4}'.format(
                self.counter, '.' * (15 - len(self.counter)), out_of, time_left, total_time))
            sys.stdout.flush()

            self.show = 0

        if self.current_iteration == self.total_iterations and self.total_iterations > 1:

            print('')


import sys
import re
import os
import enum
import binascii
from ctypes import *
from hashlib import md5

import LVblock
import LVdatatype
import LVxml as ET
from LVmisc import *

class FILE_FMT_TYPE(enum.Enum):
    NONE = 0
    Control = 1
    DLog = 2
    ClassLib = 3
    Project = 4
    Library = 5
    PackedProjLib = 6
    LLB = 7
    MenuPalette = 8
    TemplateControl = 9
    TemplateVI = 10
    Xcontrol = 11
    UsrIfaceResrc = 12
    RFilesService = 13
    RFilesOld = 14
    Subroutine = 15
    VI = 16
    Zero = 17

class RSRCHeader(RSRCStructure):
    _fields_ = [('rsrc_id1', c_ubyte * 6),		#0
                ('rsrc_fmtver', c_ushort),			#6
                ('rsrc_type', c_ubyte * 4),		#8 4-byte identifier of file type
                ('rsrc_id4', c_ubyte * 4),		#12
                ('rsrc_info_offset', c_uint32),	#16 Offset from beginning of the file to RSRC header before the Info part
                ('rsrc_info_size', c_uint32),	#20
                ('rsrc_data_offset', c_uint32),#24 Offset from beginning of the file to RSRC header before the Data part
                ('rsrc_data_size', c_uint32),	#28, sizeof is 32
    ]

    def __init__(self, po, fmtver=3):
        self.po = po
        self.rsrc_fmtver = fmtver
        if fmtver >= 3:
            self.rsrc_id1 = (c_ubyte * sizeof(self.rsrc_id1)).from_buffer_copy(b'RSRC\r\n')
        else:
            self.rsrc_id1 = (c_ubyte * sizeof(self.rsrc_id1)).from_buffer_copy(b'RSRC\0\0')
        self.rsrc_type = (c_ubyte * sizeof(self.rsrc_type)).from_buffer_copy(b'LVIN')
        self.rsrc_id4 = (c_ubyte * sizeof(self.rsrc_id4)).from_buffer_copy(b'LBVW')
        self.ftype = FILE_FMT_TYPE.NONE
        self.rsrc_data_offset = sizeof(self)
        self.starts = []

    def checkSanity(self):
        ret = True
        if bytes(self.rsrc_id1)  == b'RSRC\r\n':
            pass
        elif self.rsrc_fmtver <= 2 and bytes(self.rsrc_id1) == b'RSRC\0\0':
            pass
        else:
            if (self.po.verbose > 0):
                eprint("{:s}: RSRC Header field '{:s}' has unexpected value: {}".format(self.po.rsrc,'id1',bytes(self.rsrc_id1)))
            ret = False
        self.ftype = recognizeFileTypeFromRsrcType(self.rsrc_type)
        if self.ftype == FILE_FMT_TYPE.NONE:
            if (self.po.verbose > 0):
                eprint("{:s}: RSRC Header field '{:s}' has unexpected value: {}".format(self.po.rsrc,'rsrc_type',bytes(self.rsrc_type)))
            ret = False
        if bytes(self.rsrc_id4) == b'LBVW':
            pass
        elif self.ftype == FILE_FMT_TYPE.RFilesOld and bytes(self.rsrc_id4) == b'ResC':
            pass
        elif self.rsrc_fmtver <= 2 and bytes(self.rsrc_id4) == b'\0\0\0\0':
            # VI format from LV2.5
            pass
        else:
            if (self.po.verbose > 0):
                eprint("{:s}: RSRC Header field '{:s}' has unexpected value: {}".format(self.po.rsrc,'id4',bytes(self.rsrc_id4)))
            ret = False
        if self.rsrc_data_offset < sizeof(self):
            if (self.po.verbose > 0):
                eprint("{:s}: RSRC Header field '{:s}' has unexpected value: {}".format(self.po.rsrc,'rsrc_data_offset',self.rsrc_data_offset))
            ret = False
        return ret

class BlockInfoListHeader(RSRCStructure):
    _fields_ = [('dataset_int1', c_uint32),		#0
                ('dataset_int2', c_uint32),		#4
                ('dataset_int3', c_uint32),		#8
                ('blockinfo_offset', c_uint32),	#12
                ('blockinfo_size', c_uint32),	#16
    ]

    def __init__(self, po):
        self.po = po
        self.dataset_int3 = sizeof(RSRCHeader) # 32; assuming it's size
        self.blockinfo_offset = sizeof(RSRCHeader) + sizeof(self)
        pass

    def checkSanity(self):
        ret = True
        if self.dataset_int3 != sizeof(RSRCHeader):
            if (self.po.verbose > 0):
                eprint("{:s}: BlockInfo List Header field '{:s}' has outranged value: {:d}".format(self.po.rsrc,'dataset_int3',self.dataset_int3))
            ret = False
        if self.blockinfo_offset != sizeof(RSRCHeader) + sizeof(self):
            if (self.po.verbose > 0):
                eprint("{:s}: BlockInfo List Header field '{:s}' has outranged value: {:d}".format(self.po.rsrc,'blockinfo_offset',self.blockinfo_offset))
            ret = False
        return ret

class BlockInfoHeader(RSRCStructure):
    _fields_ = [('blockinfo_count', c_uint32),	#0
    ]

    def __init__(self, po):
        self.po = po
        pass

    def checkSanity(self):
        ret = True
        if self.blockinfo_count > 4096: # Arbitrary limit - hard to tell whether it makes sense
            if (self.po.verbose > 0):
                eprint("{:s}: BlockInfo Header field '{:s}' has outranged value: {:d}".format(self.po.rsrc,'blockinfo_count',self.blockinfo_count))
            ret = False
        return ret

def getRsrcTypeForFileType(ftype):
    """ Gives 4-byte file identifier from FILE_FMT_TYPE member
    """
    file_type = {
        FILE_FMT_TYPE.Control: b'LVCC',
        FILE_FMT_TYPE.DLog: b'LVDL',
        FILE_FMT_TYPE.ClassLib: b'CLIB',
        FILE_FMT_TYPE.Project: b'LVPJ',
        FILE_FMT_TYPE.Library: b'LIBR',
        FILE_FMT_TYPE.PackedProjLib: b'LIBP',
        FILE_FMT_TYPE.LLB: b'LVAR',
        FILE_FMT_TYPE.MenuPalette: b'LMNU',
        FILE_FMT_TYPE.RFilesService: b'LVRS',
        FILE_FMT_TYPE.RFilesOld: b'rsc ',
        FILE_FMT_TYPE.TemplateControl: b'sVCC',
        FILE_FMT_TYPE.TemplateVI: b'sVIN',
        FILE_FMT_TYPE.Xcontrol: b'LVXC',
        FILE_FMT_TYPE.UsrIfaceResrc: b'iUWl',
        FILE_FMT_TYPE.Subroutine: b'LVSB',
        FILE_FMT_TYPE.VI: b'LVIN',
        FILE_FMT_TYPE.Zero: b'\0\0\0\0',
    }.get(ftype, b'')
    return file_type

def recognizeFileTypeFromRsrcType(rsrc_type):
    """ Gives FILE_FMT_TYPE member from given 4-byte file identifier
    """
    rsrc_type_id = bytes(rsrc_type)
    for ftype in FILE_FMT_TYPE:
        curr_rsrc_type_id = getRsrcTypeForFileType(ftype)
        if len(curr_rsrc_type_id) > 0 and (curr_rsrc_type_id == rsrc_type_id):
            return ftype
    return FILE_FMT_TYPE.NONE

def getFileExtByType(ftype):
    """ Returns file extension associated with given FILE_FMT_TYPE member
    """
    fext = {
        FILE_FMT_TYPE.Control: 'ctl',
        FILE_FMT_TYPE.DLog: 'dlog',
        FILE_FMT_TYPE.ClassLib: 'lvclass',
        FILE_FMT_TYPE.Project: 'lvproj',
        FILE_FMT_TYPE.PackedProjLib: 'lvlibp',
        FILE_FMT_TYPE.Library: 'lvlib',
        FILE_FMT_TYPE.LLB: 'llb',
        FILE_FMT_TYPE.MenuPalette: 'mnu',
        FILE_FMT_TYPE.TemplateControl: 'ctt',
        FILE_FMT_TYPE.TemplateVI: 'vit',
        FILE_FMT_TYPE.Xcontrol: 'xctl',
        FILE_FMT_TYPE.UsrIfaceResrc: 'uir',
        FILE_FMT_TYPE.Subroutine: 'lsb',
        FILE_FMT_TYPE.VI: 'vi',
    }.get(ftype, 'rsrc')
    return fext

def getExistingRSRCFileWithBase(filebase):
    """ Returns file extension associated with given FILE_FMT_TYPE member
    """
    for ftype in FILE_FMT_TYPE:
        fext = getFileExtByType(ftype)
        fname = filebase + '.' + fext
        if os.path.isfile(fname):
            return fname
    return ""

class VI():
    def __init__(self, po, rsrc_fh=None, xml_root=None, text_encoding='utf-8'):
        self.rsrc_fh = None
        self.src_fname = ""
        self.xml_root = None
        self.po = po
        self.rsrc_headers = []
        self.fmtver = 3
        self.ftype = FILE_FMT_TYPE.NONE
        self.textEncoding = text_encoding
        self.blocks = None
        self.rsrc_map = []
        self.order_names = None

        if rsrc_fh is not None:
            self.dataSource = "rsrc"
            self.readRSRC(rsrc_fh)
        elif xml_root is not None:
            self.dataSource = "xml"
            self.readXML(xml_root, po.xml)
        else:
            self.dataSource = "new"

    def readRSRCList(self, fh):
        """ Read all RSRC headers from input file and check their sanity.
            After this function, `self.rsrc_headers` is filled with a list of RSRC Headers.
        """
        rsrc_headers = []
        curr_rsrc_pos = -1
        next_rsrc_pos = 0
        while curr_rsrc_pos != next_rsrc_pos:
            curr_rsrc_pos = next_rsrc_pos
            fh.seek(curr_rsrc_pos)
            rsrchead = RSRCHeader(self.po)
            if fh.readinto(rsrchead) != sizeof(rsrchead):
                raise EOFError("Could not read RSRC {:d} Header.".format(len(rsrc_headers)))
            if self.po.print_map == "RSRC":
                self.rsrc_map.append( (fh.tell(), sizeof(rsrchead), \
                  "{}[{}]".format(type(rsrchead).__name__,len(rsrc_headers)),) )
            if (self.po.verbose > 2):
                print(rsrchead)
            if not rsrchead.checkSanity():
                raise IOError("RSRC {:d} Header sanity check failed.".format(len(rsrc_headers)))
            # The last header has offset equal to its start
            if rsrchead.rsrc_info_offset >= curr_rsrc_pos:
                next_rsrc_pos = rsrchead.rsrc_info_offset
            else:
                raise IOError("Invalid position of next item after parsing RSRC {:d} Header: {:d}".format(len(rsrc_headers),rsrchead.rsrc_info_offset))
            rsrc_headers.append(rsrchead)
        self.rsrc_headers = rsrc_headers
        return (len(rsrc_headers) > 0)

    def readRSRCBlockInfo(self, fh):
        """ Read all Block-Infos from the input file.
            The Block-Infos are within last RSRC inside the file.
            This function requires `self.rsrc_headers` to be filled.
            The function returns a list of Block Headers.
        """
        blkinf_rsrchead = self.rsrc_headers[-1]
        # We expect two rsrc_headers in the RSRC file
        # Format version and file type should be identical in both headers
        self.fmtver = blkinf_rsrchead.rsrc_fmtver
        self.ftype = blkinf_rsrchead.ftype

        # Set file position just after Block-Infos RSRC header
        fh.seek(blkinf_rsrchead.rsrc_info_offset + sizeof(blkinf_rsrchead))

        # Read Block-Infos List Header located after last RSRC header
        binflsthead = BlockInfoListHeader(self.po)
        if fh.readinto(binflsthead) != sizeof(binflsthead):
            raise EOFError("Could not read BlockInfoList header.")
        if self.po.print_map == "RSRC":
            self.rsrc_map.append( (fh.tell(), sizeof(binflsthead), \
              "{}".format(type(binflsthead).__name__),) )
        if (self.po.verbose > 2):
            print(binflsthead)
        if not binflsthead.checkSanity():
            raise IOError("BlockInfoList Header sanity check failed.")
        self.binflsthead = binflsthead

        fh.seek(blkinf_rsrchead.rsrc_info_offset + binflsthead.blockinfo_offset)

        binfhead = BlockInfoHeader(self.po)
        if fh.readinto(binfhead) != sizeof(binfhead):
            raise EOFError("Could not read BlockInfo header.")
        if self.po.print_map == "RSRC":
            self.rsrc_map.append( (fh.tell(), sizeof(binfhead),
              "{}".format(type(binfhead).__name__),) )
        if not binfhead.checkSanity():
            raise IOError("BlockInfo Header sanity check failed.")
        if (self.po.verbose > 2):
            print(binfhead)

        tot_blockinfo_count = binfhead.blockinfo_count + 1

        # Read Block Headers
        block_headers = []
        for i in range(0, tot_blockinfo_count):
            block_head = LVblock.BlockHeader(self.po)
            if fh.readinto(block_head) != sizeof(block_head):
                raise EOFError("Could not read BlockInfo header.")
            if self.po.print_map == "RSRC":
                pretty_ident = getPrettyStrFromRsrcType(block_head.ident)
                self.rsrc_map.append( (fh.tell(), sizeof(block_head), \
                  "{}[{}]".format(type(block_head).__name__,pretty_ident),) )

            if (self.po.verbose > 2):
                print(block_head)
            if not block_head.checkSanity():
                raise IOError("Block Header sanity check failed.")
            #t['Count'] = reader.readUInt32() + 1
            #t['Offset'] = blkinf_rsrchead.rsrc_info_offset + binflsthead.blockinfo_offset + reader.readUInt32()
            block_headers.append(block_head)

        if self.po.print_map == "RSRC":
            self.rsrc_map.append( (fh.tell(), sizeof(BlockInfoHeader)+tot_blockinfo_count*sizeof(LVblock.BlockHeader), \
              "BlockInfo",))

        return block_headers

    def readRSRCBlockData(self, fh, block_headers):
        """ Read data sections for all Blocks from the input file.
            This function requires `block_headers` to be passed.
            After this function, `self.blocks` is filled.
        """
        # Create Array of Block; use classes defined within LVblock namespace to read data
        # specific to given block type; when block ident is unrecognized, create generic block
        blocks_arr = []
        for i, block_head in enumerate(block_headers):
            pretty_ident = getPrettyStrFromRsrcType(block_head.ident)
            bfactory = getattr(LVblock, pretty_ident, None)
            # Block may depend on some other informational blocks (ie. version info)
            # so give each block reference to the vi object
            if isinstance(bfactory, type):
                if (self.po.verbose > 1):
                    print("{:s}: Block '{:s}' index {:d} recognized".format(self.src_fname,pretty_ident,i))
                block = bfactory(self, self.po)
            else:
                block = LVblock.Block(self, self.po)
            block.initWithRSRCEarly(block_head)
            blocks_arr.append(block)

        # Create Array of Block Data
        blocks = {}
        for i, block in enumerate(blocks_arr):
            blocks[block.ident] = block
        self.blocks = blocks

        # Late part of initialization, which requires all blocks to be already present
        for block in self.blocks.values():
            block.initWithRSRCLate()

        # Now when everything is ready, parse the blocks data
        for block in self.blocks.values():
            block.parseData()

        self.rememberRSRCNamesOrder()

        # Do final integrations which establish dependencies betweebn blocks
        for block in self.blocks.values():
            block.integrateData()

        return (len(blocks) > 0)

    def readRSRC(self, fh):
        self.rsrc_fh = fh
        self.src_fname = fh.name
        self.rsrc_map = []
        self.readRSRCList(fh)
        block_headers = self.readRSRCBlockInfo(fh)
        self.readRSRCBlockData(fh, block_headers)
        self.checkSanity()
        pass

    def forceCompleteReadRSRC(self):
        """ Ensured read of all data possibly needed from input file

        This function can be called after readRSRC(). After this call,
        any further calls will not result in accessing the input RSRC
        file, as all data will be loaded to memory.
        """
        for block in self.blocks.values():
            block.readRawDataSections(section_count=0xffffffff)
        pass

    def rememberRSRCNamesOrder(self):
        """ Remembers information on section names order, if it is needed

        Check if order of section names matches order of section headers.
        If it does not, set property with the unexpected order.
        """
        ordered_blocks = self.getBlocksSaveOrder()
        # Prepare list of sections sorted by name offset
        blockNamesOrder = {}
        for block in ordered_blocks:
            for snum, section in block.enumerateRSRCSectionsWithNames():
                blockNamesOrder[section.start.name_offset] = (block.ident,snum,)
        blockNamesOrder = [itm[1] for itm in sorted(blockNamesOrder.items())]
        # Use that list to check whether names are in order
        blockNamesSorted = True
        i = 0
        for block in ordered_blocks:
            for snum, section in block.enumerateRSRCSectionsWithNames():
                if i >= len(blockNamesOrder): break
                if blockNamesOrder[i] != (block.ident,snum,):
                    blockNamesSorted = False
                    break
                i += 1
        if not blockNamesSorted:
            if (self.po.verbose > 0):
                print("{:s}: Names of sections are not ordered the same as actual sections".format(self.po.rsrc))
            self.order_names = blockNamesOrder
        else:
            self.order_names = None
        pass

    def readXMLBlockData(self):
        """ Read data sections for all Blocks from the input file.
            After this function, `self.blocks` is filled.
        """
        blocks_arr = []
        for i, block_elem in enumerate(self.xml_root):
            if block_elem.tag in ("SpecialOrder",): continue # Special tags, not treated as Blocks
            ident = block_elem.tag
            bfactory = getattr(LVblock, ident, None)
            # Block may depend on some other informational blocks (ie. version info)
            # so give each block reference to the vi object
            if isinstance(bfactory, type):
                if (self.po.verbose > 1):
                    print("{:s}: Block {:s} recognized".format(self.src_fname,ident))
                block = bfactory(self, self.po)
            else:
                block = LVblock.Block(self, self.po)
            block.initWithXMLEarly(block_elem)
            blocks_arr.append(block)
        self.blocks_arr = blocks_arr

        # Create Array of Block Data
        blocks = {}
        for i, block in enumerate(self.blocks_arr):
            blocks[block.ident] = block
        self.blocks = blocks

        # Late part of initialization, which requires all blocks to be already present
        for block in self.blocks.values():
            block.initWithXMLLate()

        # Now when everything is ready, parse the blocks data
        for block in self.blocks.values():
            block.parseData()

        # Do final integrations which establish dependencies betweebn blocks
        for block in self.blocks.values():
            block.integrateData()

        return (len(blocks) > 0)

    def readXMLOrder(self, elem):
        order_elem = elem.find("SpecialOrder")
        if order_elem is None:
            return
        blockNamesOrder = []
        names_elem = order_elem.find("Names")
        if names_elem is not None:
            for i, blkref_elem in enumerate(names_elem):
                ident = getRsrcTypeFromPrettyStr(blkref_elem.tag)
                snum = blkref_elem.get("Index")
                if snum is None:
                    snum = 0
                else:
                    snum = int(snum,0)
                blockNamesOrder.append( (ident,snum,) )
        if len(blockNamesOrder) > 0:
            self.order_names = blockNamesOrder
        else:
            self.order_names = None
        pass

    def readXML(self, xml_root, xml_fname):
        self.xml_root = xml_root
        self.src_fname = xml_fname
        if self.xml_root.tag != 'RSRC':
            raise AttributeError("Root tag of the XML is not 'RSRC'")

        fmtver_str = self.xml_root.get("FormatVersion")
        self.fmtver = int(fmtver_str, 0)
        pretty_type_str = self.xml_root.get("Type")
        if pretty_type_str is not None:
            rsrc_type_id = getRsrcTypeFromPrettyStr(pretty_type_str)
        else:
            pretty_type_str = self.xml_root.get("TypeHex")
            rsrc_type_id = bytes.fromhex(pretty_type_str)
        self.ftype = recognizeFileTypeFromRsrcType(rsrc_type_id)

        encoding_str = self.xml_root.get("Encoding")
        if encoding_str is not None:
            self.textEncoding = encoding_str

        self.rsrc_headers = []
        rsrchead = RSRCHeader(self.po, fmtver=self.fmtver)
        rsrchead.rsrc_type = (c_ubyte * sizeof(rsrchead.rsrc_type)).from_buffer_copy(rsrc_type_id)
        if self.fmtver <= 2 and self.ftype == FILE_FMT_TYPE.VI:
            rsrchead.rsrc_id4 = (c_ubyte * sizeof(rsrchead.rsrc_id4)).from_buffer_copy(b'\0\0\0\0')
        self.rsrc_headers.append(rsrchead)
        rsrchead = RSRCHeader(self.po, fmtver=self.fmtver)
        rsrchead.rsrc_type = (c_ubyte * sizeof(rsrchead.rsrc_type)).from_buffer_copy(rsrc_type_id)
        if self.fmtver <= 2 and self.ftype == FILE_FMT_TYPE.VI:
            rsrchead.rsrc_id4 = (c_ubyte * sizeof(rsrchead.rsrc_id4)).from_buffer_copy(b'\0\0\0\0')
        self.rsrc_headers.append(rsrchead)

        self.binflsthead = BlockInfoListHeader(self.po)

        dataset_int1 = self.xml_root.get("Int1")
        if dataset_int1 is not None:
            self.binflsthead.dataset_int1 = int(dataset_int1, 0)
        dataset_int2 = self.xml_root.get("Int2")
        if dataset_int2 is not None:
            self.binflsthead.dataset_int2 = int(dataset_int2, 0)

        self.readXMLBlockData()
        self.readXMLOrder(self.xml_root)
        self.checkSanity()
        pass

    def updateRSRCData(self):
        """ Updates RAW data stored in each block to changes in properties
        """
        for block in self.blocks.values():
            block.updateData()

    @staticmethod
    def blkrefCountSameIdent(blkref_list, idx):
        if idx >= len(blkref_list):
            return 0
        firstref = blkref_list[idx]
        count = 1
        for blkref in blkref_list[idx+1:]:
            if blkref[0] != firstref[0]:
                break
            count += 1
        return count

    @staticmethod
    def blkrefSortBlocks(shuffled_blocks, sorted_blkref_list):
        """ Gives list of blocks sorted to match given order of references
        """
        bridx = 0
        blocks_cache = []
        sorted_blocks = []
        for block in shuffled_blocks:
            # If we're beyond the list to sort, just add in current order
            if bridx >= len(sorted_blkref_list):
                blocks_cache.append(block)
                continue
            blkref = sorted_blkref_list[bridx]
            # If we're on a block we have in cache, add it from cache
            while True:
                cached_block = next((blk for blk in blocks_cache if blk.ident == blkref[0]), None)
                if cached_block is None:
                    break
                blocks_cache.remove(cached_block)
                sorted_blocks.append(cached_block)
                bridx += VI.blkrefCountSameIdent(sorted_blkref_list, bridx)
                if bridx >= len(sorted_blkref_list):
                    break
                blkref = sorted_blkref_list[bridx]
            if bridx >= len(sorted_blkref_list): # In case we moved beyond known ordering
                blocks_cache.append(block)
                continue
            # If we're on the block expected by sorting, add it now
            if block.ident == blkref[0]:
                sorted_blocks.append(block)
                bridx += VI.blkrefCountSameIdent(sorted_blkref_list, bridx)
                continue
            # If the current block is expected later in sorting, add it to cache
            if block.ident in [ blkref[0] for blkref in sorted_blkref_list[bridx:] ]:
                blocks_cache.append(block)
                continue
            # If we're here, the current block is not present in sorting list
            if True:
                sorted_blocks.append(block)
                bridx += VI.blkrefCountSameIdent(sorted_blkref_list, bridx)
                continue
        sorted_blocks.extend(blocks_cache)
        return sorted_blocks

    def saveRSRCData(self, fh):
        ver = self.getFileVersion()
        # Write header, though it is not completely filled yet
        rsrchead = self.rsrc_headers[0]
        fh.write((c_ubyte * sizeof(rsrchead)).from_buffer_copy(rsrchead))

        # Prepare list of blocks; this sets blocks order which we will use
        all_blocks = self.getBlocksSaveOrder()

        # Also create mutable array which will become the names block
        section_names = bytearray()

        # First, let's store names section in proper order
        if True:
            if self.order_names is None:
                all_blocks_for_names = all_blocks
                for block in all_blocks_for_names:
                    block.saveRSRCNames(section_names)
            else:
                all_blocks_for_names = self.blkrefSortBlocks(all_blocks, self.order_names)
                for block in all_blocks_for_names:
                    sections_list = [ blkref[1] for blkref in self.order_names if block.ident == blkref[0] ]
                    block.saveRSRCNames(section_names, order_list=sections_list)

        if isGreaterOrEqVersion(ver, 7,0,0):
            # The same order is used for both data and the following header blocks
            for block in all_blocks:
                if (self.po.verbose > 0):
                    print("{}: Writing RSRC block {} data".format(self.src_fname,block.ident))
                block.header.starts = block.saveRSRCData(fh)
        else:
            # Section headers are sorted normally, but section data is different - some sections are moved to end
            data_at_end_blocks = []
            for block in all_blocks:
                if block.ident in (b'LVSR',):
                    data_at_end_blocks.insert(0,block)
                    continue
                if block.ident in (b'BDPW',):
                    data_at_end_blocks.append(block)
                    continue
                if (self.po.verbose > 0):
                    print("{}: Writing RSRC block {} data".format(self.src_fname,block.ident))
                block.header.starts = block.saveRSRCData(fh)
            for block in data_at_end_blocks:
                if (self.po.verbose > 0):
                    print("{}: Writing RSRC block {} data at end".format(self.src_fname,block.ident))
                block.header.starts = block.saveRSRCData(fh)

        rsrchead.rsrc_info_offset = fh.tell()
        rsrchead.rsrc_data_size = rsrchead.rsrc_info_offset - rsrchead.rsrc_data_offset

        return all_blocks, section_names

    def saveRSRCInfo(self, fh, all_blocks, section_names):
        rsrchead = self.rsrc_headers[-1]
        fh.write((c_ubyte * sizeof(rsrchead)).from_buffer_copy(rsrchead))

        # Compute sizes and offsets within the block to be written
        start_offs = sizeof(BlockInfoHeader) + sum(sizeof(block.header) for block in all_blocks)
        for block in all_blocks:
            # the below means the same ase block_head.count = len(self.sections) - 1
            block.header.count = len(block.header.starts) - 1
            block.header.offset = start_offs
            start_offs += sum(sizeof(sect_start) for sect_start in block.header.starts)

        binflsthead = self.binflsthead
        binflsthead.blockinfo_size = binflsthead.blockinfo_offset + start_offs
        if (self.po.verbose > 2):
            print(binflsthead)
        fh.write((c_ubyte * sizeof(binflsthead)).from_buffer_copy(binflsthead))

        binfhead = BlockInfoHeader(self.po)
        binfhead.blockinfo_count = len(self.blocks) - 1
        fh.write((c_ubyte * sizeof(binfhead)).from_buffer_copy(binfhead))

        for block in all_blocks:
            if (self.po.verbose > 0):
                print("{}: Writing RSRC Info block {} header at 0x{:04X}".format(self.src_fname,bytes(block.header.ident),fh.tell()))
            if (self.po.verbose > 2):
                print(block.header)
            if not block.header.checkSanity():
                raise IOError("Block Header sanity check failed.")
            fh.write((c_ubyte * sizeof(block.header)).from_buffer_copy(block.header))

        for block in all_blocks:
            if (self.po.verbose > 0):
                print("{}: Writing RSRC Info block {} section starts at 0x{:04X}".format(self.src_fname,bytes(block.header.ident),fh.tell()))
            for s, sect_start in enumerate(block.header.starts):
                fh.write((c_ubyte * sizeof(sect_start)).from_buffer_copy(sect_start))

        # Section names as Pascal strings
        if (self.po.verbose > 0):
            print("{}: Writing RSRC Info section names at 0x{:04X}".format(self.src_fname,fh.tell()))
        fh.write(section_names)

        rsrchead.rsrc_info_offset = self.rsrc_headers[0].rsrc_info_offset
        rsrchead.rsrc_info_size = fh.tell() - rsrchead.rsrc_info_offset
        self.rsrc_headers[0].rsrc_info_size = rsrchead.rsrc_info_size
        rsrchead.rsrc_data_size = self.rsrc_headers[0].rsrc_data_size
        pass

    def resaveRSRCHeaders(self, fh):
        rsrchead = self.rsrc_headers[0]
        if (self.po.verbose > 2):
            print(rsrchead)
        fh.seek(0)
        fh.write((c_ubyte * sizeof(rsrchead)).from_buffer_copy(rsrchead))
        rsrchead = self.rsrc_headers[-1]
        if (self.po.verbose > 2):
            print(rsrchead)
        fh.seek(rsrchead.rsrc_info_offset)
        fh.write((c_ubyte * sizeof(rsrchead)).from_buffer_copy(rsrchead))
        pass

    def saveRSRC(self, fh):
        self.src_fname = fh.name
        self.updateRSRCData()
        all_blocks, section_names = self.saveRSRCData(fh)
        self.saveRSRCInfo(fh, all_blocks, section_names)
        self.resaveRSRCHeaders(fh)
        pass

    def exportXMLRoot(self):
        """ Creates root of the XML export tree
        """
        ver = self.getFileVersion()
        elem = ET.Element('RSRC')
        elem.set("FormatVersion", "{:d}".format(self.fmtver))
        rsrc_type_id = getRsrcTypeForFileType(self.ftype)
        if any((c < ord(' ') or c > ord('~')) for c in rsrc_type_id):
            elem.set("TypeHex", rsrc_type_id.hex())
        else:
            elem.set("Type", rsrc_type_id.decode('ascii'))
        elem.set("Encoding", self.textEncoding)

        if self.ftype == FILE_FMT_TYPE.LLB or isSmallerVersion(ver, 7,0,0):
            dataset_int1 = self.binflsthead.dataset_int1
        else:
            dataset_int1 = None
        if dataset_int1 is not None:
            elem.set("Int1", "0x{:08X}".format(dataset_int1))

        # The value is verified to be used in LV6.0.1, unused in LV8.6
        if self.ftype == FILE_FMT_TYPE.LLB or isSmallerVersion(ver, 7,0,0):
            dataset_int2 = self.binflsthead.dataset_int2
        else:
            dataset_int2 = None
        if dataset_int2 is not None:
            elem.set("Int2", "0x{:08X}".format(dataset_int2))

        return elem

    def exportXMLOrder(self, elem):
        if self.order_names is None:
            return
        order_elem = ET.SubElement(elem,"SpecialOrder")
        comment_elem = ET.Comment(" {:s} ".format("Provides information on how items were ordered in the RSRC file"))
        order_elem.append(comment_elem)
        if self.order_names is not None:
            subelem = ET.SubElement(order_elem,"Names")
            for blockref in self.order_names:
                pretty_ident = getPrettyStrFromRsrcType(blockref[0])
                blkref_elem = ET.Element(pretty_ident)
                blkref_elem.set("Index", str(blockref[1]))
                subelem.append(blkref_elem)
        pass

    def exportBinBlocksXMLTree(self):
        """ Export the file data into BIN files with XML glue
        """
        elem = self.exportXMLRoot()

        for ident, block in self.blocks.items():
            if (self.po.verbose > 0):
                print("{}: Writing BIN block {}".format(self.src_fname,ident))
            subelem = block.exportXMLTree(simple_bin=True)
            elem.append(subelem)

        ET.pretty_element_tree_heap(elem)
        return elem

    def exportXMLTree(self):
        """ Export the file data into XML tree
        """
        elem = self.exportXMLRoot()

        for ident, block in self.blocks.items():
            if (self.po.verbose > 0):
                print("{}: Writing block {}".format(self.src_fname,ident))
            subelem = block.exportXMLTree()
            elem.append(subelem)

        self.exportXMLOrder(elem)
        ET.pretty_element_tree_heap(elem)
        return elem

    def checkSanity(self):
        ret = True
        for ident, block in self.blocks.items():
            block.parseData()
            if not block.checkSanity():
                if (self.po.verbose > 0):
                    eprint("{:s}: Warning: Block {} sanity check failed!"\
                      .format(self.src_fname,ident))
                ret = False
        return ret

    def getBlockIdByBlockName(self, ident):
        for i in range(0, len(self.blockInfo)):
            if self.blockInfo[i]['BlockName'] == ident:
                return i
        return None

    def getPositionOfBlockInfoHeader(self):
        """ Gives file position at which BlockInfoHeader is located within the Info Resource

            The BlockInfoHeader is then followed by array of BlockHeader structs.
        """
        blkinf_rsrchead = self.rsrc_headers[-1]
        return blkinf_rsrchead.rsrc_info_offset + self.binflsthead.blockinfo_offset

    def getPositionOfBlockSectionStart(self):
        """ Gives file position at which BlockSectionStart structs are placed within the Info Resource

            Offsets to groups of BlockSectionStart elements are inside BlockHeader structs; this
            function can be used to validate them.
        """
        return self.getPositionOfBlockInfoHeader() + sizeof(BlockInfoHeader) + sizeof(LVblock.BlockHeader) * len(self.blocks)

    def getPositionOfBlockSectionNames(self):
        """ Gives file position at which Section Names are placed within the Info Resource
        """
        tot_sections_count = 0
        for block in self.blocks.values():
            tot_sections_count += len(block.sections)
        return self.getPositionOfBlockSectionStart() + sizeof(LVblock.BlockSectionStart) * tot_sections_count

    def getPositionOfBlockInfoEnd(self):
        """ Gives file position at which the Info Resource ends
        """
        blkinf_rsrchead = self.rsrc_headers[-1]
        return blkinf_rsrchead.rsrc_info_offset + blkinf_rsrchead.rsrc_info_size

    def consolidatedTDEnumerate(self, mainType=None, fullType=None):
        VCTP = self.get_or_raise('VCTP')
        typeList = VCTP.getContent()
        out_list = []
        for conn_idx, clientTD in enumerate(typeList):
            if mainType is not None and clientTD.nested.mainType() != mainType:
                continue
            if fullType is not None and clientTD.nested.fullType() != fullType:
                continue
            out_list.append( (len(out_list), conn_idx, clientTD.nested,) )
        return out_list

    def getHeapTD(self, heapTypeId):
        DTHP = self.get('DTHP')
        if DTHP is None:
            return None
        return DTHP.getHeapTD(heapTypeId)

    def setNewPassword(self, password_text=None, password_md5=None):
        """ Calculates password
        """
        BDPW = self.get_or_raise('BDPW')
        BDPW.parseData()
        BDPW.recalculateHash1(store=False) # this is needed to find salt
        if password_text == "":
            # If removing password, also remove protected flag
            LVSR = self.get('LVSR')
            if LVSR is not None:
                LVSR.parseData()
                LVSR.protected = False
                LVSR.updateSectionData()
        BDPW.setPassword(password_text=password_text, password_md5=password_md5, store=True)
        #BDPW.recalculateHash1(store=True) # called by updateSectionData()
        #BDPW.recalculateHash2(store=True) # called by updateSectionData()
        BDPW.updateSectionData()
        return BDPW

    def printRSRCMap(self):
        # BlockSectionStart elements are really independent; but let's put them into some parent
        # for clarity. After all, all versions of LV create these next to each other.
        parent_beg = 0xffffffff
        parent_end = 0x0
        for mapItem in self.rsrc_map:
            if re.match(r"BlockSectionStart\[.+\]", mapItem[2]):
                parent_beg = min(parent_beg, mapItem[0]-mapItem[1])
                parent_end = max(parent_end, mapItem[0])
        if parent_beg < parent_end:
            self.rsrc_map.append( (parent_end, parent_end-parent_beg, \
              "BlockSectionStarts",) )
        # Put section data into a parent as well
        parent_beg = 0xffffffff
        parent_end = 0x0
        for mapItem in self.rsrc_map:
            if re.match(r"BlockSectionData\[.+\]", mapItem[2]):
                parent_beg = min(parent_beg, mapItem[0]-mapItem[1])
                parent_end = max(parent_end, mapItem[0])
        if parent_beg < parent_end:
            self.rsrc_map.append( (parent_end, parent_end-parent_beg, \
              "BlockData",) )
        # Names block also should have one parent
        parent_beg = 0xffffffff
        parent_end = 0x0
        for mapItem in self.rsrc_map:
            if re.match(r"NameOf.+\[.+\]", mapItem[2]):
                parent_beg = min(parent_beg, mapItem[0]-mapItem[1])
                parent_end = max(parent_end, mapItem[0])
        if parent_beg < parent_end:
            self.rsrc_map.append( (parent_end, parent_end-parent_beg, \
              "NameStrings",) )
        parents = []
        rsrc_map_sorted = sorted(self.rsrc_map, key=lambda x: (x[0]-x[1], -x[1], len(x[2])))
        for mapItem in rsrc_map_sorted:
            while len(parents) > 0:
                parItem = parents[-1]
                if parItem[0] > mapItem[0]-mapItem[1]:
                    break
                parents.pop()
            parents.append(mapItem)
            print("{:08X}: {:>{}s}{:s} (size:{:d})".format(mapItem[0]-mapItem[1],"",2*(len(parents)-1),mapItem[2],mapItem[1]))
        pass

    def isLoaded(self):
        return (self.blocks is not None)

    def get(self, ident):
        if isinstance(ident, str):
            ident = getRsrcTypeFromPrettyStr(ident)
        if ident in self.blocks:
            return self.blocks[ident]
        return None

    def get_one_of(self, *identv):
        for ident in identv:
            if isinstance(ident, str):
                ident = getRsrcTypeFromPrettyStr(ident)
            if ident in self.blocks:
                return self.blocks[ident]
        return None

    def get_or_raise(self, ident):
        if isinstance(ident, str):
            ident = getRsrcTypeFromPrettyStr(ident)
        if ident in self.blocks:
            return self.blocks[ident]
        raise LookupError("Block {} not found in RSRC file.".format(ident))

    def get_one_of_or_raise(self, *identv):
        for ident in identv:
            if isinstance(ident, str):
                ident = getRsrcTypeFromPrettyStr(ident)
            if ident in self.blocks:
                return self.blocks[ident]
        raise LookupError("None of blocks {} found in RSRC file.".format(",".join(identv)))

    def getBlocksSaveOrder(self):
        """ Returns list of blocks in the order they should be saved
        """
        return self.blocks.values()

    def getFileVersion(self):
        """ Gets file version array from any existing version block
        """
        vers = self.get_one_of('LVSR', 'vers') # TODO add LVIN when its supported
        if vers is not None:
            ver = vers.getVersion()
        else:
            # No version found - return all fields zeroed out
            ver = decodeVersion(0x0)
        return ver


"""Provides the WXXMLParser class."""

import wx
from wx.lib.agw.floatspin import FloatSpin
from wx.lib.intctrl import IntCtrl
from .base import XMLParser

class DuplicateSizerError(Exception):
    """There is already a main sizer."""

class InvalidTagError(Exception):
    """Invalid tag found."""

class NoValueError(Exception):
    """No value was provided where one should be."""

class NoParent:
    """Used to specify a default panel should not be created."""

    def __str__(self):
        return 'No Parent'

    def __repr__(self):
        return str(self)

no_parent = NoParent()

class WXXMLParser(XMLParser):
    """Populate wx.Frame instances from XML."""

    def populate_from_root(self, root, frame, parent=no_parent):
        """
        Overrides the default populate_from_root to add wx-specific code. In
        particular the parent argument.

        If parent is no_parent, then parent will be frame.
        If parent is None, then a wx.Panel will be created with frame as its
        only argument.

        Any controls created will have parent as their first argument. As such,
        parent should end up as either frame, or a wx.Panel instance created
        with frame as the parent (parent=None).

        Each parse_* method should be prepared to take 4 arguments:
        node: The node to parse.
        frame: The frame to add controls to (when they have an attribute called
        name).
        parent: The parent to use for control creation.
        sizer: The sizer the created control should be added to.

        Everything else is the same.
        """
        sizer = None
        if parent is no_parent:
            parent = frame
        elif parent is None:
            parent = wx.Panel(frame)
        for node in root:
            res = self.parse_node(node, frame, parent, sizer)
            if isinstance(res, wx.Sizer):
                if sizer is not None:
                    raise DuplicateSizerError(
                        'Sizer %r is the second sizer (first was %r).' % (
                            res, sizer
                        )
                    )
                sizer = res
        if sizer is not None:
            parent.SetSizerAndFit(res)

    def get_flags(self, text, default=0):
        """Given a string like "te_rich2,te_password", return
        wx.TE_RICH2 | wx.TE_PASSWORD."""
        for entry in text.upper().split(','):
            entry = entry.strip()
            default |= getattr(wx, entry)
        return default

    def parse_node(self, node, frame, parent, sizer):
        """Parse a single node."""
        res = super().parse_node(node, frame, parent, sizer)
        a = node.attrib
        label = a.get('label', None)
        if label is not None:
            res.SetLabel(label)
        style = a.get('style', None)
        if style is not None:
            res.SetWindowStyle(self.get_flags(style))
        size = a.get('size', None)
        if size is not None:
            size = self.get_list(size)
            res.SetSize(size)
        if sizer is not None:
            proportion = int(a.get('sizer_proportion', '0'))
            flags = a.get('sizer_flag', 'grow')
            flags = self.get_flags(flags)
            sizer.Add(res, proportion, flags)
        binders = a.get('bind', None)
        if binders is not None:
            for binder in binders.split(','):
                event_name, func_name = binder.split(':')
                event = getattr(wx, f'EVT_{event_name.upper()}')
                func = getattr(self, func_name)
                res.Bind(event, func)
        return res

    def parse_title(self, node, frame, parent, sizer):
        """Add a title to frame."""
        frame.SetTitle(node.text)

    def parse_sizer(self, node, frame, parent, sizer):
        """Parse a sizer and all contained nodes."""
        flags = self.get_flags(node.attrib.get('orient', 'horizontal'))
        s = wx.BoxSizer(flags)
        for child in node:
            self.parse_node(child, frame, parent, s)
        return s

    def parse_label(self, node, frame, parent, sizer):
        """Create a label."""
        return wx.StaticText(parent, label=node.text)

    def parse_text(self, node, frame, parent, sizer):
        """Create a text control."""
        return wx.TextCtrl(parent, value=node.text or '')

    def parse_integer(self, node, frame, parent, sizer):
        """Create a control that accepts integers."""
        a = node.attrib
        min_value = a.get('min', None)
        if min_value is not None:
            min_value = int(min_value)
        max_value = a.get('max', None)
        if max_value is not None:
            max_value = int(max_value)
        limited = int(a.get('limited', 0))
        allow_none = int(a.get('allow_none', 0))
        allow_long = int(a.get('allow_long', 0))
        value = node.text
        if value is None:
            value = 0
        else:
            value = int(value)
        return IntCtrl(
            parent, min=min_value, max=max_value, limited=limited,
            allow_none=allow_none, allow_long=allow_long, value=value
        )

    def parse_float(self, node, frame, parent, sizer):
        """Return a float control."""
        if node.text is None:
            value = 0.0
        else:
            value = float(node.text)
        a = node.attrib
        min_value = a.get('min', None)
        if min_value is not None:
            min_value = float(min_value)
        max_value = a.get('max', None)
        if max_value is not None:
            max_value = float(max_value)
        increment = float(a.get('increment', 1.0))
        digits = int(a.get('digits', -1))
        return FloatSpin(
            parent, value=value, min_val=min_value, max_val=max_value,
            increment=increment, digits=digits
        )

    def parse_slider(self, node, frame, parent, sizer):
        """Return a slider control."""
        if node.text is None:
            value = 0
        else:
            value = int(node.text)
        a = node.attrib
        min_value = int(a.get('min', 0))
        max_value = int(a.get('max', 100))
        return wx.Slider(
            parent, value=value, minValue=min_value, maxValue=max_value
        )

    def parse_checkbox(self, node, frame, parent, sizer):
        """Return a checkbox."""
        cb = wx.CheckBox(parent)
        if node.text is not None:
            cb.SetValue(int(node.text))
        return cb

    def parse_button(self, node, frame, parent, sizer):
        """Returns a button."""
        a = node.attrib
        b = wx.Button(parent)
        default = int(a.get('default', 0))
        if default:
            b.SetDefault()
        return b

    def parse_choice(self, node, frame, parent, sizer):
        """Get a popup button."""
        choices = node.attrib.get('choices', None)
        if choices is None:
            choices = []
        else:
            choices = self.get_list(choices, function=str)
        choice = wx.Choice(parent, choices=choices)
        if node.text is not None:
            choice.SetSelection(int(node.text))
        return choice

    def parse_list(self, node, frame, parent, sizer):
        """Return a simple list box."""
        a = node.attrib
        choices = a.get('choices', None)
        if choices is None:
            choices = []
        else:
            choices = self.get_list(choices, function=str)
        b = wx.ListBox(parent, choices=choices)
        if node.text is not None:
            b.SetSelection(int(node.text))
        return b

    def parse_table(self, node, frame, parent, sizer):
        """Return a list control with columns."""
        # We have to include the style with this control, otherwise adding
        # items with Append will fail when there are multiple columns, and the
        # default style is specified.
        style = self.get_flags(node.attrib.pop('style', 'lc_icon'))
        c = wx.ListCtrl(parent, style=style)
        value = None
        items = []
        for tag in node:
            if tag.tag == 'value':
                value = self.parse_value(tag, frame, parent, sizer)
            elif tag.tag == 'column':
                args = self.parse_column(tag, frame, parent, sizer)
                c.AppendColumn(*args)
            elif tag.tag == 'item':
                item = self.parse_item(tag, frame, parent, sizer)
                items.append(item)
            else:
                raise InvalidTagError(tag)
        for item in items:
            c.Append(item)
        if value is not None:
            c.Focus(value)
            c.Select(value)
        return c

    def parse_value(self, node, frame, parent, sizer):
        """Get a value as an integer."""
        return int(node.text)

    def parse_column(self, node, frame, parent, sizer):
        """Return args that can be sent to wx.ListCtrl.AppendColumn."""
        heading = node.text
        if heading is None:
            raise NoValueError(node)
        a = node.attrib
        format = a.get('format', 'list_format_left')
        format = self.get_flags(format)
        width = int(a.get('width', -1))
        return (heading, format, width)

    def parse_item(self, node, frame, parent, sizer):
        """Parse a list item."""
        return self.get_list(node.text, function=str)