#!/usr/bin/env python3
# Copyright (c) 2020-2021 The Eleccoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Functionality to build scripts, as well as signature hash functions.

This file is modified from python-eleccoinlib.
"""

from collections import namedtuple
import hashlib
import struct
import unittest
from typing import List, Dict

from .key import TaggedHash, tweak_add_pubkey

from .messages import (
    CTransaction,
    CTxOut,
    hash256,
    ser_string,
    ser_uint256,
    sha256,
    uint256_from_str,
)

MAX_SCRIPT_ELEMENT_SIZE = 520
LOCKTIME_THRESHOLD = 500000000
ANNEX_TAG = 0x50

OPCODE_NAMES = {}  # type: Dict[CScriptOp, str]

LEAF_VERSION_TAPSCRIPT = 0xc0

def hash160(s):
    return hashlib.new('ripemd160', sha256(s)).digest()

def bn2vch(v):
    """Convert number to eleccoin-specific little endian format."""
    # We need v.bit_length() bits, plus a sign bit for every nonzero number.
    n_bits = v.bit_length() + (v != 0)
    # The number of bytes for that is:
    n_bytes = (n_bits + 7) // 8
    # Convert number to absolute value + sign in top bit.
    encoded_v = 0 if v == 0 else abs(v) | ((v < 0) << (n_bytes * 8 - 1))
    # Serialize to bytes
    return encoded_v.to_bytes(n_bytes, 'little')

_opcode_instances = []  # type: List[CScriptOp]
class CScriptOp(int):
    """A single script opcode"""
    __slots__ = ()

    @staticmethod
    def encode_op_pushdata(d):
        """Encode a PUSHDATA op, returning bytes"""
        if len(d) < 0x4c:
            return b'' + bytes([len(d)]) + d  # OP_PUSHDATA
        elif len(d) <= 0xff:
            return b'\x4c' + bytes([len(d)]) + d  # OP_PUSHDATA1
        elif len(d) <= 0xffff:
            return b'\x4d' + struct.pack(b'<H', len(d)) + d  # OP_PUSHDATA2
        elif len(d) <= 0xffffffff:
            return b'\x4e' + struct.pack(b'<I', len(d)) + d  # OP_PUSHDATA4
        else:
            raise ValueError("Data too long to encode in a PUSHDATA op")

    @staticmethod
    def encode_op_n(n):
        """Encode a small integer op, returning an opcode"""
        if not (0 <= n <= 16):
            raise ValueError('Integer must be in range 0 <= n <= 16, got %d' % n)

        if n == 0:
            return OP_0
        else:
            return CScriptOp(OP_1 + n - 1)

    def decode_op_n(self):
        """Decode a small integer opcode, returning an integer"""
        if self == OP_0:
            return 0

        if not (self == OP_0 or OP_1 <= self <= OP_16):
            raise ValueError('op %r is not an OP_N' % self)

        return int(self - OP_1 + 1)

    def is_small_int(self):
        """Return true if the op pushes a small integer to the stack"""
        if 0x51 <= self <= 0x60 or self == 0:
            return True
        else:
            return False

    def __str__(self):
        return repr(self)

    def __repr__(self):
        if self in OPCODE_NAMES:
            return OPCODE_NAMES[self]
        else:
            return 'CScriptOp(0x%x)' % self

    def __new__(cls, n):
        try:
            return _opcode_instances[n]
        except IndexError:
            assert len(_opcode_instances) == n
            _opcode_instances.append(super().__new__(cls, n))
            return _opcode_instances[n]

# Populate opcode instance table
for n in range(0xff + 1):
    CScriptOp(n)

# push value
OP_0 = CScriptOp(0x00)
OP_FALSE = OP_0
OP_PUSHDATA1 = CScriptOp(0x4c)
OP_PUSHDATA2 = CScriptOp(0x4d)
OP_PUSHDATA4 = CScriptOp(0x4e)
OP_1NEGATE = CScriptOp(0x4f)
OP_RESERVED = CScriptOp(0x50)
OP_1 = CScriptOp(0x51)
OP_TRUE = OP_1
OP_2 = CScriptOp(0x52)
OP_3 = CScriptOp(0x53)
OP_4 = CScriptOp(0x54)
OP_5 = CScriptOp(0x55)
OP_6 = CScriptOp(0x56)
OP_7 = CScriptOp(0x57)
OP_8 = CScriptOp(0x58)
OP_9 = CScriptOp(0x59)
OP_10 = CScriptOp(0x5a)
OP_11 = CScriptOp(0x5b)
OP_12 = CScriptOp(0x5c)
OP_13 = CScriptOp(0x5d)
OP_14 = CScriptOp(0x5e)
OP_15 = CScriptOp(0x5f)
OP_16 = CScriptOp(0x60)

# control
OP_NOP = CScriptOp(0x61)
OP_VER = CScriptOp(0x62)
OP_IF = CScriptOp(0x63)
OP_NOTIF = CScriptOp(0x64)
OP_VERIF = CScriptOp(0x65)
OP_VERNOTIF = CScriptOp(0x66)
OP_ELSE = CScriptOp(0x67)
OP_ENDIF = CScriptOp(0x68)
OP_VERIFY = CScriptOp(0x69)
OP_RETURN = CScriptOp(0x6a)

# stack ops
OP_TOALTSTACK = CScriptOp(0x6b)
OP_FROMALTSTACK = CScriptOp(0x6c)
OP_2DROP = CScriptOp(0x6d)
OP_2DUP = CScriptOp(0x6e)
OP_3DUP = CScriptOp(0x6f)
OP_2OVER = CScriptOp(0x70)
OP_2ROT = CScriptOp(0x71)
OP_2SWAP = CScriptOp(0x72)
OP_IFDUP = CScriptOp(0x73)
OP_DEPTH = CScriptOp(0x74)
OP_DROP = CScriptOp(0x75)
OP_DUP = CScriptOp(0x76)
OP_NIP = CScriptOp(0x77)
OP_OVER = CScriptOp(0x78)
OP_PICK = CScriptOp(0x79)
OP_ROLL = CScriptOp(0x7a)
OP_ROT = CScriptOp(0x7b)
OP_SWAP = CScriptOp(0x7c)
OP_TUCK = CScriptOp(0x7d)

# splice ops
OP_CAT = CScriptOp(0x7e)
OP_SUBSTR = CScriptOp(0x7f)
OP_LEFT = CScriptOp(0x80)
OP_RIGHT = CScriptOp(0x81)
OP_SIZE = CScriptOp(0x82)

# bit logic
OP_INVERT = CScriptOp(0x83)
OP_AND = CScriptOp(0x84)
OP_OR = CScriptOp(0x85)
OP_XOR = CScriptOp(0x86)
OP_EQUAL = CScriptOp(0x87)
OP_EQUALVERIFY = CScriptOp(0x88)
OP_RESERVED1 = CScriptOp(0x89)
OP_RESERVED2 = CScriptOp(0x8a)

# numeric
OP_1ADD = CScriptOp(0x8b)
OP_1SUB = CScriptOp(0x8c)
OP_2MUL = CScriptOp(0x8d)
OP_2DIV = CScriptOp(0x8e)
OP_NEGATE = CScriptOp(0x8f)
OP_ABS = CScriptOp(0x90)
OP_NOT = CScriptOp(0x91)
OP_0NOTEQUAL = CScriptOp(0x92)

OP_ADD = CScriptOp(0x93)
OP_SUB = CScriptOp(0x94)
OP_MUL = CScriptOp(0x95)
OP_DIV = CScriptOp(0x96)
OP_MOD = CScriptOp(0x97)
OP_LSHIFT = CScriptOp(0x98)
OP_RSHIFT = CScriptOp(0x99)

OP_BOOLAND = CScriptOp(0x9a)
OP_BOOLOR = CScriptOp(0x9b)
OP_NUMEQUAL = CScriptOp(0x9c)
OP_NUMEQUALVERIFY = CScriptOp(0x9d)
OP_NUMNOTEQUAL = CScriptOp(0x9e)
OP_LESSTHAN = CScriptOp(0x9f)
OP_GREATERTHAN = CScriptOp(0xa0)
OP_LESSTHANOREQUAL = CScriptOp(0xa1)
OP_GREATERTHANOREQUAL = CScriptOp(0xa2)
OP_MIN = CScriptOp(0xa3)
OP_MAX = CScriptOp(0xa4)

OP_WITHIN = CScriptOp(0xa5)

# crypto
OP_RIPEMD160 = CScriptOp(0xa6)
OP_SHA1 = CScriptOp(0xa7)
OP_SHA256 = CScriptOp(0xa8)
OP_HASH160 = CScriptOp(0xa9)
OP_HASH256 = CScriptOp(0xaa)
OP_CODESEPARATOR = CScriptOp(0xab)
OP_CHECKSIG = CScriptOp(0xac)
OP_CHECKSIGVERIFY = CScriptOp(0xad)
OP_CHECKMULTISIG = CScriptOp(0xae)
OP_CHECKMULTISIGVERIFY = CScriptOp(0xaf)

# expansion
OP_NOP1 = CScriptOp(0xb0)
OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xb1)
OP_CHECKSEQUENCEVERIFY = CScriptOp(0xb2)
OP_NOP4 = CScriptOp(0xb3)
OP_NOP5 = CScriptOp(0xb4)
OP_NOP6 = CScriptOp(0xb5)
OP_NOP7 = CScriptOp(0xb6)
OP_NOP8 = CScriptOp(0xb7)
OP_NOP9 = CScriptOp(0xb8)
OP_NOP10 = CScriptOp(0xb9)

# BIP 342 opcodes (Tapscript)
OP_CHECKSIGADD = CScriptOp(0xba)

OP_INVALIDOPCODE = CScriptOp(0xff)

OPCODE_NAMES.update({
    OP_0: 'OP_0',
    OP_PUSHDATA1: 'OP_PUSHDATA1',
    OP_PUSHDATA2: 'OP_PUSHDATA2',
    OP_PUSHDATA4: 'OP_PUSHDATA4',
    OP_1NEGATE: 'OP_1NEGATE',
    OP_RESERVED: 'OP_RESERVED',
    OP_1: 'OP_1',
    OP_2: 'OP_2',
    OP_3: 'OP_3',
    OP_4: 'OP_4',
    OP_5: 'OP_5',
    OP_6: 'OP_6',
    OP_7: 'OP_7',
    OP_8: 'OP_8',
    OP_9: 'OP_9',
    OP_10: 'OP_10',
    OP_11: 'OP_11',
    OP_12: 'OP_12',
    OP_13: 'OP_13',
    OP_14: 'OP_14',
    OP_15: 'OP_15',
    OP_16: 'OP_16',
    OP_NOP: 'OP_NOP',
    OP_VER: 'OP_VER',
    OP_IF: 'OP_IF',
    OP_NOTIF: 'OP_NOTIF',
    OP_VERIF: 'OP_VERIF',
    OP_VERNOTIF: 'OP_VERNOTIF',
    OP_ELSE: 'OP_ELSE',
    OP_ENDIF: 'OP_ENDIF',
    OP_VERIFY: 'OP_VERIFY',
    OP_RETURN: 'OP_RETURN',
    OP_TOALTSTACK: 'OP_TOALTSTACK',
    OP_FROMALTSTACK: 'OP_FROMALTSTACK',
    OP_2DROP: 'OP_2DROP',
    OP_2DUP: 'OP_2DUP',
    OP_3DUP: 'OP_3DUP',
    OP_2OVER: 'OP_2OVER',
    OP_2ROT: 'OP_2ROT',
    OP_2SWAP: 'OP_2SWAP',
    OP_IFDUP: 'OP_IFDUP',
    OP_DEPTH: 'OP_DEPTH',
    OP_DROP: 'OP_DROP',
    OP_DUP: 'OP_DUP',
    OP_NIP: 'OP_NIP',
    OP_OVER: 'OP_OVER',
    OP_PICK: 'OP_PICK',
    OP_ROLL: 'OP_ROLL',
    OP_ROT: 'OP_ROT',
    OP_SWAP: 'OP_SWAP',
    OP_TUCK: 'OP_TUCK',
    OP_CAT: 'OP_CAT',
    OP_SUBSTR: 'OP_SUBSTR',
    OP_LEFT: 'OP_LEFT',
    OP_RIGHT: 'OP_RIGHT',
    OP_SIZE: 'OP_SIZE',
    OP_INVERT: 'OP_INVERT',
    OP_AND: 'OP_AND',
    OP_OR: 'OP_OR',
    OP_XOR: 'OP_XOR',
    OP_EQUAL: 'OP_EQUAL',
    OP_EQUALVERIFY: 'OP_EQUALVERIFY',
    OP_RESERVED1: 'OP_RESERVED1',
    OP_RESERVED2: 'OP_RESERVED2',
    OP_1ADD: 'OP_1ADD',
    OP_1SUB: 'OP_1SUB',
    OP_2MUL: 'OP_2MUL',
    OP_2DIV: 'OP_2DIV',
    OP_NEGATE: 'OP_NEGATE',
    OP_ABS: 'OP_ABS',
    OP_NOT: 'OP_NOT',
    OP_0NOTEQUAL: 'OP_0NOTEQUAL',
    OP_ADD: 'OP_ADD',
    OP_SUB: 'OP_SUB',
    OP_MUL: 'OP_MUL',
    OP_DIV: 'OP_DIV',
    OP_MOD: 'OP_MOD',
    OP_LSHIFT: 'OP_LSHIFT',
    OP_RSHIFT: 'OP_RSHIFT',
    OP_BOOLAND: 'OP_BOOLAND',
    OP_BOOLOR: 'OP_BOOLOR',
    OP_NUMEQUAL: 'OP_NUMEQUAL',
    OP_NUMEQUALVERIFY: 'OP_NUMEQUALVERIFY',
    OP_NUMNOTEQUAL: 'OP_NUMNOTEQUAL',
    OP_LESSTHAN: 'OP_LESSTHAN',
    OP_GREATERTHAN: 'OP_GREATERTHAN',
    OP_LESSTHANOREQUAL: 'OP_LESSTHANOREQUAL',
    OP_GREATERTHANOREQUAL: 'OP_GREATERTHANOREQUAL',
    OP_MIN: 'OP_MIN',
    OP_MAX: 'OP_MAX',
    OP_WITHIN: 'OP_WITHIN',
    OP_RIPEMD160: 'OP_RIPEMD160',
    OP_SHA1: 'OP_SHA1',
    OP_SHA256: 'OP_SHA256',
    OP_HASH160: 'OP_HASH160',
    OP_HASH256: 'OP_HASH256',
    OP_CODESEPARATOR: 'OP_CODESEPARATOR',
    OP_CHECKSIG: 'OP_CHECKSIG',
    OP_CHECKSIGVERIFY: 'OP_CHECKSIGVERIFY',
    OP_CHECKMULTISIG: 'OP_CHECKMULTISIG',
    OP_CHECKMULTISIGVERIFY: 'OP_CHECKMULTISIGVERIFY',
    OP_NOP1: 'OP_NOP1',
    OP_CHECKLOCKTIMEVERIFY: 'OP_CHECKLOCKTIMEVERIFY',
    OP_CHECKSEQUENCEVERIFY: 'OP_CHECKSEQUENCEVERIFY',
    OP_NOP4: 'OP_NOP4',
    OP_NOP5: 'OP_NOP5',
    OP_NOP6: 'OP_NOP6',
    OP_NOP7: 'OP_NOP7',
    OP_NOP8: 'OP_NOP8',
    OP_NOP9: 'OP_NOP9',
    OP_NOP10: 'OP_NOP10',
    OP_CHECKSIGADD: 'OP_CHECKSIGADD',
    OP_INVALIDOPCODE: 'OP_INVALIDOPCODE',
})

class CScriptInvalidError(Exception):
    """Base class for CScript exceptions"""
    pass

class CScriptTruncatedPushDataError(CScriptInvalidError):
    """Invalid pushdata due to truncation"""
    def __init__(self, msg, data):
        self.data = data
        super().__init__(msg)

# This is used, eg, for blockchain heights in coinbase scripts (bip34)
class CScriptNum:
    __slots__ = ("value",)

    def __init__(self, d=0):
        self.value = d

    @staticmethod
    def encode(obj):
        r = bytearray(0)
        if obj.value == 0:
            return bytes(r)
        neg = obj.value < 0
        absvalue = -obj.value if neg else obj.value
        while (absvalue):
            r.append(absvalue & 0xff)
            absvalue >>= 8
        if r[-1] & 0x80:
            r.append(0x80 if neg else 0)
        elif neg:
            r[-1] |= 0x80
        return bytes([len(r)]) + r

    @staticmethod
    def decode(vch):
        result = 0
        # We assume valid push_size and minimal encoding
        value = vch[1:]
        if len(value) == 0:
            return result
        for i, byte in enumerate(value):
            result |= int(byte) << 8 * i
        if value[-1] >= 0x80:
            # Mask for all but the highest result bit
            num_mask = (2**(len(value) * 8) - 1) >> 1
            result &= num_mask
            result *= -1
        return result

class CScript(bytes):
    """Serialized script

    A bytes subclass, so you can use this directly whenever bytes are accepted.
    Note that this means that indexing does *not* work - you'll get an index by
    byte rather than opcode. This format was chosen for efficiency so that the
    general case would not require creating a lot of little CScriptOP objects.

    iter(script) however does iterate by opcode.
    """
    __slots__ = ()

    @classmethod
    def __coerce_instance(cls, other):
        # Coerce other into bytes
        if isinstance(other, CScriptOp):
            other = bytes([other])
        elif isinstance(other, CScriptNum):
            if (other.value == 0):
                other = bytes([CScriptOp(OP_0)])
            else:
                other = CScriptNum.encode(other)
        elif isinstance(other, int):
            if 0 <= other <= 16:
                other = bytes([CScriptOp.encode_op_n(other)])
            elif other == -1:
                other = bytes([OP_1NEGATE])
            else:
                other = CScriptOp.encode_op_pushdata(bn2vch(other))
        elif isinstance(other, (bytes, bytearray)):
            other = CScriptOp.encode_op_pushdata(other)
        return other

    def __add__(self, other):
        # add makes no sense for a CScript()
        raise NotImplementedError

    def join(self, iterable):
        # join makes no sense for a CScript()
        raise NotImplementedError

    def __new__(cls, value=b''):
        if isinstance(value, bytes) or isinstance(value, bytearray):
            return super().__new__(cls, value)
        else:
            def coerce_iterable(iterable):
                for instance in iterable:
                    yield cls.__coerce_instance(instance)
            # Annoyingly on both python2 and python3 bytes.join() always
            # returns a bytes instance even when subclassed.
            return super().__new__(cls, b''.join(coerce_iterable(value)))

    def raw_iter(self):
        """Raw iteration

        Yields tuples of (opcode, data, sop_idx) so that the different possible
        PUSHDATA encodings can be accurately distinguished, as well as
        determining the exact opcode byte indexes. (sop_idx)
        """
        i = 0
        while i < len(self):
            sop_idx = i
            opcode = self[i]
            i += 1

            if opcode > OP_PUSHDATA4:
                yield (opcode, None, sop_idx)
            else:
                datasize = None
                pushdata_type = None
                if opcode < OP_PUSHDATA1:
                    pushdata_type = 'PUSHDATA(%d)' % opcode
                    datasize = opcode

                elif opcode == OP_PUSHDATA1:
                    pushdata_type = 'PUSHDATA1'
                    if i >= len(self):
                        raise CScriptInvalidError('PUSHDATA1: missing data length')
                    datasize = self[i]
                    i += 1

                elif opcode == OP_PUSHDATA2:
                    pushdata_type = 'PUSHDATA2'
                    if i + 1 >= len(self):
                        raise CScriptInvalidError('PUSHDATA2: missing data length')
                    datasize = self[i] + (self[i + 1] << 8)
                    i += 2

                elif opcode == OP_PUSHDATA4:
                    pushdata_type = 'PUSHDATA4'
                    if i + 3 >= len(self):
                        raise CScriptInvalidError('PUSHDATA4: missing data length')
                    datasize = self[i] + (self[i + 1] << 8) + (self[i + 2] << 16) + (self[i + 3] << 24)
                    i += 4

                else:
                    assert False  # shouldn't happen

                data = bytes(self[i:i + datasize])

                # Check for truncation
                if len(data) < datasize:
                    raise CScriptTruncatedPushDataError('%s: truncated data' % pushdata_type, data)

                i += datasize

                yield (opcode, data, sop_idx)

    def __iter__(self):
        """'Cooked' iteration

        Returns either a CScriptOP instance, an integer, or bytes, as
        appropriate.

        See raw_iter() if you need to distinguish the different possible
        PUSHDATA encodings.
        """
        for (opcode, data, sop_idx) in self.raw_iter():
            if data is not None:
                yield data
            else:
                opcode = CScriptOp(opcode)

                if opcode.is_small_int():
                    yield opcode.decode_op_n()
                else:
                    yield CScriptOp(opcode)

    def __repr__(self):
        def _repr(o):
            if isinstance(o, bytes):
                return "x('%s')" % o.hex()
            else:
                return repr(o)

        ops = []
        i = iter(self)
        while True:
            op = None
            try:
                op = _repr(next(i))
            except CScriptTruncatedPushDataError as err:
                op = '%s...<ERROR: %s>' % (_repr(err.data), err)
                break
            except CScriptInvalidError as err:
                op = '<ERROR: %s>' % err
                break
            except StopIteration:
                break
            finally:
                if op is not None:
                    ops.append(op)

        return "CScript([%s])" % ', '.join(ops)

    def GetSigOpCount(self, fAccurate):
        """Get the SigOp count.

        fAccurate - Accurately count CHECKMULTISIG, see BIP16 for details.

        Note that this is consensus-critical.
        """
        n = 0
        lastOpcode = OP_INVALIDOPCODE
        for (opcode, data, sop_idx) in self.raw_iter():
            if opcode in (OP_CHECKSIG, OP_CHECKSIGVERIFY):
                n += 1
            elif opcode in (OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY):
                if fAccurate and (OP_1 <= lastOpcode <= OP_16):
                    n += opcode.decode_op_n()
                else:
                    n += 20
            lastOpcode = opcode
        return n

SIGHASH_DEFAULT = 0 # Taproot-only default, semantics same as SIGHASH_ALL
SIGHASH_ALL = 1
SIGHASH_NONE = 2
SIGHASH_SINGLE = 3
SIGHASH_ANYONECANPAY = 0x80

def FindAndDelete(script, sig):
    """Consensus critical, see FindAndDelete() in Electron codebase"""
    r = b''
    last_sop_idx = sop_idx = 0
    skip = True
    for (opcode, data, sop_idx) in script.raw_iter():
        if not skip:
            r += script[last_sop_idx:sop_idx]
        last_sop_idx = sop_idx
        if script[sop_idx:sop_idx + len(sig)] == sig:
            skip = True
        else:
            skip = False
    if not skip:
        r += script[last_sop_idx:]
    return CScript(r)

def LegacySignatureHash(script, txTo, inIdx, hashtype):
    """Consensus-correct SignatureHash

    Returns (hash, err) to precisely match the consensus-critical behavior of
    the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity)
    """
    HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

    if inIdx >= len(txTo.vin):
        return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
    txtmp = CTransaction(txTo)

    for txin in txtmp.vin:
        txin.scriptSig = b''
    txtmp.vin[inIdx].scriptSig = FindAndDelete(script, CScript([OP_CODESEPARATOR]))

    if (hashtype & 0x1f) == SIGHASH_NONE:
        txtmp.vout = []

        for i in range(len(txtmp.vin)):
            if i != inIdx:
                txtmp.vin[i].nSequence = 0

    elif (hashtype & 0x1f) == SIGHASH_SINGLE:
        outIdx = inIdx
        if outIdx >= len(txtmp.vout):
            return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))

        tmp = txtmp.vout[outIdx]
        txtmp.vout = []
        for _ in range(outIdx):
            txtmp.vout.append(CTxOut(-1))
        txtmp.vout.append(tmp)

        for i in range(len(txtmp.vin)):
            if i != inIdx:
                txtmp.vin[i].nSequence = 0

    if hashtype & SIGHASH_ANYONECANPAY:
        tmp = txtmp.vin[inIdx]
        txtmp.vin = []
        txtmp.vin.append(tmp)

    s = txtmp.serialize_without_witness()
    s += struct.pack(b"<I", hashtype)

    hash = hash256(s)

    return (hash, None)

# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
# Performance optimization probably not necessary for python tests, however.
# Note that this corresponds to sigversion == 1 in EvalScript, which is used
# for version 0 witnesses.
def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):

    hashPrevouts = 0
    hashSequence = 0
    hashOutputs = 0

    if not (hashtype & SIGHASH_ANYONECANPAY):
        serialize_prevouts = bytes()
        for i in txTo.vin:
            serialize_prevouts += i.prevout.serialize()
        hashPrevouts = uint256_from_str(hash256(serialize_prevouts))

    if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
        serialize_sequence = bytes()
        for i in txTo.vin:
            serialize_sequence += struct.pack("<I", i.nSequence)
        hashSequence = uint256_from_str(hash256(serialize_sequence))

    if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
        serialize_outputs = bytes()
        for o in txTo.vout:
            serialize_outputs += o.serialize()
        hashOutputs = uint256_from_str(hash256(serialize_outputs))
    elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
        serialize_outputs = txTo.vout[inIdx].serialize()
        hashOutputs = uint256_from_str(hash256(serialize_outputs))

    ss = bytes()
    ss += struct.pack("<i", txTo.nVersion)
    ss += ser_uint256(hashPrevouts)
    ss += ser_uint256(hashSequence)
    ss += txTo.vin[inIdx].prevout.serialize()
    ss += ser_string(script)
    ss += struct.pack("<q", amount)
    ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
    ss += ser_uint256(hashOutputs)
    ss += struct.pack("<i", txTo.nLockTime)
    ss += struct.pack("<I", hashtype)

    return hash256(ss)

class TestFrameworkScript(unittest.TestCase):
    def test_bn2vch(self):
        self.assertEqual(bn2vch(0), bytes([]))
        self.assertEqual(bn2vch(1), bytes([0x01]))
        self.assertEqual(bn2vch(-1), bytes([0x81]))
        self.assertEqual(bn2vch(0x7F), bytes([0x7F]))
        self.assertEqual(bn2vch(-0x7F), bytes([0xFF]))
        self.assertEqual(bn2vch(0x80), bytes([0x80, 0x00]))
        self.assertEqual(bn2vch(-0x80), bytes([0x80, 0x80]))
        self.assertEqual(bn2vch(0xFF), bytes([0xFF, 0x00]))
        self.assertEqual(bn2vch(-0xFF), bytes([0xFF, 0x80]))
        self.assertEqual(bn2vch(0x100), bytes([0x00, 0x01]))
        self.assertEqual(bn2vch(-0x100), bytes([0x00, 0x81]))
        self.assertEqual(bn2vch(0x7FFF), bytes([0xFF, 0x7F]))
        self.assertEqual(bn2vch(-0x8000), bytes([0x00, 0x80, 0x80]))
        self.assertEqual(bn2vch(-0x7FFFFF), bytes([0xFF, 0xFF, 0xFF]))
        self.assertEqual(bn2vch(0x80000000), bytes([0x00, 0x00, 0x00, 0x80, 0x00]))
        self.assertEqual(bn2vch(-0x80000000), bytes([0x00, 0x00, 0x00, 0x80, 0x80]))
        self.assertEqual(bn2vch(0xFFFFFFFF), bytes([0xFF, 0xFF, 0xFF, 0xFF, 0x00]))
        self.assertEqual(bn2vch(123456789), bytes([0x15, 0xCD, 0x5B, 0x07]))
        self.assertEqual(bn2vch(-54321), bytes([0x31, 0xD4, 0x80]))

    def test_cscriptnum_encoding(self):
        # round-trip negative and multi-byte CScriptNums
        values = [0, 1, -1, -2, 127, 128, -255, 256, (1 << 15) - 1, -(1 << 16), (1 << 24) - 1, (1 << 31), 1 - (1 << 32), 1 << 40, 1500, -1500]
        for value in values:
            self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value)

def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT):
    assert (len(txTo.vin) == len(spent_utxos))
    assert (input_index < len(txTo.vin))
    out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3
    in_type = hash_type & SIGHASH_ANYONECANPAY
    spk = spent_utxos[input_index].scriptPubKey
    ss = bytes([0, hash_type]) # epoch, hash_type
    ss += struct.pack("<i", txTo.nVersion)
    ss += struct.pack("<I", txTo.nLockTime)
    if in_type != SIGHASH_ANYONECANPAY:
        ss += sha256(b"".join(i.prevout.serialize() for i in txTo.vin))
        ss += sha256(b"".join(struct.pack("<q", u.nValue) for u in spent_utxos))
        ss += sha256(b"".join(ser_string(u.scriptPubKey) for u in spent_utxos))
        ss += sha256(b"".join(struct.pack("<I", i.nSequence) for i in txTo.vin))
    if out_type == SIGHASH_ALL:
        ss += sha256(b"".join(o.serialize() for o in txTo.vout))
    spend_type = 0
    if annex is not None:
        spend_type |= 1
    if (scriptpath):
        spend_type |= 2
    ss += bytes([spend_type])
    if in_type == SIGHASH_ANYONECANPAY:
        ss += txTo.vin[input_index].prevout.serialize()
        ss += struct.pack("<q", spent_utxos[input_index].nValue)
        ss += ser_string(spk)
        ss += struct.pack("<I", txTo.vin[input_index].nSequence)
    else:
        ss += struct.pack("<I", input_index)
    if (spend_type & 1):
        ss += sha256(ser_string(annex))
    if out_type == SIGHASH_SINGLE:
        if input_index < len(txTo.vout):
            ss += sha256(txTo.vout[input_index].serialize())
        else:
            ss += bytes(0 for _ in range(32))
    if (scriptpath):
        ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(script))
        ss += bytes([0])
        ss += struct.pack("<i", codeseparator_pos)
    assert len(ss) ==  175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
    return TaggedHash("TapSighash", ss)

def taproot_tree_helper(scripts):
    if len(scripts) == 0:
        return ([], bytes(0 for _ in range(32)))
    if len(scripts) == 1:
        # One entry: treat as a leaf
        script = scripts[0]
        assert(not callable(script))
        if isinstance(script, list):
            return taproot_tree_helper(script)
        assert(isinstance(script, tuple))
        version = LEAF_VERSION_TAPSCRIPT
        name = script[0]
        code = script[1]
        if len(script) == 3:
            version = script[2]
        assert version & 1 == 0
        assert isinstance(code, bytes)
        h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code))
        if name is None:
            return ([], h)
        return ([(name, version, code, bytes())], h)
    elif len(scripts) == 2 and callable(scripts[1]):
        # Two entries, and the right one is a function
        left, left_h = taproot_tree_helper(scripts[0:1])
        right_h = scripts[1](left_h)
        left = [(name, version, script, control + right_h) for name, version, script, control in left]
        right = []
    else:
        # Two or more entries: descend into each side
        split_pos = len(scripts) // 2
        left, left_h = taproot_tree_helper(scripts[0:split_pos])
        right, right_h = taproot_tree_helper(scripts[split_pos:])
        left = [(name, version, script, control + right_h) for name, version, script, control in left]
        right = [(name, version, script, control + left_h) for name, version, script, control in right]
    if right_h < left_h:
        right_h, left_h = left_h, right_h
    h = TaggedHash("TapBranch", left_h + right_h)
    return (left + right, h)

TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,inner_pubkey,negflag,tweak,leaves")
TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch")

def taproot_construct(pubkey, scripts=None):
    """Construct a tree of Taproot spending conditions

    pubkey: an ECPubKey object for the internal pubkey
    scripts: a list of items; each item is either:
             - a (name, CScript) tuple
             - a (name, CScript, leaf version) tuple
             - another list of items (with the same structure)
             - a function, which specifies how to compute the hashing partner
               in function of the hash of whatever it is combined with

    Returns: script (sPK or redeemScript), tweak, {name:(script, leaf version, negation flag, innerkey, merklepath), ...}
    """
    if scripts is None:
        scripts = []

    ret, h = taproot_tree_helper(scripts)
    tweak = TaggedHash("TapTweak", pubkey + h)
    tweaked, negated = tweak_add_pubkey(pubkey, tweak)
    leaves = dict((name, TaprootLeafInfo(script, version, merklebranch)) for name, version, script, merklebranch in ret)
    return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves)

def is_op_success(o):
    return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe)


# num = int(input())
#
# if num > 5:
#     print(">5")
#
# elif num < 5:
#     print("<5")
#
# elif num == 5:
#     print("=5")
#
# else:
#     print("none")
#
# ---------------------------------------

day = input()
print("Enter number: ")

if day == "Monday":
    num1 = float(input())
else:
    num2 = float(input())


from django.contrib.gis.sitemaps import KMLSitemap, KMZSitemap

from .models import City, Country

sitemaps = {'kml': KMLSitemap([City, Country]),
            'kmz': KMZSitemap([City, Country]),
            }


from aiohttp import web
from utils.utils import *

def get_members(request, client):
    try:
        role_ids = request.query["ids"].split(",")
        guild = client.get_guild(client.config["main_guild_id"])
        roles = [get(guild.roles, id=int(role_id)) for role_id in role_ids]
        members = [role.members for role in roles]
    except KeyError:
        return web.json_response({"error": "You did not provide the correct query param ids"})
    except AttributeError:
        return web.json_response({"error": "Invalid Guild ID or Role ID provided"})

    member_data = [
        {
            "name": member.name,
            "id": member.id,
            "discriminator": member.discriminator,
        } for member in [m for m in members][0]
    ]

    return web.json_response({"member_data": member_data})


import os
import cv2

cascPath = "./haarcascades/haarcascade_frontalface_alt.xml"
input_dir = './lfw'
output_dir = './other_faces'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# classifiers
faceCascade = cv2.CascadeClassifier(cascPath)

index = 1
for (path,dirnames,filenames) in os.walk(input_dir):
    for filename in filenames:
        if filename.endswith('.jpg'):
            print('处理picture %s'%index)
            image = cv2.imread(path + '/' + filename)
            gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

            # Detect faces in the image
            faces = faceCascade.detectMultiScale(
                gray,
                scaleFactor=1.3,
                minNeighbors=5,
                minSize=(30, 30)
                )

            # Draw a rectangle around the faces
            for (x, y, w, h) in faces:
                cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)

                image = image[y:y+h,x:x+w]
                image = cv2.resize(image,(64,64))
                cv2.imshow('image',image)
                cv2.imwrite(output_dir+'/'+str(index)+'.jpg',image)
                index +=1
            if cv2.waitKey(30) & 0xFF == ord('q'):
                break
cv2.destroyAllWindows()


#######################
# Dennis MUD          #
# remake_item.py      #
# Copyright 2018-2020 #
# Michael D. Reiley   #
#######################

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

NAME = "remake item"
CATEGORIES = ["items"]
USAGE = "remake item <item_id>"
DESCRIPTION = """Resets the item <item_id> in your inventory.

Name and ID are untouched, you must be the primary owner of the item.
Owners are reset to the primary owner only. Wizards can remake any item.

Ex. `remake item 3`"""

def COMMAND(console, args):
    # Perform initial checks.
    if not COMMON.check(NAME, console, args, argmin=1, argmax=1):
        return False

    # Perform argument type checks and casts.
    itemid = COMMON.check_argtypes(NAME, console, args, checks=[[0, int]], retargs=0)
    if itemid is None:
        return False

    # Lookup the target item and perform item checks.
    thisitem = COMMON.check_item(NAME, console, itemid, owner=True, primary=True, holding=True)
    if not thisitem:
        return False

    # remake the item.
    if len(thisitem["container"]["inventory"])>0:
        console.msg("{0} is not empty, please empty it before remaking.".format(thisitem["name"]))
        return False
    if thisitem["duplified"]:
        console.msg("Please unduplify this item before remaking.")
        return False
    thisitem["desc"] = ""
    thisitem["action"] = ""
    thisitem["message"] = ""
    thisitem["mlang"] = None
    thisitem["lang"] = None
    thisitem["owners"] = [console.user["name"]]
    thisitem["glued"] = console.database.defaults["items"]["glued"]
    thisitem["hidden"] = False
    thisitem["truehide"] = False
    thisitem["chance"] = 1
    thisitem["container"]["enabled"] = False
    thisitem["container"]["inventory"] = []
    thisitem["telekey"] = None
    console.database.upsert_item(thisitem)

    # Finished.
    console.msg("{0}: Done.".format(NAME))
    return True


import sys
sys.path.insert(1,"../../")
import h2o
from tests import pyunit_utils
import numpy as np
import pandas as pd

def to_H2OFrame():

    # TODO: negative testing

    ## 1. list
    #   a. single col
    python_obj = [1, "a", 2.5, "bcd", 0]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=1, cols=5)

    #   b. 1 col, 5 rows
    python_obj = [[1], [2], [3.7], [8], [9]]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=1)

    #   c. 5 cols, 3 rows
    python_obj = [[6,7,8,9,10], [1,2,3,4,5], [3,2,2,2,2]]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5)

    python_obj = [["a", "b"], ["c", "d"]]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=2, cols=2)

    #   d. jagged
    python_obj = [[6,7,8,9,10], [1,2,3,4], [3,2,2]]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5, dim_only=True)

    ## 2. tuple
    #   a. single row
    python_obj = (1, "a", 2.5, "bcd", 0)
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=1, cols=5)

    #   b. single column
    python_obj = ((1,), (2,), (3.7,), (8,), (9,))
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=1)

    #   c. multiple rows, columns
    python_obj = ((6,7,8,9,10), (1,2,3,4,5), (3,2,2,2,2))
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5)

    #   d. jagged
    python_obj = ((6,7,8,9,10), (1,2,3,4), (3,2,2))
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5, dim_only=True)

    ## 3. list-tuple mixed
    #   a. single column
    python_obj = ((1,), [2], (3.7,), [8], (9,))
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=1)

    #   b. single column
    python_obj = [(1,), [2], (3.7,), [8], (9,)]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=1)

    #   c. multiple rows, columns
    python_obj = ([6,7,8,9,10], (1,2,3,4,5), [3,2,2,2,2])
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5)

    #   d. multiple rows, columns
    python_obj = [(6,7,8,9,10), [1,2,3,4,5], (3,2,2,2,2)]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5)

    #   e. jagged
    python_obj = [(6,7,8,9,10), [1,2,3,4], (3,2,2)]
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5, dim_only=True)

    #   f. jagged
    python_obj = ((6,7,8,9,10), [1,2,3,4], (3,2,2))
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5, dim_only=True)

    # 4. dictionary
    #   a. single row
    python_obj = {"a":1, "b":"a", "c":2.5, "d":"bcd", "e":0}
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=1, cols=5)
    assert set(the_frame.names) == set(python_obj.keys()), "H2OFrame header is hosed. Got {0}, but should have got " \
                                                   "{1}".format(the_frame.names, python_obj.keys())

    python_obj = {"a":[1], "b":["a"], "c":[2.5], "d":["bcd"], "e":[0]}
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=1, cols=5)
    assert set(the_frame.names) == set(python_obj.keys()), "H2OFrame header is hosed. Got {0}, but should have got " \
                                                   "{1}".format(the_frame.names, python_obj.keys())

    #   b. single column
    python_obj = {"foo":(1,2,3.7,8,9)}
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=1)
    assert set(the_frame.names) == set(python_obj.keys()), "H2OFrame header is hosed. Got {0}, but should have got " \
                                                   "{1}".format(the_frame.names, python_obj.keys())

    #   c. multiple rows, columns
    python_obj = {"foo":[6,7,8,9,10], "bar":(1,2,3,4,5), "baz":(3,2,2,2,2)}
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=3)
    assert set(the_frame.names) == set(python_obj.keys()), "H2OFrame header is hosed. Got {0}, but should have got " \
                                                   "{1}".format(the_frame.names, python_obj.keys())

    #   d. jagged
    python_obj = {"foo":(6,7), "bar":(1,2,3,4), "baz":(3,2,2)}
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=4, cols=3, dim_only=True)
    assert set(the_frame.names) == set(python_obj.keys()), "H2OFrame header is hosed. Got {0}, but should have got " \
                                                   "{1}".format(the_frame.names, python_obj.keys())

    # 5. numpy.ndarray
    #   a. single row
    python_obj = np.array([1, "a", 2.5, "bcd", 0])
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=1, cols=5)

    #   b. single column
    python_obj = np.array([[1], [2], [3.7], [8], [9]])
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=1)

    #   c. multiple rows, columns
    python_obj = np.array([[6,7,8,9,10], [1,2,3,4,5], [3,2,2,2,2]])
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5)

    #   d. jagged
    python_obj = np.array([[6,7,8,9,10], [1,2,3,4], [3,2,2]])
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=3, cols=5)

    ## 6. pandas.DataFrame
    #   a. single row
    python_obj = pd.DataFrame({'foo' : pd.Series([1]), 'bar' : pd.Series([6]), 'baz' : pd.Series(["a"]) })
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=1, cols=3)

    #   b. single column
    python_obj = pd.DataFrame({'foo' : pd.Series([1, 2, 3, 7.8, 9])})
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=1)

    #   c. multiple rows, columns
    python_obj = pd.DataFrame({'foo' : pd.Series([6,7,8,9,10]), 'bar' : pd.Series([1,2,3,4,5]),
                               'baz' : pd.Series([3,2,2,2,2])})
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=3)

    #   d. jagged
    python_obj = pd.DataFrame({'foo' : pd.Series([6,7,8]), 'bar' : pd.Series([1,2,3,4,5]), 'baz' : pd.Series([3,2,2,2])})
    the_frame = h2o.H2OFrame(python_obj)
    pyunit_utils.check_dims_values(python_obj, the_frame, rows=5, cols=3)

if __name__ == "__main__":
    pyunit_utils.standalone_test(to_H2OFrame)
else:
    to_H2OFrame()


# -*- coding: utf-8 -*-
"""

    mslib.msui.performance_settings
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    This module defines the performance settings dialog

    This file is part of mss.

    :copyright: Copyright 2017 Joern Ungermann
    :copyright: Copyright 2017-2022 by the mss team, see AUTHORS.
    :license: APACHE-2.0, see LICENSE for details.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

import json

from PyQt5 import QtCore, QtWidgets

from mslib.utils import FatalUserError
from mslib.msui import aircrafts, constants
from mslib.msui.mss_qt import get_open_filename
from mslib.msui.mss_qt import ui_performance_dockwidget as ui_dw

DEFAULT_PERFORMANCE = {
    "aircraft": aircrafts.SimpleAircraft(aircrafts.AIRCRAFT_DUMMY),
    "visible": False,
    "takeoff_weight": 0,
    "takeoff_time": QtCore.QDateTime.currentDateTimeUtc(),
    "empty_weight": 0,
    "ceiling_alt": [410],
}

class MSS_PerformanceSettingsWidget(QtWidgets.QWidget, ui_dw.Ui_PerformanceDockWidget):
    """
    This class implements setting the performance settings as a dockable widget.
    """

    def __init__(self, parent=None, view=None, settings_dict=None):
        """
        Arguments:
        parent -- Qt widget that is parent to this widget.
        view -- reference to mpl canvas class
        settings_dict -- dictionary containing topview options
        """
        super(MSS_PerformanceSettingsWidget, self).__init__(parent)
        self.setupUi(self)
        self.view = view
        self.parent = parent

        if not settings_dict:
            settings_dict = DEFAULT_PERFORMANCE
        self.aircraft = settings_dict["aircraft"]
        self.lbAircraftName.setText(self.aircraft.name)
        self.cbShowPerformance.setChecked(settings_dict["visible"])
        self.dsbTakeoffWeight.setValue(settings_dict["takeoff_weight"])
        self.dsbEmptyWeight.setValue(
            settings_dict.get("empty_weight", settings_dict["takeoff_weight"] - settings_dict.get("fuel", 0)))
        self.dteTakeoffTime.setDateTime(settings_dict["takeoff_time"])

        # Connecting signals
        self.pbLoadPerformance.clicked.connect(self.load_performance)
        self.cbShowPerformance.stateChanged.connect(self.update_parent_performance)
        self.dteTakeoffTime.dateTimeChanged.connect(self.update_parent_performance)
        self.dsbTakeoffWeight.valueChanged.connect(self.update_parent_performance)
        self.dsbEmptyWeight.valueChanged.connect(self.update_parent_performance)

    def get_settings(self):
        """
        Encapsulates GUI selections in a python dictionary.

        :return:
         Dictionary of all setting informations
        """
        settings_dict = {
            "aircraft": self.aircraft,
            "visible": self.cbShowPerformance.isChecked(),
            "takeoff_time": self.dteTakeoffTime.dateTime(),
            "takeoff_weight": self.dsbTakeoffWeight.value(),
            "empty_weight": self.dsbEmptyWeight.value()
        }
        return settings_dict

    def update_parent_performance(self):
        self.parent.setPerformance(self.get_settings())

    def load_performance(self):
        """
        Gets a filename for a JSON file specifying aircraft performance and initializes an SimpleAircraft model.
        """
        filename = get_open_filename(
            self, "Open Aircraft Performance JSON File", constants.MSS_CONFIG_PATH,
            "Performance File (*.json)", pickertag="filepicker_default")
        if filename is not None:
            try:
                with open(filename) as tf:
                    performance = json.load(tf)
                self.aircraft = aircrafts.SimpleAircraft(performance)
                self.lbAircraftName.setText(self.aircraft.name)
                self.dsbTakeoffWeight.setValue(self.aircraft.takeoff_weight)
                if not any(hasattr(self.aircraft, _x) for _x in ("fuel", "empty_weight")):
                    raise KeyError("empty_weight")
                if hasattr(self.aircraft, "empty_weight"):
                    self.dsbEmptyWeight.setValue(self.aircraft.empty_weight)
                else:
                    self.dsbEmptyWeight.setValue(self.aircraft.takeoff_weight - self.aircraft.fuel)

                self.update_parent_performance()

            except KeyError as ex:
                QtWidgets.QMessageBox.critical(self, self.tr("Performance JSON Load"),
                                               self.tr(f"JSON File missing '{ex}' entry"))
            except (FatalUserError, ValueError) as ex:
                QtWidgets.QMessageBox.critical(self, self.tr("Performance JSON Load"),
                                               self.tr(f"JSON File has Syntax Problems:\n{ex}"))


from decimal import Decimal

import graphene
from django_filters import FilterSet, OrderingFilter
from graphene import relay
from graphene_django.filter import DjangoFilterConnectionField
from graphene_django.types import DjangoObjectType
from graphene_file_upload.scalars import Upload
from graphql import GraphQLError
from graphql_jwt.decorators import login_required
from graphql_relay.node.node import from_global_id, to_global_id

from .models import BilbyJob, Label, FileDownloadToken, BilbyJobUploadToken
from .status import JobStatus
from .types import JobStatusType, BilbyJobCreationResult, JobParameterInput, JobParameterOutput, JobIniInput, \
    JobDetailsInput
from .utils.db_search.db_search import perform_db_search
from .utils.derive_job_status import derive_job_status
from .utils.gen_parameter_output import generate_parameter_output
from .utils.jobs.request_file_download_id import request_file_download_ids
from .utils.jobs.request_job_filter import request_job_filter
from .views import create_bilby_job, update_bilby_job, create_bilby_job_from_ini_string, upload_bilby_job

class LabelType(DjangoObjectType):
    class Meta:
        model = Label
        interfaces = (relay.Node,)

class UserBilbyJobFilter(FilterSet):
    class Meta:
        model = BilbyJob
        fields = '__all__'

    order_by = OrderingFilter(
        fields=(
            ('last_updated', 'lastUpdated'),
            ('name', 'name'),
        )
    )

    @property
    def qs(self):
        return BilbyJob.user_bilby_job_filter(super(UserBilbyJobFilter, self).qs, self)

class PublicBilbyJobFilter(FilterSet):
    class Meta:
        model = BilbyJob
        fields = '__all__'

    order_by = OrderingFilter(
        fields=(
            ('last_updated', 'last_updated'),
            ('name', 'name'),
        )
    )

    @property
    def qs(self):
        return BilbyJob.public_bilby_job_filter(super(PublicBilbyJobFilter, self).qs, self)

class BilbyJobNode(DjangoObjectType):
    class Meta:
        model = BilbyJob
        convert_choices_to_enum = False
        interfaces = (relay.Node,)

    job_status = graphene.Field(JobStatusType)
    last_updated = graphene.String()
    params = graphene.Field(JobParameterOutput)
    labels = graphene.List(LabelType)

    @classmethod
    def get_queryset(parent, queryset, info):
        return BilbyJob.bilby_job_filter(queryset, info)

    def resolve_last_updated(parent, info):
        return parent.last_updated.strftime("%Y-%m-%d %H:%M:%S UTC")

    def resolve_params(parent, info):
        return generate_parameter_output(parent)

    def resolve_labels(parent, info):
        return parent.labels.all()

    def resolve_job_status(parent, info):
        # Uploaded jobs are always complete
        if parent.is_uploaded_job:
            return {
                "name": JobStatus.display_name(JobStatus.COMPLETED),
                "number": JobStatus.COMPLETED,
                "date": parent.creation_time
            }

        try:
            # Get job details from the job controller
            _, jc_jobs = request_job_filter(
                info.context.user.user_id,
                ids=[parent.job_controller_id]
            )

            status_number, status_name, status_date = derive_job_status(jc_jobs[0]["history"])

            return {
                "name": status_name,
                "number": status_number,
                "date": status_date.strftime("%Y-%m-%d %H:%M:%S UTC")
            }
        except Exception:
            return {
                "name": "Unknown",
                "number": 0,
                "data": "Unknown"
            }

class UserDetails(graphene.ObjectType):
    username = graphene.String()

    def resolve_username(parent, info):
        return "Todo"

class BilbyResultFile(graphene.ObjectType):
    path = graphene.String()
    is_dir = graphene.Boolean()
    file_size = graphene.Decimal()
    download_token = graphene.String()

class BilbyResultFiles(graphene.ObjectType):
    class Meta:
        interfaces = (relay.Node,)

    class Input:
        job_id = graphene.ID()

    files = graphene.List(BilbyResultFile)
    is_uploaded_job = graphene.Boolean()

class BilbyPublicJobNode(graphene.ObjectType):
    user = graphene.String()
    name = graphene.String()
    job_status = graphene.Field(JobStatusType)
    labels = graphene.List(LabelType)
    description = graphene.String()
    timestamp = graphene.String()
    id = graphene.ID()

class BilbyPublicJobConnection(relay.Connection):
    class Meta:
        node = BilbyPublicJobNode

class GenerateBilbyJobUploadToken(graphene.ObjectType):
    token = graphene.String()

class Query(object):
    bilby_job = relay.Node.Field(BilbyJobNode)
    bilby_jobs = DjangoFilterConnectionField(BilbyJobNode, filterset_class=UserBilbyJobFilter)
    public_bilby_jobs = relay.ConnectionField(
        BilbyPublicJobConnection,
        search=graphene.String(),
        time_range=graphene.String()
    )

    all_labels = graphene.List(LabelType)

    bilby_result_files = graphene.Field(BilbyResultFiles, job_id=graphene.ID(required=True))

    gwclouduser = graphene.Field(UserDetails)

    generate_bilby_job_upload_token = graphene.Field(GenerateBilbyJobUploadToken)

    @login_required
    def resolve_generate_bilby_job_upload_token(self, info, **kwargs):
        user = info.context.user

        # Create a job upload token
        token = BilbyJobUploadToken.create(user)

        # Return the generated token
        return GenerateBilbyJobUploadToken(token=str(token.token))

    @login_required
    def resolve_all_labels(self, info, **kwargs):
        return Label.all()

    @login_required
    def resolve_public_bilby_jobs(self, info, **kwargs):
        # Perform the database search
        success, jobs = perform_db_search(info.context.user, kwargs)
        if not success:
            return []

        # Parse the result in to graphql objects
        result = []
        for job in jobs:
            bilby_job = BilbyJob.get_by_id(job['job']['id'], info.context.user)
            result.append(
                BilbyPublicJobNode(
                    user=f"{job['user']['firstName']} {job['user']['lastName']}",
                    name=job['job']['name'],
                    description=job['job']['description'],
                    job_status=JobStatusType(
                        name=JobStatus.display_name(
                            JobStatus.COMPLETED if bilby_job.is_uploaded_job else job['history'][0]['state']
                        ),
                        number=JobStatus.COMPLETED if bilby_job.is_uploaded_job else job['history'][0]['state'],
                        date=bilby_job.creation_time if bilby_job.is_uploaded_job else job['history'][0]['timestamp']
                    ),
                    labels=bilby_job.labels.all(),
                    timestamp=bilby_job.creation_time if bilby_job.is_uploaded_job else job['history'][0]['timestamp'],
                    id=to_global_id("BilbyJobNode", job['job']['id'])
                )
            )

        # Nb. The perform_db_search function currently requests one extra record than kwargs['first'].
        # This triggers the ArrayConnection used by returning the result array to correctly set
        # hasNextPage correctly, such that infinite scroll works as expected.
        return result

    @login_required
    def resolve_gwclouduser(self, info, **kwargs):
        return info.context.user

    @login_required
    def resolve_bilby_result_files(self, info, **kwargs):
        # Get the model id of the bilby job
        _, job_id = from_global_id(kwargs.get("job_id"))

        # Try to look up the job with the id provided
        job = BilbyJob.get_by_id(job_id, info.context.user)

        # Fetch the file list from the job controller
        success, files = job.get_file_list()
        if not success:
            raise Exception("Error getting file list. " + str(files))

        # Generate download tokens for the list of files
        paths = [f['path'] for f in filter(lambda x: not x['isDir'], files)]
        tokens = FileDownloadToken.create(job, paths)

        # Generate a dict that can be used to query the generated tokens
        token_dict = {tk.path: tk.token for tk in tokens}

        # Build the resulting file list and send it back to the client
        result = [
            BilbyResultFile(
                path=f["path"],
                is_dir=f["isDir"],
                file_size=Decimal(f["fileSize"]),
                download_token=token_dict[f["path"]] if f["path"] in token_dict else None
            )
            for f in files
        ]

        return BilbyResultFiles(
            files=result,
            is_uploaded_job=job.is_uploaded_job
        )

class BilbyJobMutation(relay.ClientIDMutation):
    class Input:
        params = JobParameterInput()

    result = graphene.Field(BilbyJobCreationResult)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, params):
        user = info.context.user

        # Create the bilby job
        bilby_job = create_bilby_job(user, params)

        # Convert the bilby job id to a global id
        job_id = to_global_id("BilbyJobNode", bilby_job.id)

        # Return the bilby job id to the client
        return BilbyJobMutation(
            result=BilbyJobCreationResult(job_id=job_id)
        )

class BilbyJobFromIniStringMutation(relay.ClientIDMutation):
    class Input:
        params = JobIniInput()

    result = graphene.Field(BilbyJobCreationResult)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, params):
        user = info.context.user

        # Create the bilby job
        bilby_job = create_bilby_job_from_ini_string(user, params)

        # Convert the bilby job id to a global id
        job_id = to_global_id("BilbyJobNode", bilby_job.id)

        # Return the bilby job id to the client
        return BilbyJobFromIniStringMutation(
            result=BilbyJobCreationResult(job_id=job_id)
        )

class UpdateBilbyJobMutation(relay.ClientIDMutation):
    class Input:
        job_id = graphene.ID(required=True)
        private = graphene.Boolean(required=False)
        labels = graphene.List(graphene.String, required=False)

    result = graphene.String()

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user

        job_id = kwargs.pop("job_id")

        # Update privacy of bilby job
        message = update_bilby_job(from_global_id(job_id)[1], user, **kwargs)

        # Return the bilby job id to the client
        return UpdateBilbyJobMutation(
            result=message
        )

class GenerateFileDownloadIds(relay.ClientIDMutation):
    class Input:
        job_id = graphene.ID(required=True)
        download_tokens = graphene.List(graphene.String, required=True)

    result = graphene.List(graphene.String)

    @classmethod
    @login_required
    def mutate_and_get_payload(cls, root, info, job_id, download_tokens):
        user = info.context.user

        # Get the job these file downloads are for
        job = BilbyJob.get_by_id(from_global_id(job_id)[1], user)

        # Verify the download tokens and get the paths
        paths = FileDownloadToken.get_paths(job, download_tokens)

        # Check that all tokens were found
        if None in paths:
            raise GraphQLError("At least one token was invalid or expired.")

        # For uploaded jobs, we can just return the exact some download tokens - this function is basically a no-op
        # for uploaded jobs
        if job.is_uploaded_job:
            return GenerateFileDownloadIds(
                result=download_tokens
            )

        # Request the list of file download ids from the list of paths
        # Only the original job author may generate a file download id
        success, result = request_file_download_ids(
            job,
            paths
        )

        # Report the error if there is one
        if not success:
            raise GraphQLError(result)

        # Return the list of file download ids
        return GenerateFileDownloadIds(
            result=result
        )

class UploadBilbyJobMutation(relay.ClientIDMutation):
    class Input:
        upload_token = graphene.String()
        details = JobDetailsInput()
        job_file = Upload(required=True)

    result = graphene.Field(BilbyJobCreationResult)

    @classmethod
    def mutate_and_get_payload(cls, root, info, upload_token, details, job_file):
        # Get the token being used to perform the upload - this will return None if the token doesn't exist or
        # is expired
        token = BilbyJobUploadToken.get_by_token(upload_token)
        if not token:
            raise GraphQLError("Job upload token is invalid or expired.")

        # Try uploading the bilby job
        bilby_job = upload_bilby_job(token, details, job_file)

        # Convert the bilby job id to a global id
        job_id = to_global_id("BilbyJobNode", bilby_job.id)

        # Return the bilby job id to the client
        return BilbyJobMutation(
            result=BilbyJobCreationResult(job_id=job_id)
        )

class Mutation(graphene.ObjectType):
    new_bilby_job = BilbyJobMutation.Field()
    new_bilby_job_from_ini_string = BilbyJobFromIniStringMutation.Field()
    update_bilby_job = UpdateBilbyJobMutation.Field()
    generate_file_download_ids = GenerateFileDownloadIds.Field()
    upload_bilby_job = UploadBilbyJobMutation.Field()


from grpc.beta import implementations
import numpy
import traceback

import tensorflow as tf

from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2

from flask_restplus import Resource, abort

from monocker_api.api.restplus import restplus_api
from monocker_api.api.models import getModel
from monocker_api.db.data_models import PredictionRequest
from monocker_api import settings

#==============================================================================
# helper functions
#==============================================================================
def getMonockerModelStub(host, port):
  try:
    channel = implementations.insecure_channel(host, int(port))
    stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)
  except Exception as e:
    print("===========================================================")
    print("Encountered error while requesting gRPC connection.")
    print("Error: ")
    print(e)
    traceback.print_exc()
    print("===========================================================")
    stub = None
  return stub

def getServingRequest(model, payload):
  request = predict_pb2.PredictRequest()
  request.model_spec.name = model['model_name']
  request.model_spec.signature_name = model['model_signature']

  request.inputs['images'].CopyFrom(
    tf.contrib.util.make_tensor_proto(
      payload['model_input'],
      shape=payload['model_input_shape']
    )
  )

  return request
#==============================================================================

#==============================================================================
# Models API
#==============================================================================
# define namespace
api = restplus_api.namespace(
  'predict',
  description="Operations related to requesting model evaluations"
)

# Define /models route handlers
@api.route('/')
class Models(Resource):
  @api.response(501, 'Error in model computation')
  @api.response(403, 'Could not connect to tf serving server')
  @api.response(404, 'Model not found.')
  @api.response(201, 'Successfully retrieved model evaluation.')
  @api.expect(PredictionRequest, validate=False, required=True)
  def post(self):
    # get inputs
    payload = restplus_api.payload

    # get model
    model = getModel(payload['model_name'])
    if model is None:
      return 'Model not found.', 404

    # get request
    model['model_signature'] = payload['model_signature']
    serving_request = getServingRequest(model, payload)

    # get stub
    stub = getMonockerModelStub(model['ip_address'], model['port'])
    if stub is None:
      return 'Could not connect to tf serving server', 403

    # make grpc prediction request then return results
    try:
      prediction = stub.Predict(serving_request, 5.0)
      model_response = list(prediction.outputs['scores'].float_val)
      return {'model_response': model_response}, 201

    except Exception as e:
      return str(e), 501

#==============================================================================


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

import torch
import torch.utils.data as data
from torchvision import transforms
import os
import pickle
import random
import pdb
import sys
import json
from PIL import Image

sys.path.insert(0, '.')

import numpy as np
from skimage import io

def Context_dataset(args, embedding_size):
    # Random seed
    np.random.seed(args.seed)

    # Getting the classes and annotations
    # ******
    data_path = args.data_path
    with open(data_path+'/Context/data/split_'+ str(args.split) +'.json','r') as fp:
        gt_annotations = json.load(fp)

    # Load Embedding according to OCR
    if args.embedding == 'w2vec' or args.embedding == 'fasttext' or args.embedding == 'glove' or  args.embedding == 'bert':
        if args.ocr == 'google_ocr':
            with open(data_path + '/Context/' + args.ocr + '/text_embeddings/Context_' + args.embedding + '.json', 'r') as fp:
                text_embedding = json.load(fp)
        else:
            with open(data_path + '/Context/' + args.ocr + '/text_embeddings/Context_' + args.embedding + '.pickle','rb') as fp:
                text_embedding = pickle.load(fp)
    elif args.embedding =='phoc':
        text_embedding = {'embedding':'phoc'}
    elif args.embedding == 'fisher':
        text_embedding = {'embedding':'fisher'}
    else:
        print('OCR SELECTED NOT IMPLEMENTED')

    # Load Local features from Faster R-CNN VG

    with open(args.data_path + '/Context/context_local_feats.npy', 'rb') as fp:
        local_feats = np.load(fp, encoding='bytes')

    # Create img_name to index of local features
    with open(args.data_path + '/Context/context_local_feats_image_ids.txt', 'r') as fp:
        image_ids = fp.readlines()
    image_name2features_index = {}
    for item in image_ids:
        img_name = item.strip().split(',')[0].split('/')[-1].replace('\'', '')
        idx = item.strip().split(',')[1].replace(')', '').replace(' ','')
        image_name2features_index[img_name] = idx

    # BBOXES LOADING FOR TEXT FEATURES
    # Load BBOXES of Scene Text
    with open(data_path + '/Context/google_ocr/bboxes/Context_bboxes.json', 'r') as fp:
        text_bboxes = json.load(fp)

    # Load BBOXES of Local Visual Features
    with open(data_path + '/Context/context_bboxes.npy', 'rb') as fp:
        local_bboxes = np.load(fp, encoding='bytes')

    # Data Loaders

    train_transform = transforms.Compose([
        transforms.Resize((256, 256)),
        transforms.RandomRotation(degrees=15),
        transforms.ColorJitter(),
        transforms.RandomHorizontalFlip(),
        transforms.RandomCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406],
                             std=[0.229, 0.224, 0.225])
    ])

    train_loader = Context_Train(args, gt_annotations, text_embedding, embedding_size, local_feats, image_name2features_index, text_bboxes, local_bboxes, train_transform)

    test_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406],
                             std=[0.229, 0.224, 0.225])
    ])

    test_loader = Context_Test(args, gt_annotations, text_embedding, embedding_size, local_feats, image_name2features_index, text_bboxes, local_bboxes, test_transform)

    return train_loader, test_loader, gt_annotations, text_embedding

class Context_Train(data.Dataset):
    def __init__(self, args, gt_annotations, text_embedding, embedding_size, local_feats, image_name2features_index, text_bboxes, local_bboxes, transform=None):

        self.args = args
        self.gt_annotations = gt_annotations
        self.text_embedding = text_embedding
        self.embedding_size = embedding_size
        self.transform = transform
        self.image_list = list(gt_annotations['train'].keys())

        self.image_name2features_index = image_name2features_index
        self.local_feats = local_feats
        self.text_bboxes = text_bboxes
        self.local_bboxes = local_bboxes

    def __len__(self):
        return len(self.gt_annotations['train'])

    def __getitem__(self, index):
        data_path = self.args.data_path
        assert index <= len(self), 'index range error'

        image_name = self.image_list[index].rstrip()
        image_path = data_path+'/Context/data/JPEGImages/' + image_name
        img = Image.open(image_path).convert('RGB')
        if self.transform:
            img = self.transform(img)

        img_class = self.gt_annotations['train'][image_name]
        label = np.zeros(28)
        label[int(img_class) - 1] = 1
        label = torch.from_numpy(label)
        label = label.type(torch.FloatTensor)

        if self.args.embedding == 'w2vec' or self.args.embedding == 'fasttext' or self.args.embedding == 'glove' or self.args.embedding == 'bert':
            text_embedding = np.asarray(self.text_embedding[image_name])
        elif self.args.embedding == 'phoc':
            with open (data_path + '/Context/yolo_phoc/'+image_name[:-3]+'json') as fp:
                phocs = json.load(fp)
                text_embedding = np.resize(phocs, (np.shape(phocs)[0], 604))
        elif self.args.embedding == 'fisher':
            if self.args.ocr == 'yolo_phoc':
                relative_path = '/Context/old_fisher_vectors/'
            elif self.args.ocr == 'e2e_mlt':
                relative_path = '/Context/fasttext_fisher/'
            else: print('Not Implemented')
            with open (data_path + relative_path +image_name[:-3]+'json')as fp:
                fisher_vector = json.load(fp)
                text_embedding = np.resize(fisher_vector, (1, 38400))
        # FISHER VECTORS DO NOT NEED MAX TEXTUAL
        if self.args.embedding != 'fisher':
            text_features = np.zeros((self.args.max_textual, self.embedding_size))
            if np.shape(text_embedding)[0] == 0:
                text_embedding = np.zeros((1,self.embedding_size))
            elif np.shape(text_embedding)[0] > self.args.max_textual:
                text_embedding = text_embedding[0:self.args.max_textual]
            text_features[:len(text_embedding)] = text_embedding
        else:
            text_features = text_embedding

        text_features = torch.from_numpy(text_features)
        text_features = text_features.type(torch.FloatTensor)

        # SCENE TEXT BBOXES ONLY FOR GOOGLE OCR
        text_bboxes = np.asarray(self.text_bboxes[image_name])
        if self.args.ocr == 'google_ocr':
            text_bboxes_features = np.zeros((self.args.max_textual, 4))
            if np.shape(text_bboxes)[0] == 0:
                text_bboxes = np.zeros((1, 4))
            elif np.shape(text_bboxes)[0] > self.args.max_textual:
                text_bboxes = text_bboxes[0:self.args.max_textual]
            text_bboxes_features[:len(text_bboxes)] = text_bboxes
        else:
            # NO BBOXES FOR OTHER OCRs
            text_bboxes_features = np.zeros((self.args.max_textual, 4))
        text_bboxes_features = torch.from_numpy(text_bboxes_features)
        text_bboxes_features = text_bboxes_features.type(torch.FloatTensor)

        # LOCAL VISUAL FEATURES
        local_features_index = self.image_name2features_index[image_name]
        local_features = self.local_feats[int(local_features_index)]
        local_features = torch.from_numpy(local_features[:int(self.args.max_visual)][:])
        local_features = local_features.type(torch.FloatTensor)
        # LOCAL VISUAL BBOXES
        local_bboxes_features = self.local_bboxes[int(local_features_index)]
        local_bboxes_features = torch.from_numpy(local_bboxes_features[:int(self.args.max_visual)][:])
        local_bboxes_features = local_bboxes_features.type(torch.FloatTensor)

        return img, label, text_features, local_features, text_bboxes_features, local_bboxes_features, image_name

class Context_Test(data.Dataset):
    def __init__(self, args, gt_annotations, text_embedding, embedding_size, local_feats, image_name2features_index, text_bboxes, local_bboxes, transform=None):
        self.args = args
        self.gt_annotations = gt_annotations
        self.text_embedding = text_embedding
        self.embedding_size = embedding_size
        self.transform = transform
        self.image_list = list(gt_annotations['test'].keys())

        self.image_name2features_index = image_name2features_index
        self.local_feats = local_feats
        self.text_bboxes = text_bboxes
        self.local_bboxes = local_bboxes

    def __len__(self):
        return len(self.gt_annotations['test'])

    def __getitem__(self, index):
        data_path = self.args.data_path
        assert index <= len(self), 'index range error'
        image_name = self.image_list[index].rstrip()
        image_path = data_path+ '/Context/data/JPEGImages/' + image_name
        img = Image.open(image_path).convert('RGB')
        if self.transform:
            img = self.transform(img)

        img_class = self.gt_annotations['test'][image_name]
        label = np.zeros(28)
        label[int(img_class) - 1] = 1
        label = torch.from_numpy(label)
        label = label.type(torch.FloatTensor)

        if self.args.embedding == 'w2vec' or self.args.embedding == 'fasttext' or self.args.embedding == 'glove' or self.args.embedding == 'bert':
            text_embedding = np.asarray(self.text_embedding[image_name])
        elif self.args.embedding == 'phoc':
            with open (data_path + '/Context/yolo_phoc/'+image_name[:-3]+'json') as fp:
                phocs = json.load(fp)
                text_embedding = np.resize(phocs, (np.shape(phocs)[0], 604))

        elif self.args.embedding == 'fisher':
            if self.args.ocr == 'yolo_phoc':
                relative_path = '/Context/old_fisher_vectors/'
            elif self.args.ocr == 'e2e_mlt':
                relative_path = '/Context/fasttext_fisher/'
            else: print('Not Implemented')
            with open (data_path + relative_path +image_name[:-3]+'json')as fp:
                fisher_vector = json.load(fp)
                text_embedding = np.resize(fisher_vector, (1, 38400))
        # FISHER VECTORS DO NOT NEED MAX TEXTUAL
        if self.args.embedding != 'fisher':
            text_features = np.zeros((self.args.max_textual, self.embedding_size))
            if np.shape(text_embedding)[0] == 0:
                text_embedding = np.zeros((1,self.embedding_size))
            elif np.shape(text_embedding)[0] > self.args.max_textual:
                text_embedding = text_embedding[0:self.args.max_textual]
            text_features[:len(text_embedding)] = text_embedding
        else:
            text_features = text_embedding

        text_features = torch.from_numpy(text_features)
        text_features = text_features.type(torch.FloatTensor)
        # SCENE TEXT BBOXES ONLY FOR GOOGLE OCR
        text_bboxes = np.asarray(self.text_bboxes[image_name])
        if self.args.ocr == 'google_ocr':
            text_bboxes_features = np.zeros((self.args.max_textual, 4))
            if np.shape(text_bboxes)[0] == 0:
                text_bboxes = np.zeros((1, 4))
            elif np.shape(text_bboxes)[0] > self.args.max_textual:
                text_bboxes = text_bboxes[0:self.args.max_textual]
            text_bboxes_features[:len(text_bboxes)] = text_bboxes
        else:
            # NO BBOXES FOR OTHER OCRs
            text_bboxes_features = np.zeros((self.args.max_textual, 4))
        text_bboxes_features = torch.from_numpy(text_bboxes_features)
        text_bboxes_features = text_bboxes_features.type(torch.FloatTensor)

        # LOCAL VISUAL FEATURES
        local_features_index = self.image_name2features_index[image_name]
        local_features = self.local_feats[int(local_features_index)]
        local_features = torch.from_numpy(local_features[:int(self.args.max_visual)][:])
        local_features = local_features.type(torch.FloatTensor)
        # LOCAL VISUAL BBOXES
        local_bboxes_features = self.local_bboxes[int(local_features_index)]
        local_bboxes_features = torch.from_numpy(local_bboxes_features[:int(self.args.max_visual)][:])
        local_bboxes_features = local_bboxes_features.type(torch.FloatTensor)

        return img, label, text_features, local_features, text_bboxes_features, local_bboxes_features, image_name


from .voxel_set_abstraction import VoxelSetAbstraction
from .def_voxel_set_abstraction import DefVoxelSetAbstraction

__all__ = {
    'VoxelSetAbstraction': VoxelSetAbstraction,
    'DefVoxelSetAbstraction': DefVoxelSetAbstraction
}


# Copyright Contributors to the Rez project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

'''
Bundle a context and its packages into a relocatable dir.
'''
from __future__ import print_function

import os
import os.path
import sys

def setup_parser(parser, completions=False):
    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        "-s", "--skip-non-relocatable", action="store_true",
        help="leave non-relocatable packages non-bundled, rather than raise an error")
    group.add_argument(
        "-f", "--force", action="store_true",
        help="bundle package even if it isn't relocatable (use at your own risk)")
    group.add_argument(
        "-n", "--no-lib-patch", action="store_true",
        help="don't apply library patching within the bundle")
    parser.add_argument(
        "RXT",
        help="context to bundle")
    parser.add_argument(
        "DEST_DIR",
        help="directory to create bundle in; must not exist")

def command(opts, parser, extra_arg_groups=None):
    from rez.utils.logging_ import print_error
    from rez.bundle_context import bundle_context
    from rez.resolved_context import ResolvedContext

    rxt_filepath = os.path.abspath(os.path.expanduser(opts.RXT))
    dest_dir = os.path.abspath(os.path.expanduser(opts.DEST_DIR))

    # sanity checks
    if not os.path.exists(rxt_filepath):
        print_error("File does not exist: %s", rxt_filepath)
        sys.exit(1)

    context = ResolvedContext.load(rxt_filepath)

    bundle_context(
        context=context,
        dest_dir=dest_dir,
        force=opts.force,
        skip_non_relocatable=opts.skip_non_relocatable,
        verbose=opts.verbose,
        patch_libs=(not opts.no_lib_patch)
    )


class CogTV:
    def __init__(self):
        pass

    def setScreen(self, scene):
        pass


#!/usr/bin/python2.7
# -*- coding: UTF-8 -*-
'''
Created on 2018年6月15日

@author: zhaohongxing
'''
import os

from PyQt5.Qt import Qt
from PyQt5.Qt import QIcon,QStandardItemModel,QStandardItem
'''
from PyQt5 import QtGui
'''
from PyQt5.QtWidgets import QTableView,QVBoxLayout,QDialog,QPushButton
from PyQt5.QtCore import QSize, pyqtSignal
import wechatutil

class ContactListWindow(QDialog):
    WIDTH = 600
    membersConfirmed = pyqtSignal(str)

    def __init__(self,member_list,parent = None):
        super(ContactListWindow,self).__init__(parent)
        self.setModal(True)
        self.user_home = os.path.expanduser('~')
        self.app_home = self.user_home + '/.wechat/'
        self.head_home = ("%s/heads"%(self.app_home))
        self.cache_home = ("%s/cache/"%(self.app_home))
        self.cache_image_home = "%s/image/"%(self.cache_home)
        self.contact_head_home = ("%s/contact/"%(self.head_home))
        self.default_head_icon = './resource/images/default.png'
        self.members = member_list
        self.membersTable = QTableView()
        self.membersTable.horizontalHeader().setStretchLastSection(True)
        self.membersTable.verticalHeader().setDefaultSectionSize(60)
        #self.membersTable.horizontalHeader().setDefaultSectionSize(60)
        self.membersTable.setColumnWidth(0, 10);
        self.membersTable.setColumnWidth(1, 60);
        self.membersTable.verticalHeader().setVisible(False)
        self.membersTable.horizontalHeader().setVisible(False)
        #confirm
        self.confirm = QPushButton(wechatutil.unicode("確定"),self)
        self.membersTableModel = QStandardItemModel(0,2)
        self.membersTableModel.itemChanged.connect(self.itemChanged)
        self.initinal_member_list_widget()
        mainLayout=QVBoxLayout()
        mainLayout.addWidget(self.membersTable)
        mainLayout.addWidget(self.confirm)
        self.setLayout(mainLayout)
        #self.membersTable.clicked.connect(self.contact_item_clicked)
        self.confirm.clicked.connect(self.do_confirm)
        self.selectedRowCount = 0

    def itemChanged(self,item):
        if item.checkState() == Qt.Checked:
            self.selectedRowCount += 1
        else:
            self.selectedRowCount -= 1

        if self.selectedRowCount > 0:
            self.confirm.setText(wechatutil.unicode("確定(%d)"%(self.selectedRowCount)))
        else:
            self.confirm.setText(wechatutil.unicode("確定"))

    def initinal_member_list_widget(self):
        self.append_row(self.members, self.membersTableModel)
        self.membersTable.setModel(self.membersTableModel)
        self.membersTable.setIconSize(QSize(40,40))

    def append_row(self,members,data_model):
        for (i,member) in enumerate(members):
            cells = []
            user_name = member['UserName']
            user_name_cell = QStandardItem(user_name)
            user_name_cell.setCheckable(True)
            cells.append(user_name_cell)

            user_avatar = self.contact_head_home + member['UserName']+".jpg"
            if not os.path.exists(user_avatar):
                user_avatar = self.default_head_icon
            dn = member['DisplayName'] or member['NickName']
            if not dn:
                dn = member['NickName']
            item = QStandardItem(QIcon(user_avatar),wechatutil.unicode(dn))
            cells.append(item)
            data_model.appendRow(cells)

    def do_confirm(self):
        rowCount = self.membersTableModel.rowCount()
        selected_user_names = ""
        for row in range(rowCount):
            item = self.membersTableModel.item(row,0)
            if item.checkState() == Qt.Checked:
                index = self.membersTableModel.index(row,0)
                user_name_obj = self.membersTableModel.data(index)
                if user_name_obj:
                    user_name = user_name_obj
                    user = {}
                    user['UserName']=user_name
                    selected_user_names=selected_user_names+(user_name)
                    selected_user_names=selected_user_names+(";")

        if len(selected_user_names) > 0:
            dictt = {}
            dictt['UserNames']=selected_user_names
            self.membersConfirmed.emit(selected_user_names)

            self.close()