# -*- coding: UTF-8 -*-
from __future__ import print_function, division

import gdb
from gdb.unwinder import Unwinder
from gdb.FrameDecorator import FrameDecorator

import struct
import re
import itertools

from . import intf_dbg as intf
import andb.py23 as py23

inferior = gdb.selected_inferior()
arch = inferior.architecture().name()

class BasicTypes:

    ptr_t = gdb.lookup_type('void').pointer()

    u8_t = gdb.lookup_type('unsigned char')
    u16_t = gdb.lookup_type('unsigned short')
    u32_t = gdb.lookup_type('unsigned int')
    u64_t = gdb.lookup_type('unsigned long')

    s8_t = gdb.lookup_type('char')
    s16_t = gdb.lookup_type('short')
    s32_t = gdb.lookup_type('int')
    s64_t = gdb.lookup_type('long')

    u8p_t = u8_t.pointer()
    u16p_t = u16_t.pointer()
    u32p_t = u32_t.pointer()
    u64p_t = u64_t.pointer()

    s8p_t = s8_t.pointer()
    s16p_t = s16_t.pointer()
    s32p_t = s32_t.pointer()
    s64p_t = s64_t.pointer()

    p_double = gdb.lookup_type('double').pointer()

    """
    """
    #char16_t = gdb.lookup_type('char16_t')
    #char16p_t = char16_t.pointer()

    @classmethod
    def GetType(cls, typ):
        return Type(typ)

class Command(intf.Command, gdb.Command):
    _cxpr = None

    def __init__(self):
        gdb.Command.__init__(
            self,
            self._cxpr,
            gdb.COMMAND_USER,
            prefix=self._is_prefix)

    @classmethod
    def Register(cls):
        cls()

    def invoke(self, arg, tty):
        child_fn = getattr(self, 'Dispatch')
        return child_fn(arg)

    def complete(self, text, word):
        child_fn = getattr(self, 'Complete')
        return child_fn(text)

class Block(intf.Block):

    _address = None

    @property
    def address(self):
        return self._address

    def LoadPtr(self, off):
        #b = inferior.read_memory(self._address + off, 8)
        #return struct.unpack('Q', b)[0]
        return int(gdb.Value(self._address + off).cast(BasicTypes.u64p_t).dereference())

    def LoadU64(self, off):
        #b = inferior.read_memory(self._address + off, 8)
        #return struct.unpack('Q', b)[0]
        return int(gdb.Value(self._address + off).cast(BasicTypes.u64p_t).dereference())

    def LoadU32(self, off):
        #b = inferior.read_memory(self._address + off, 4)
        #return struct.unpack('I', b)[0]
        return int(gdb.Value(self._address + off).cast(BasicTypes.u32p_t).dereference())

    def LoadU16(self, off):
        #b = inferior.read_memory(self._address + off, 2)
        #return struct.unpack('H', b)[0]
        return int(gdb.Value(self._address + off).cast(BasicTypes.u16p_t).dereference())

    def LoadU8(self, off):
        #b = inferior.read_memory(self._address + off, 1)
        #return struct.unpack('B', b)[0]
        return int(gdb.Value(self._address + off).cast(BasicTypes.u8p_t).dereference())

    def LoadDouble(self, off):
        address = self._address + off
        return Target.ReadDouble(address)

    """ String function.
    """
    def GetCString(self, length=-1):
        return Target.ReadCStr(self.address, length=length)

    def LoadCString(self, off, length = -1):
        address = self.address + off
        return Target.ReadCStr(address, length=length)

    def LoadUString(self, off, length = -1):
        address = self.address + off
        return Target.ReadUStr(address, length=length)

class Value(intf.Value):

    # holds the gdb.Value()
    _I_value = None

    # save the address
    _I_address = None

    # cache is pointer
    _I_is_pointer = None

    def __init__(self, pyo_value=None):
        # holds the internal Value
        if isinstance(pyo_value, Value):
            self._I_value = pyo_value._I_value
            self._I_address = pyo_value._I_address
            self._I_is_pointer = pyo_value._I_is_pointer
        elif isinstance(pyo_value, gdb.Value):
            self._I_value = pyo_value
        elif pyo_value is None:
            self._I_value = None
        else:
            raise Exception

    def __getitem__(self, member_name_or_index):
        #print(type(member_name_or_index), member_name_or_index)
        return Value(self._I_value[member_name_or_index])

    @classmethod
    def CreateTypedAddress(cls, pyo_type, address):
        o = Value(gdb.Value(int(address)).cast(pyo_type._I_type))
        o._I_address = int(address)
        o._I_is_pointer = True
        return o

    @classmethod
    def CreateFromString(cls, type_name, address):
        t = Type.LookupType(type_name)
        if t is None:
            return None
        return cls.CreateTypedAddress(t, address)

    def Cast(self, pyo_type):
        return Value(self._I_value.cast(pyo_type._I_type))

    """ pointer and reference
    """

    @property
    def address(self):
        if self._I_address is not None:
            return self._I_address
        if self.is_pointer:
            self._I_address = int(self.__GetNonTypedefValue())
        else:
            o = self._I_value.address
            self._I_address = int(o)
        return self._I_address

    def IsPointerType(self):
        """ return True if is a pointer type (or typedefs)
        """
        # strip all the typedefs, check whether is pointer code.
        code = self._I_value.type.strip_typedefs().code
        #print('%s.type = %s' % (str(self), code))
        return code == gdb.TYPE_CODE_PTR

    is_pointer = property(IsPointerType)

    @property
    def size(self):
        return self._I_value.type.sizeof

    def GetType(self):
        return Type(self._I_value.type)

    def has(self, name):
        try:
            x = self[name]
            return x is not None
        except:
            return False

    def AddressOf(self):
        o = self._I_value.address
        if o is None:
            return None
        return Value(o)

    def Dereference(self):
        o = self._I_value.dereference()
        if o is None:
            return None
        return Value(o)

    def __GetNonTypedefValue(self):
        t = self._I_value.type.strip_typedefs()
        return Value(self._I_value.cast(t))

    """ String function.
    """
    def GetCString(self, length=-1):
        return Target.ReadCStr(self.address, length=length)

    def LoadCString(self, off, length = -1):
        address = self.address + off
        return Target.ReadCStr(address, length=length)

    def LoadUString(self, off, length = -1):
        address = self.address + off
        return Target.ReadUStr(address, length=length)

    """ Load functions.
    """
    def LoadType(self, off, typ):
        """ Load 'type' value from offset """
        v = Value.CreateFromString(typ, self.address + off)
        return v

    def LoadIntValue(self, off, typ):
        v = gdb.Value(self.address + off).cast(typ).dereference()
        return int(v)

    def LoadPtr(self, off):
        return self.LoadIntValue(off, BasicTypes.u64p_t)

    def LoadU8(self, off):
        return self.LoadIntValue(off, BasicTypes.u8p_t)

    def LoadU16(self, off):
        return self.LoadIntValue(off, BasicTypes.u16p_t)

    def LoadU32(self, off):
        return self.LoadIntValue(off, BasicTypes.u32p_t)

    def LoadU64(self, off):
        return self.LoadIntValue(off, BasicTypes.u64p_t)

    def LoadS8(self, off):
        return self.LoadIntValue(off, BasicTypes.s8p_t)

    def LoadS16(self, off):
        return self.LoadIntValue(off, BasicTypes.s16p_t)

    def LoadS32(self, off):
        return self.LoadIntValue(off, BasicTypes.s32p_t)

    def LoadS64(self, off):
        return self.LoadIntValue(off, BasicTypes.s64p_t)

    def LoadDouble(self, off):
        address = self.address + off
        return TarGet.ReadDouble(address)

    """ magic methods
    """
    def __eq__(self, other):

        # both Values, compare address
        if isinstance(other, Value):
            if self.address == other.address:
                return True

        # other is an integer
        elif isinstance(other, py23.integer_types):
            # self is pointer, compare address
            if self.is_pointer:
                if self.address == int(other):
                    return True

            # self is data, compare number
            else:
                if int(self) == int(other):
                    return True

        return False

    def __int__(self):
        """ GDB valpy_int implement return integer number if,
            1) is_floating_type(value)
            2) is_integral_type(value)
            3) type->code() == TYPE_CODE_PTR
            otherwise, a Exception('Connot convert value to int') will raise.
        """
        v = self.__GetNonTypedefValue()
        return int(v._I_value)

    def __add__(self, other):
        """
            C-style pointer arithmetic
            1) ptr + int, return
            2）
        """
        if self.is_pointer and isinstance(other, py23.integer_types):
            return Value(self._I_value + other)

        # number arithmetic
        return int(self) + int(other)

    def __sub__(self, other):
        if self.is_pointer and  isinstance(other, py23.integer_types):
            # C-Style Pointer sub
            return Value(self._I_value - other)

        # number arithmetic
        return int(self) - int(other)

    def __str__(self):
        return "(%s) %s" % (self._I_value.type, str(self._I_value))

class Type(intf.Type):

    def __init__(self, pyo_type = None):
        # holds the internal Type
        if pyo_type is None:
            self._I_type = None
        elif isinstance(pyo_type, Type):
            self._I_type = pyo_type._I_type
        elif isinstance(pyo_type, gdb.Type):
            self._I_type = pyo_type
        else:
            raise Exception

    @classmethod
    def LookupType(cls, type_name):
        try:
            t = gdb.lookup_type(type_name)
        except:
            return None
        return Type(t)

    def GetPointerType(self):
        return Type(self._I_type.pointer())

    def GetArrayType(self, size):
        return Type(self._I_type.array(size))

    def GetEnumMembers(self):
        out_list = []
        for i in self._I_type.fields():
            if i.enumval is None:
                continue
            nam = i.name.split('::')[-1]
            val = i.enumval
            out_list.append({"name": nam, "value": val})
        return out_list

    def SizeOf(self):
        return int(self._I_type.sizeof)

    def IsIntergralType(self):
        if self._I_type == gdb.TYPE_CODE_INT or \
            self._I_type == gdb.TYPE_CODE_ENUM or \
            self._I_type == gdb.TYPE_CODE_FLAGS or \
            self._I_type == gdb.TYPE_CODE_CHAR or \
            self._I_type == gdb.TYPE_CODE_RANGE or \
            self._I_type == gdb.TYPE_CODE_BOOL:
            return True
        return False

    def IsPointerType(self):
        code = self._I_type.strip_typedefs().code
        return code == TYPE_CODE_PTR

    def GetTemplateArgument(self, index):
        return Type(self._I_type.template_argument(index))

    def __str__(self):
        return str(self._I_type)

    def __eq__(self):
        pass

class Thread(intf.Thread):

    @property
    def tid(self):
        """
            InferiorThread.ptid
            [0] : Process ID
            [1] : LWPID
            [2] : Thread ID
        """
        return self._I_thread.ptid[1]

    @property
    def name(self):
        return self._I_thread.name

    def GetFrameTop(self):
        # by gdb.selected_frame()
        self._I_thread.switch()
        _frame = gdb.newest_frame()
        rob = Frame(_frame)
        return rob

    @classmethod
    def BacktraceCurrent(self, parser):
        v8_unwinder.Enable()
        f = gdb.newest_frame()
        for i in range(200):
            if f is None:
                break

            frame = Frame(f)
            v8f = parser(frame)
            if v8f:
                print("#%-2d %s" % (i, v8f.Description()))
            else:
                print("#%-2d %s" % (i, frame.Description()))
            f = f.older()
        v8_unwinder.Disable()

    @classmethod
    def GetV8Frames(self, parser):
        out = []
        v8_unwinder.Enable()
        f = gdb.newest_frame()
        for i in range(1000):
            if f is None:
                break

            frame = Frame(f)
            v8f = parser(frame)
            if v8f:
                out.append(v8f.Flatten())
            else:
                out.append(frame.Flatten())
            f = f.older()
        v8_unwinder.Disable()
        return out

    def GetEnviron(self):
        top = self.GetFrameTop()
        if top is None:
            return None

        sp = top.GetSP()
        if not sp:
            return None
        mmap = Target.GetMemoryRegions().Search(sp)
        if not mmap:
            return None

        names = []
        hit = 0
        addr = mmap.end_address - 8
        for i in range(4096*100):
            p = addr - i
            c = Target.ReadInt(p, 1)
            if c == 0:
                hit = hit + 1
            else:
                hit = 0
            if hit > 64:
                print("env: %x" %p)
                env_start = p + 64
                env_end = mmap.end_address - 8
                env_len = env_end - env_start
                names = Target.MemoryRead(env_start, env_len).decode('utf8').split('\0')
                names = filter(lambda x: x != "", names)
                break
        return names

class Symval(intf.Symval):
    pass

class Frame(intf.Frame):
    _decorate = None

    def GetSP(self):
        if arch == 'aarch64':
            return int(self._I_frame.read_register('sp').cast(BasicTypes.u64p_t))
        return int(self._I_frame.read_register('rsp').cast(BasicTypes.u64p_t))

    def GetPC(self):
        if arch == 'aarch64':
            return int(self._I_frame.read_register('pc').cast(BasicTypes.u64p_t))
        return int(self._I_frame.read_register('rip').cast(BasicTypes.u64p_t))

    def GetFP(self):
        if arch == 'aarch64':
            return int(self._I_frame.read_register('x29').cast(BasicTypes.u64p_t))
        return int(self._I_frame.read_register('rbp').cast(BasicTypes.u64p_t))

    def GetFunctionName(self):
        return self._I_frame.name()

    def Decorate(self):
        if self._decorate is None:
            self._decorate = FrameDecorator(self._I_frame)
        return self._decorate

    def GetArgs(self):
        dec = self.Decorate()
        if dec is None: return []
        out = []
        frame_args = dec.frame_args()
        if frame_args is None: return []
        for i in frame_args:
            out.append(Symval(i.symbol(), self._I_frame.read_var(i.symbol())))
        return out

    def GetLocals(self):
        dec = self.Decorate()
        if dec is None: return []
        out = []
        frame_locals = dec.frame_locals()
        if frame_locals is None: return []
        for i in frame_locals:
            #print(i)
            out.append(Symval(i.symbol(), i.value()))
        return out

    def GetPosition(self):
        dec = self.Decorate()
        line = dec.line()
        if line is None: line = 0
        return (dec.filename(), line)

class MemoryRegionInfo(intf.MemoryRegionInfo):
    pass

class MemoryRegions(intf.MemoryRegions):
    _I_regions = None

    """
        Sections Flags,
        ALLOC: Section will have space allocated in the process when loaded. Set for all sections except those containing debug information.
        LOAD: Section will be loaded from the file into the child process memory. Set for pre-initialized code and data, clear for .bss sections.
        RELOC: Section needs to be relocated before loading.
        READONLY: Section cannot be modified by the child process.
        CODE: Section contains executable code only.
        DATA: Section contains data only (no executable code).
        ROM: Section will reside in ROM.
        CONSTRUCTOR: Section contains data for constructor/destructor lists.
        HAS_CONTENTS: Section is not empty.
        NEVER_LOAD: An instruction to the linker to not output the section.
        COFF_SHARED_LIBRARY: A notification to the linker that the section contains COFF shared library information.
        IS_COMMON: Section contains common symbols.
    """

    @classmethod
    def LoadFromSection(cls):
        v = gdb.execute('maintenance info sections', to_string = True)
        for l in v.splitlines():
            s = '\[(\d+)\]\s+([a-zA-Z0-9]+)->([a-zA-Z0-9]+) at [a-zA-Z0-9]+: (\S+) (.*)'
            m = re.search(s, l)
            if m is None:
                continue

            start_address = int(m.group(2), 16)
            if start_address == 0:
                continue

            has_contents = 0
            v = MemoryRegionInfo.READ | MemoryRegionInfo.WRITE
            for i in m.group(5).split(' '):
                if i == 'CODE':
                    v |= MemoryRegionInfo.EXECUTE
                    v &= (~MemoryRegionInfo.WRITE)
                elif i == 'READONLY':
                    v &= (~MemoryRegionInfo.WRITE)
                elif i == 'HAS_CONTENTS':
                    has_contents = 1
            if not has_contents:
                v = 0

            pyo = MemoryRegionInfo()
            pyo._I_mode = v
            #pyo._I_id = int(m.group(1))
            pyo._I_start_address = start_address
            pyo._I_end_address = int(m.group(3), 16)
            pyo._I_name = m.group(4)

            # add to _I_regions
            cls._I_regions.append(pyo)

    @classmethod
    def LoadFromProc(cls):
        """ for live process, we couldn't get memory maps from sections,
            we need read from proc filesystem.

            TBD: support attached process.
        """
        pass

    @classmethod
    def Load(cls):
        if cls._I_regions is None:
            cls._I_regions = []
            cls.LoadFromSection()
        return cls._I_regions

class ConvenienceVariables(object):

    @classmethod
    def Get(cls, name):
        return gdb.convenience_variable(name)

    @classmethod
    def Set(cls, name, value):
        print("(%s) $%s = %s" % (value.type, name, value))
        gdb.set_convenience_variable(name, value)

""" Target
"""
class Target(intf.Target):

    @classmethod
    def GetThreads(cls):
        rob = []
        inferior = gdb.selected_inferior()
        for i in inferior.threads():
            pyo = Thread()
            pyo._I_thread = i
            rob.insert(0, pyo)
        return rob

    @classmethod
    def GetCurrentThread(cls):
        _thread = gdb.selected_thread()
        if _thread is None:
            return None
        robj = Thread()
        robj._I_thread = _thread
        return robj

    @classmethod
    def GetMemoryRegions(cls):
        m = MemoryRegions()
        m.Load()
        return m

    @classmethod
    def AddDwfFile(cls, filename):
        gdb.execute("add-symbol-file '%s'" % filename)

    @classmethod
    def LoadRaw(cls, name):
        """ read raw value from GDB """
        try:
            v = gdb.parse_and_eval(name)
            return int(v)
        except:
            return None

    @classmethod
    def ReadSymbolAddress(cls, symbol_name):
        """ Read Symbol address
        """
        try:
            v = gdb.parse_and_eval("&'%s'" % symbol_name)
            return int(v)
        except:
            return None

    @classmethod
    def ReadSymbolValue(cls, symbol_name):
        """ Read Symbol address
        """
        try:
            v = gdb.parse_and_eval("'%s'" % symbol_name)
            return int(v)
        except:
            return None

    @classmethod
    def ReadCStr(cls, address, length=-1):
        """ decode value(char string pointer) to python string
        """
        if length == 0:
            return ''
        elif length > 0:
            # read bytes then decode
            s = Target.MemoryRead(address, length)
            return s.decode('utf8', 'ignore')
        else:
            # use gdb.Value.string()
            v = gdb.Value(address).cast(BasicTypes.s8p_t)
            return v.string('utf8', 'ignore')

    @classmethod
    def ReadUStr(cls, address, length=-1):
        """ decode value(char16_t string pointer) to python string
        """
        if length == 0:
            return ''
        elif length > 0:
            # read bytes then decode
            length *= 2
            s = Target.MemoryRead(address, length)
            return s.decode("utf-16", 'ignore')
        else:
            # use gdb.Value.string()
            v = gdb.Value(address).cast(BasicTypes.char16p_t)
            return v.string("utf-16", 'ignore')

    @classmethod
    def ReadInt(cls, addr, byte_size=8, is_sign=0):
        #if not isinstance(address, int):
        #    print("type error: ", type(address), address)
        #    raise Exception
        address = int(addr)

        if not 1 <= byte_size <= 8:
            raise Exception

        if is_sign:
            if byte_size == 1:
                t = BasicTypes.s8p_t
            elif byte_size == 2:
                t = BasicTypes.s16p_t
            elif byte_size == 4:
                t = BasicTypes.s32p_t
            elif byte_size == 8:
                t = BasicTypes.s64p_t
        else:
            if byte_size == 1:
                t = BasicTypes.u8p_t
            elif byte_size == 2:
                t = BasicTypes.u16p_t
            elif byte_size == 4:
                t = BasicTypes.u32p_t
            elif byte_size == 8:
                t = BasicTypes.u64p_t

        v = gdb.Value(address).cast(t).dereference()
        return int(v)

    @classmethod
    def MemoryRead(cls, address, size):
        inferior = gdb.selected_inferior()
        s = inferior.read_memory(address, size)
        return bytes(s)

    @classmethod
    def MemoryFind(cls, start, end, addr, byte_size=8):
        cmd = "find /g 0x%x, 0x%x, 0x%x" % (start, end, addr)
        out = ""
        try:
            out = gdb.execute(cmd, False, True)
        except Exception as e:
            print(start, end, e)
        lines = out.splitlines(False)
        find = []
        for i in lines:
            if i.startswith('0x'):
                end = i.find(' ')
                if end > 0:
                    i = i[:end]
                find.append(int(i, 16))

        if len(find) > 0:
            return find
        return None

    @classmethod
    def ReadDouble(cls, address):
        t = BasicTypes.p_double
        v = gdb.Value(address).cast(t).dereference()
        return v

    @classmethod
    def LookupSymbol(cls, symbol_name):
        """ Inner use only, lldb don't have same api.
        """
        t, s = gdb.lookup_symbol(symbol_name)
        if t is None:
            return None
        v = t.value()
        return Value(v)

    @classmethod
    def MemoryDump(cls, file_to_save, start_address, end_address):
        v = gdb.execute('dump memory %s 0x%x 0x%x' % (file_to_save, start_address, end_address), to_string = True)
        return v

""" Stack Frames
"""
class AndbFrameFilter():

    def __init__(self):
        self.name = "AndbFrameFilter"
        self.priority = 100
        self.enabled = False
        gdb.frame_filters[self.name] = self

    def Enable(self):
        self.enabled = True

    def Disable(self):
        self.enabled = False

    def filter(self, frame_iter):
        #return itertools.imap(InlinedFrameDecorator, frame_iter)
        return AndbFrameIterator(frame_iter)

class AndbFrameIterator:
    """ filter out JS Frames
    """
    def __init__(self, it, paser):
        self.frame_iterator = it
        self.parser = paser

    def __iter__(self):
        return self

    def next(self):
        decorator = next(self.frame_iterator)
        frm = Frame(decorator.inferior_frame())

        v8f = self.parser(frm)
        if v8f:
            return V8FrameDecorator(decorator)
        return decorator

class SymValueWrapper():

    def __init__(self, symbol, value):
        self._sym = symbol
        self._val = value

    def value(self):
        return self._val

    def symbol(self):
        return self._sym

class V8FrameDecorator(FrameDecorator):

    def __init__(self, fobj):
        super(AndbFrameDecorator, self).__init__(fobj)

    def function(self):
        frame = self.inferior_frame()
        #print (frame)
        name = str(frame.name())

        #if frame.type() == gdb.INLINE_FRAME:
        name = name + " [inlined]"

        return name

    def frame_args(self):
        return [SymValueWrapper('Context', 0x3)]

    def frame_locals(self):
        return None

    def filename(self):
        return 'test.js'

    def line(self):
        return 0

class FrameId(object):
    """ caller frame id
    """

    def __init__(self, sp, pc):
        self._sp = sp
        self._pc = pc

    @property
    def sp(self):
        return self._sp

    @property
    def pc(self):
        return self._pc

if arch == "aarch64":
    # support for arm64
    class V8Unwinder(Unwinder):
        """ v8 unwinder based on Frame Pointer.
        """

        def __init__(self):
            super(V8Unwinder, self).__init__("V8Unwinder")
            self.enabled = False
            gdb.unwinder.register_unwinder(None, self, True)
            print("v8 unwinder registed.")

        def Enable(self):
            self.enabled = True
            gdb.invalidate_cached_frames()

        def Disable(self):
            self.enabled = False
            gdb.invalidate_cached_frames()

        def __call__(self, pending_frame):
            bp = pending_frame.read_register('x29')
            pc = pending_frame.read_register('pc')
            sp = pending_frame.read_register('sp')
            #gdb.write("bp(%x), sp(%x), pc(%x)\n"%(bp, sp, pc))

            # rbp used as frame pointer
            base = 8*1024*1024-1
            if int(bp) & ~base != int(sp) & ~base or int(bp) <= int(sp):
                return None

            if (bp == 0):
                return None

            caller_bp = bp.cast(gdb.lookup_type('long').pointer()).dereference()
            caller_pc = (bp+8).cast(gdb.lookup_type('long').pointer()).dereference()
            caller_sp = bp + 16

            # valid rbp
            if int(caller_bp) & ~base != int(sp) & ~base:
                return None

            frameid = FrameId(sp, pc)
            unwind_info = pending_frame.create_unwind_info(frameid)
            unwind_info.add_saved_register('pc', caller_pc)
            unwind_info.add_saved_register('sp', caller_sp)
            unwind_info.add_saved_register('x29', caller_bp)
            #unwind_info.level = 0
            return unwind_info
else:
    # default for x86_64
    class V8Unwinder(Unwinder):
        """ v8 unwinder based on Frame Pointer.
        """

        def __init__(self):
            super(V8Unwinder, self).__init__("V8Unwinder")
            self.enabled = False
            gdb.unwinder.register_unwinder(None, self, True)
            print("v8 unwinder registed.")

        def Enable(self):
            self.enabled = True
            gdb.invalidate_cached_frames()

        def Disable(self):
            self.enabled = False
            gdb.invalidate_cached_frames()

        def __call__(self, pending_frame):
            bp = pending_frame.read_register('rbp')
            pc = pending_frame.read_register('rip')
            sp = pending_frame.read_register('rsp')
            #gdb.write("bp(%x), sp(%x), pc(%x)\n"%(bp, sp, pc))

            # rbp used as frame pointer
            base = 8*1024*1024-1
            if int(bp) & ~base != int(sp) & ~base or int(bp) <= int(sp):
                return None

            if (bp == 0):
                return None

            caller_bp = bp.cast(gdb.lookup_type('long').pointer()).dereference()
            caller_pc = (bp+8).cast(gdb.lookup_type('long').pointer()).dereference()
            caller_sp = bp + 16

            # valid rbp
            if int(caller_bp) & ~base != int(sp) & ~base:
                return None

            frameid = FrameId(sp, pc)
            unwind_info = pending_frame.create_unwind_info(frameid)
            unwind_info.add_saved_register('rip', caller_pc)
            unwind_info.add_saved_register('rsp', caller_sp)
            unwind_info.add_saved_register('rbp', caller_bp)
            #unwind_info.level = 0
            return unwind_info

v8_unwinder = V8Unwinder()
#AndbFrameFilter

print("gdb debugger loaded")


''' UVa 11879 - Multiple of 17s '''
# https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3001
# Date: 2021-08-14 17:43:55
# Run time: 0.010
# Verdict: AC

from sys import stdin

for line in stdin:
    if line == '0\n':
        break

    a = int(line.strip())
    print(1 if a % 17 == 0 else 0)


from typing import Optional, List, Union
from dataclasses import dataclass

import io

import yaml

from conditions import T, Condition, NonEmptyString, AnyString, AnyList, AnyDict, NonEmptyList, AnyBool

class TestplanValidationError(ValueError):
    pass

def enforce(condition: Condition[T], value: T, variable_desc: str) -> None:
    if not condition.check(value):
        raise TestplanValidationError(f"{variable_desc} {condition.describe()}: {value}")

@dataclass
class TestCase():
    name: str
    context: str
    disabled: bool
    input: List[str]

@dataclass
class TestSuite():
    name: str
    disabled: bool
    tests: List[TestCase]
    executors: List[str]

@dataclass
class TestSuiteCollection():
    name: str
    path: str
    disabled: bool
    testSuites: List[TestSuite]
    children: List["TestSuiteCollection"]

def _trace_to_string(trace: List[Union[str, int]], finalElement: Optional[Union[str, int]] = None):
    trace_to_work_on = trace
    if finalElement is not None:
        trace_to_work_on = trace_to_work_on + [finalElement]

    result = "."
    for element in trace_to_work_on:
        if isinstance(element, str):
            if len(result) > 1:
                result += "."
            result += element
        if isinstance(element, int):
            result += f"[{element}]"

    return result

class TestPlan():
    tests: TestSuiteCollection
    _required_executors: Optional[List[str]] = None

    def parse_test_case(self, trace: List[Union[str, int]], yaml_dict: dict) -> TestCase:
        enforce(AnyDict(), yaml_dict, _trace_to_string(trace))
        name = yaml_dict.get("name", "")
        enforce(NonEmptyString(), name, _trace_to_string(trace, "name"))
        context = yaml_dict.get("context", "")
        enforce(AnyString(), context, _trace_to_string(trace, "context"))
        disabled = yaml_dict.get("disabled", False)
        enforce(AnyBool(), disabled, _trace_to_string(trace, "disabled"))
        input_yaml = yaml_dict.get("input", [])
        enforce(AnyList(), input_yaml, _trace_to_string(trace, "input"))

        input: List[str] = list()
        for i, input_line in enumerate(input_yaml):
            new_trace = trace + ["input", i]
            enforce(NonEmptyString(), input_line, _trace_to_string(new_trace))
            input.append(input_line)

        return TestCase(name, context, disabled, input)

    def parse_test_suite(self, trace: List[Union[str, int]], yaml_dict: dict) -> TestSuite:
        enforce(AnyDict(), yaml_dict, _trace_to_string(trace))
        name = yaml_dict.get("name", "")
        enforce(NonEmptyString(), name, _trace_to_string(trace, "name"))
        disabled = yaml_dict.get("disabled", False)
        enforce(AnyBool(), disabled, _trace_to_string(trace, "disabled"))
        tests_yaml = yaml_dict.get("tests", [])
        enforce(NonEmptyList(), tests_yaml, _trace_to_string(trace, "tests"))
        executors_yaml = yaml_dict.get("executors", [])
        enforce(NonEmptyList(), executors_yaml, _trace_to_string(trace, "executors"))

        tests: List[TestCase] = list()
        for i, test_dict in enumerate(tests_yaml):
            new_trace = trace + ["tests", i]
            tests.append(self.parse_test_case(new_trace, test_dict))

        executors: List[str] = list()
        for i, executor in enumerate(executors_yaml):
            new_trace = trace + ["executors", i]
            enforce(NonEmptyString(), executor, _trace_to_string(new_trace))
            executors.append(executor)

        return TestSuite(name, disabled, tests, executors)

    def parse_test_suite_collection(self, trace: List[Union[str, int]], yaml_dict: dict) -> TestSuiteCollection:
        enforce(AnyDict(), yaml_dict, _trace_to_string(trace))
        name = yaml_dict.get("name", "")
        enforce(NonEmptyString(), name, _trace_to_string(trace, "name"))
        disabled = yaml_dict.get("disabled", False)
        enforce(AnyBool(), disabled, _trace_to_string(trace, "disabled"))
        path = yaml_dict.get("path", "")
        enforce(AnyString(), path, _trace_to_string(trace, "path"))
        test_suites_yaml = yaml_dict.get("testSuites", [])
        enforce(AnyList(), test_suites_yaml, _trace_to_string(trace, "testSuites"))
        children_yaml = yaml_dict.get("children", [])
        enforce(AnyList(), children_yaml, _trace_to_string(trace, "children"))

        test_suites: List[TestSuite] = list()
        for i, test_suite_dict in enumerate(test_suites_yaml):
            new_trace = trace + ["testSuites", i]
            test_suites.append(self.parse_test_suite(new_trace, test_suite_dict))

        children: List[TestSuiteCollection] = list()
        for i, child_dict in enumerate(children_yaml):
            new_trace = trace + ["children", i]
            children.append(self.parse_test_suite_collection(new_trace, child_dict))

        return TestSuiteCollection(name, path, disabled, test_suites, children)

    def __init__(self, test_plan: io.IOBase) -> None:
        plan = yaml.safe_load(test_plan)
        self.tests = self.parse_test_suite_collection([], plan)

    def _get_required_exectutors(self, tsc: TestSuiteCollection) -> List[str]:
        result = []
        for child in tsc.children:
            result += self._get_required_exectutors(child)
        for testSuite in tsc.testSuites:
            if not testSuite.disabled:
                result += testSuite.executors
        return result

    def get_required_executors(self) -> List[str]:
        if self._required_executors is None:
            self._required_executors = self._get_required_exectutors(self.tests)
        return self._required_executors


import pandas as pd
import pytz

from .auth import AuthApp, OAuth2Token
from authlib.integrations.requests_client import OAuth2Session
from typing import Union
from datetime import datetime

def authorize(func):
    """Load OAuth2Session

    A :class:`authlib.integrations.requests_client.OAuth2Session ` is needed in order to
    call the sky api and save api tokens.

    loadClient first attempts to load a token from the users cache but if there's no token
    available it will automatically launch a local web server in order to authenticate you
    with the Sky API
    """

    def wrap(*args, **kwargs):
        # Assing the sky instance
        skyObject = args[0]

        # Chcking if theres a client existant
        if not skyObject.client:
            # Chccking if the user already has a token caced
            if not skyObject._loadCachedToken():
                authorizationApp(skyObject)
            # Initalizing the client class
            skyObject.client = OAuth2Session(
                token=skyObject.token,
                client_id=skyObject.token["client_id"],
                client_secret=skyObject.token["client_secret"],
                token_endpoint="https://oauth2.sky.blackbaud.com/token",
                token_endpoint_auth_method="client_secret_basic",
                preserve_refresh_token=True,
            )
            # Updating access token and preserving refresh token
            skyObject.client.refresh_token(
                "https://oauth2.sky.blackbaud.com/token", preserve_refresh_token=True
            )
        # Running the call function
        return func(*args, **kwargs)

    return wrap

def authorizationApp(skyObject) -> None:
    """Launch server to retrieve Sky API token"""
    # Checking if the user passed in a valid dictionary with their credentials
    if skyObject.credentials and isinstance(skyObject.credentials, dict):
        valid_credentials = (
            skyObject.credentials.get("client_id")
            and skyObject.credentials.get("client_secret")
            and skyObject.credentials.get("redirect_uri")
        )
        if valid_credentials:
            app = AuthApp.load_credentials(skyObject.credentials)
        else:
            # If the credentials aren't valid then passing in the path to sky_credentials.json
            app = AuthApp.load_credentials(skyObject.file_path)
    else:
        # If the user didn't pass in a dict then passing in the path to sky_credentials.json
        app = AuthApp.load_credentials(skyObject.file_path)
    # Launching a local server
    skyObject.token = app.run_local_server()
    # Saving the new api token
    skyObject._saveToken(skyObject.token)

def cleanAdvancedList(data: pd.DataFrame) -> pd.DataFrame:
    """Cleans data from the legacy/list Sky API endpoint
    Args:
        data: Data returned from calling the legacy/list Sky API endpoint

    Returns:
        Pandas DataFrame with data from a Core Advanced List
    """
    # Initializing empty array to store index data
    index = []
    # Number of columns in the data
    ncol = len(data.name.unique())
    # Initializing index value
    num = 0

    for i in range(len(data)):
        if (i % ncol) == 0:
            num += 1
        index.append(num)

    # Setting the index values
    data["index"] = index

    # Piviting the data wider
    data = data.pivot(index="index", columns="name", values="value")
    return data

def isActiveTerm(data: pd.DataFrame) -> pd.DataFrame:
    """Takes

    Args:
        data: pd.DataFrame with two datetime columns named
        'begin_date' & 'end_date' respectfully

    Returns:
        Orginal DataFrame, but with a 'active' column that represents whether the current date
        lies within the date range provided in the ['begin_date'. 'end_date'] columns.
    """
    # Setting time zone
    utc = pytz.UTC
    today = utc.localize(datetime.now())
    # Intializing active to be False
    data["active"] = False
    # Checking which terms are active
    for index, row in data.iterrows():
        data.loc[index, "active"] = row["begin_date"] <= today <= row["end_date"]
    return data


from openpyxl import load_workbook
from datetime import datetime
import requests

workbook = load_workbook(filename="mau.xlsx")
sheet = workbook.active

matricula = 0
nombreAlumno = 1
nivel = 2
programa = 3
estatus = 4
ultimaAsistencia = 5

info = ['A', 'B', 'C', 'D', 'E', 'F']

urls = []

comentario = "ssasdas"
lugar = "PEDRO MORENO"

personIndex = 2
for personIndex in range(2, 22):
    informacion = []
    for letter in info:
        if(letter == 'F'):
            informacion.append([])
            cell = letter+str(personIndex)
            dateFull = sheet[cell].value
            year = dateFull.strftime("%Y")
            month = dateFull.strftime("%m")
            day = dateFull.strftime("%d")
            informacion[5].append(year)
            informacion[5].append(day)
            informacion[5].append(month)
        else:
            cell = letter+str(personIndex)
            informacion.append(sheet[cell].value)
    print(informacion)
    comentario = input("Ingrese el comentario de la persona: ")
    print("Haciendo envio")
    maestro = "Mauricio"
    final_url = "entry.142362158_sentinel=&entry.142362158="+str(lugar)+"&entry.2070048545=" + str(informacion[matricula])+"&entry.301730181_year="+str(informacion[5][0])+"&entry.301730181_month="+str(informacion[5][2])+"&entry.301730181_day="+str(informacion[5][1])+"&entry.2108397311_hour=08&entry.2108397311_minute=30&entry.246699160="+maestro+"&entry.1073304337="+str(informacion[nivel])+"&entry.1118742320="+"OK"+"&entry.969578486="+comentario+"&fvv=1&draftResponse=%5Bnull%2Cnull%2C%22-768346404936795950%22%5D%0D%0A&pageHistory=0&fbzx=-768346404936795950"
    original_url = "https://docs.google.com/forms/d/e/1FAIpQLSdPujwS_4h0BBPyrnMsL0uOzOWLT8TXnqK7fAQn5t0SUkQWmQ/formResponse?"
    print(original_url + final_url)
    urls.append(original_url + final_url)

answer = input("seguro? (Y/N) :")

if(answer == 'Y'):
    print("Enviando respuestas")
    for url in urls:
        r = requests.post(url)
    print("Finalizado, buen dia!")
else:
    print("saliendo")
print("adios popo...")


# This file is part of EventGhost.
# Copyright (C) 2005 Lars-Peter Voss <bitmonster@eventghost.org>
#
# EventGhost is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# EventGhost is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EventGhost; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

import eg
import wx
from cStringIO import StringIO
from time import clock
from eg.WinApi.Dynamic import SendMessageTimeout

class EventDropSource(wx.DropSource):

    def __init__(self, win, text):
        wx.DropSource.__init__(self, win)
        # create our own data format and use it in a
        # custom data object
        customData = wx.CustomDataObject("DragItem")
        customData.SetData(text)

        # Now make a data object for the text and also a composite
        # data object holding both of the others.
        textData = wx.TextDataObject(text.decode("UTF-8"))

        data = wx.DataObjectComposite()
        data.Add(textData)
        data.Add(customData)

        # We need to hold a reference to our data object, instead it could
        # be garbage collected
        self.data = data

        # And finally, create the drop source and begin the drag
        # and drop operation
        self.SetData(data)

from eg.Classes.TreeItem import (
    HINT_NO_DROP, HINT_MOVE_INSIDE, HINT_MOVE_BEFORE, HINT_MOVE_AFTER,
    HINT_MOVE_BEFORE_OR_AFTER, HINT_MOVE_EVERYWHERE
)

HITTEST_FLAGS = (
    wx.TREE_HITTEST_ONITEMLABEL |
    wx.TREE_HITTEST_ONITEMICON |
    wx.TREE_HITTEST_ONITEMRIGHT
)

class EventDropTarget(wx.PyDropTarget):

    def __init__(self, window):
        wx.PyDropTarget.__init__(self)
        self.treeCtrl = window
        self.dragCls = eg.EventItem
        self.dragObject = None
        # specify the type of data we will accept
        textData = wx.TextDataObject()
        self.customData = wx.CustomDataObject(wx.CustomDataFormat("DragItem"))
        self.customData.SetData("")
        compositeData = wx.DataObjectComposite()
        compositeData.Add(textData)
        compositeData.Add(self.customData)
        self.SetDataObject(compositeData)
        self.lastExpanded = None
        self.lastHighlighted = None
        self.isExternalDrag = True
        self.position = None
        self.lastDropTime = clock()
        self.lastTargetItem = None

    def OnDragOver(self, x, y, dragResult):
        tree = self.treeCtrl
        dragCls = self.dragCls
        if self.lastHighlighted is not None:
            tree.SetItemDropHighlight(self.lastHighlighted, False)
            self.lastHighlighted = None
        # The value returned here tells the source what kind of visual
        # feedback to give.  For example, if wxDragCopy is returned then
        # only the copy cursor will be shown, even if the source allows
        # moves.  You can use the passed in (x,y) to determine what kind
        # of feedback to give.
        mousePosition = tree.ScreenToClient(wx.GetMousePosition())
        targetItemId, flags = tree.HitTest(mousePosition)
        if flags & HITTEST_FLAGS:
            obj = tree.GetPyData(targetItemId)
            dragObject = self.dragObject
            tmpObj = obj
            while tmpObj is not None:
                if tmpObj == dragObject:
                    self.position = None
                    return wx.DragNone
                tmpObj = tmpObj.parent

            insertionHint = obj.DropTest(dragCls)
            if targetItemId == self.lastTargetItem:
                if (
                    self.lastDropTime + 0.6 < clock()
                    and (
                        obj.__class__ == tree.document.FolderItem
                        or insertionHint == HINT_MOVE_INSIDE
                        or insertionHint == HINT_MOVE_EVERYWHERE
                    ) and not tree.IsExpanded(targetItemId)
                ):
                    tree.Expand(targetItemId)
            else:
                self.lastDropTime = clock()
                self.lastTargetItem = targetItemId

            if insertionHint == HINT_MOVE_BEFORE_OR_AFTER:
                targetRect = tree.GetBoundingRect(targetItemId)
                if y > targetRect.y + targetRect.height / 2:
                    insertionHint = HINT_MOVE_AFTER
                else:
                    insertionHint = HINT_MOVE_BEFORE
            elif insertionHint == HINT_MOVE_EVERYWHERE:
                targetRect = tree.GetBoundingRect(targetItemId)
                if y < targetRect.y + targetRect.height / 4:
                    insertionHint = HINT_MOVE_BEFORE
                elif y > targetRect.y + (targetRect.height / 4) * 3:
                    insertionHint = HINT_MOVE_AFTER
                else:
                    insertionHint = HINT_MOVE_INSIDE

            if insertionHint == HINT_NO_DROP:
                tree.ClearInsertMark()
                self.position = None
                dragResult = wx.DragNone
            elif insertionHint == HINT_MOVE_INSIDE:
                tree.SetItemDropHighlight(targetItemId, True)
                self.lastHighlighted = targetItemId
                self.position = (obj, 0)
                for i in xrange(len(obj.childs)-1, -1, -1):
                    child = obj.childs[i]
                    insertionHint = child.DropTest(dragCls)
                    if (
                        insertionHint in (
                            HINT_MOVE_AFTER,
                            HINT_MOVE_BEFORE_OR_AFTER,
                            HINT_MOVE_EVERYWHERE
                        )
                    ):
                        tree.SetInsertMark(child.id, 1)
                        self.position = (obj, i+1)
                        break
                else:
                    tree.ClearInsertMark()
                dragResult = wx.DragMove
            elif insertionHint == HINT_MOVE_BEFORE:
                parent = obj.parent
                pos = parent.GetChildIndex(obj)
                for i in xrange(pos-1, -1, -1):
                    child = parent.childs[i]
                    insertionHint = child.DropTest(dragCls)
                    if insertionHint == HINT_MOVE_BEFORE:
                        targetItemId = child.id
                        pos -= 1
                tree.SetInsertMark(targetItemId, 0)
                self.position = (parent, pos)
                dragResult = wx.DragMove
            elif insertionHint == HINT_MOVE_AFTER:
                parent = obj.parent
                pos = parent.GetChildIndex(obj)
                for i in xrange(pos+1, len(parent.childs)):
                    child = parent.childs[i]
                    insertionHint = child.DropTest(dragCls)
                    if insertionHint == HINT_MOVE_AFTER:
                        targetItemId = child.id
                        pos += 1
                tree.SetInsertMark(targetItemId, 1)
                self.position = (parent, pos+1)
                dragResult = wx.DragMove
            return dragResult
        if tree.GetSelection().IsOk():
            tree.SelectItem(tree.GetSelection(), False)
            #tree.SetItemDropHighlight(tree.GetSelection(), False)
        return wx.DragNone

    @eg.LogIt
    def OnData(self, dummyX, dummyY, dragResult):
        # Called when OnDrop returns True.
        tree = self.treeCtrl
        tree.ClearInsertMark()
        if self.isExternalDrag and self.position is not None:
            # We need to get the data and do something with it.
            if self.GetData():
                # copy the data from the drag source to our data object
                if tree.GetSelection().IsOk():
                    tree.SelectItem(tree.GetSelection(), False)
                if self.lastHighlighted is not None:
                    tree.SetItemDropHighlight(self.lastHighlighted, False)
                if self.customData.GetDataSize() > 0:
                    label = self.customData.GetData()
                    self.customData.SetData("")
                    parent, pos = self.position
                    eg.UndoHandler.NewEvent().Do(
                        tree.document,
                        label,
                        parent,
                        pos
                    )
        # what is returned signals the source what to do
        # with the original data (move, copy, etc.)  In this
        # case we just return the suggested value given to us.
        return dragResult

    def OnLeave(self):
        self.treeCtrl.ClearInsertMark()

    if eg.debugLevel:
        @eg.LogIt
        def __del__(self):
            pass

class TreeCtrl(wx.TreeCtrl):

    @eg.AssertNotMainThread
    def __init__(self, parent, document=None):
        self.document = document
        self.root = None
        self.hasFocus = False
        self.editLabelId = None
        self.expandingItemPos = None
        self.expandingItemId = None

        style = (
            wx.TR_HAS_BUTTONS |
            wx.TR_EDIT_LABELS |
            wx.TR_ROW_LINES |
            wx.CLIP_CHILDREN
        )
        wx.TreeCtrl.__init__(self, parent, style=style)

        self.SetImageList(eg.Icons.gImageList)

        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
        self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding)
        self.Bind(wx.EVT_TREE_ITEM_COLLAPSING, self.OnItemCollapsing)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginLabelEdit)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndLabelEdit)
        self.Bind(wx.EVT_SET_FOCUS, self.OnGetFocus)
        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
        self.Bind(wx.EVT_TREE_ITEM_GETTOOLTIP, self.OnToolTip)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivate)
        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelectionChanged)
        self.Bind(wx.EVT_TREE_ITEM_MENU, self.OnContextMenu)
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDoubleClick)
        if eg.debugLevel:
            self.Bind(wx.EVT_TREE_ITEM_GETTOOLTIP, self.OnToolTip)
        self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnExpanded)

        self.dropTarget = EventDropTarget(self)
        self.SetDropTarget(self.dropTarget)
        self.hwnd = self.GetHandle()
        normalfont = self.GetFont()
        italicfont = wx.Font(
            normalfont.GetPointSize(),
            normalfont.GetFamily(),
            normalfont.GetStyle(),
            normalfont.GetWeight(),
            normalfont.GetUnderlined(),
            normalfont.GetFaceName(),
            normalfont.GetDefaultEncoding()
        )
        italicfont.SetStyle(wx.FONTSTYLE_ITALIC)
        self.normalfont = normalfont
        self.italicfont = italicfont

    if eg.debugLevel:
        @eg.LogIt
        def __del__(self):
            pass

    def OnLeftDoubleClick(self, event):
        treeItem = self.HitTest(event.GetPosition())[0]
        if treeItem.IsOk():
            item = self.GetPyData(treeItem)
            if item.isConfigurable:
                # we have to wait till the mouse button is up again
                while wx.GetMouseState().LeftDown():
                    wx.GetApp().Yield()
                # and use CallLater and don't call Skip(), as otherwise the
                # click would go through to the newly opened frame.
                wx.CallLater(1, eg.UndoHandler.Configure().Try, self.document)
                return
        # if we don't call Skip() on all other situations, a expandable item
        # would not expand
        event.Skip()

    @eg.LogIt
    def OnItemActivate(self, event):
        item = event.GetItem()
        if item.IsOk():
            wx.CallAfter(eg.UndoHandler.Configure().Try, self.document)
        # if we don't call Skip(), a expandable item would not expand
        event.Skip()

    @eg.AssertNotMainThread
    def SetData(self):
        self.Freeze()
        try:
            self.root = self.document.root
            self.root.CreateTreeItem(self, None)
            self.Expand(self.root.id)
            self.document.selection.Select()
            firstVisibleItem = self.document.firstVisibleItem
            if firstVisibleItem and firstVisibleItem.id is not None:
                self.ScrollTo(firstVisibleItem.id)
        finally:
            self.Thaw()

    @eg.AssertNotMainThread
    @eg.LogIt
    def Destroy(self):
        try:
            self.document.firstVisibleItem = self.GetPyData(
                self.GetFirstVisibleItem()
            )
        except:
            pass
        self.document.SetTree(None)
        self.Freeze()
        self.Unbind(wx.EVT_TREE_SEL_CHANGED)
        self.DeleteAllItems()

    @eg.LogIt
    def OnRightDown(self, event):
        self.EndEditLabel(self.GetSelection(), False)
        # seems like newer wxPython doesn't select the item on right-click
        # so we have to do it ourself
        pos = event.GetPosition()
        item = self.HitTest(pos)[0]
        if item.IsOk():
            self.SelectItem(item)

    @eg.LogIt
    def OnContextMenu(self, event):
        self.SetFocus()
        frame = self.document.frame
        frame.SetupEditMenu(frame.popupMenu)
        self.PopupMenu(frame.popupMenu, event.GetPoint())

    #@eg.LogIt
    def OnSelectionChanged(self, event):
        itemId = event.GetItem()
        item = self.GetPyData(itemId)
        self.document.selection = item
        event.Skip()

    #@eg.LogIt
    def OnItemExpanding(self, event):
        if self.expandingItemId is None:
            self.expandingItemId = event.GetItem()
            self.expandingItemPos = self.GetFirstVisibleItem()
            self.Freeze()
        try:
            itemId = event.GetItem()
            if not self.IsExpanded(itemId):
                item = self.GetPyData(itemId)
                item.isExpanded = True
                for child in item.childs:
                    child.CreateTreeItem(self, itemId)
        finally:
            pass

    #@eg.LogIt
    def OnExpanded(self, event):
        if self.expandingItemId == event.GetItem():
            self.expandingItemId = None
            self.ScrollTo(self.expandingItemPos)
            self.Thaw()

    #@eg.LogIt
    def OnItemCollapsing(self, event):
        itemId = event.GetItem()
        if itemId == self.root.id:
            event.Veto()
            return
        self.Freeze()
        item = self.GetPyData(itemId)
        item.isExpanded = False
        self.Collapse(itemId)
        self.DeleteChildren(itemId)
        self.SetItemHasChildren(itemId)
        newSelectedId = self.GetSelection()
        newSelectedItem = self.GetPyData(newSelectedId)
        document = self.document
        if newSelectedItem is not document.selection:
            document.selection = newSelectedItem
        self.Thaw()

    #@eg.LogIt
    def OnCollapsed(self, event):
        itemId = event.GetItem()
        item = self.GetPyData(itemId)
        item.isExpanded = False

#    def OnToolTip(self, event):
#        id = event.GetItem()
#        item = self.GetPyData(id)
#        if not item:
#            return
#        xmlId = item.xmlId
#        s = item.GetLabel() + "\n\nxmlId: " + str(xmlId)
#        if isinstance(item, eg.ContainerItem):
#            s += "\nchilds: " + str(len(item.childs))
#            s += "\nexpanded:" + str(item.isExpanded)
#            s += "\nIsExpanded:" + str(self.IsExpanded(id))
#        event.SetToolTip(s)

    def OnToolTip(self, event):
        pass

    def SetInsertMark(self, treeItem, after):
        # TVM_SETINSERTMARK = 4378
        if treeItem is not None:
            lParam = long(treeItem.m_pItem)
            SendMessageTimeout(self.hwnd, 4378, after, lParam, 1, 100, None)

    def ClearInsertMark(self):
        SendMessageTimeout(self.hwnd, 4378, 0, long(0), 1, 100, None)

    def OnGetFocus(self, event):
        self.hasFocus = True
        eg.Notify("FocusChange", self)
        event.Skip(True)

    def OnKillFocus(self, event):
        self.hasFocus = False
        if self.editLabelId is None:
            eg.Notify("FocusChange", None)
        event.Skip(True)

    @eg.LogIt
    def OnBeginLabelEdit(self, event):
        item = self.GetPyData(event.GetItem())
        if (not item.isRenameable) or (not self.hasFocus):
            event.Veto()
            return
        self.editLabelId = event.GetItem()
        wx.CallAfter(eg.Notify, "FocusChange", "Edit")

    def OnEndLabelEdit(self, event):
        self.editLabelId = None
        eg.Notify("FocusChange", self)
        itemId = event.GetItem()
        item = self.GetPyData(itemId)
        newLabel = event.GetLabel()
        if not event.IsEditCancelled() and item.GetLabel() != newLabel:
            eg.UndoHandler.Rename(self.document, item, newLabel)
        event.Skip()

    def OnCmdExpandAll(self):
        self.Freeze()
        def Expand(item):
            if isinstance(item, eg.ContainerItem):
                if not item.isExpanded:
                    self.Expand(item.id)
                for child in item.childs:
                    Expand(child)
        Expand(self.root)
        self.EnsureVisible(self.GetSelection())
        self.Thaw()

    def OnCmdCollapseAll(self):
        self.Freeze()
        def Collapse(item):
            if isinstance(item, eg.ContainerItem):
                item.isExpanded = False
                for child in item.childs:
                    Collapse(child)
            item.id = None
        for child in self.root.childs:
            child.isExpanded = False
            for subchild in child.childs:
                Collapse(subchild)
            self.CollapseAndReset(child.id)
        self.Thaw()

    @eg.LogItWithReturn
    def OnBeginDrag(self, event):
        dragId = event.GetItem()
        dragObject = self.GetPyData(dragId)
        if dragObject is self.document.autostartMacro:
            return
        self.SelectItem(dragId)
        dropSource = EventDropSource(self, self.GetItemXml(dragObject))
        dropTarget = self.dropTarget
        dropTarget.dragCls = dragObject.__class__.__bases__[1]
        dropTarget.dragObject = dragObject
        dropTarget.isExternalDrag = False
        dragTimerId = wx.NewId()
        dragTimer = wx.Timer(self, dragTimerId)
        self.Bind(wx.EVT_TIMER, self.OnDragTimer, id=dragTimerId)
        dragTimer.Start(50)
        dropSource.DoDragDrop(wx.Drag_AllowMove)
        dragTimer.Stop()
        self.Unbind(wx.EVT_TIMER, id=dragTimerId)

        dropTarget.dragCls = eg.EventItem
        dropTarget.dragObject = None
        dropTarget.isExternalDrag = True
        insertionMarkPos = dropTarget.position
        self.SelectItem(dragId, False)
        self.ClearInsertMark()
        if dropTarget.lastHighlighted is not None:
            self.SetItemDropHighlight(dropTarget.lastHighlighted, False)

        if insertionMarkPos is not None:
            parent, pos = insertionMarkPos
            eg.UndoHandler.MoveTo(self.document, dragObject, parent, pos)

    def GetTopLevelWindow(self):
        result = self
        while True:
            parent = result.GetParent()
            if parent is None:
                return result
            result = parent

    def OnDragTimer(self, dummyEvent):
        pos = wx.GetMousePosition()
        treeRect = self.GetScreenRect()
        frameRect = self.GetTopLevelWindow().GetScreenRect()
        if treeRect.x <= pos.x <= treeRect.GetRight():
            if pos.y < treeRect.y:
                if pos.y > frameRect.y:
                    self.ScrollLines(-1)
            elif pos.y > treeRect.GetBottom():
                if pos.y < frameRect.GetBottom():
                    self.ScrollLines(1)

    def Cut(self, dummyEvent=None):
        eg.UndoHandler.Cut(self.document, self.document.selection)

    def Copy(self, dummyEvent=None):
        item = self.GetSelection()
        if item.IsOk():
            obj = self.GetPyData(item)
            data = self.GetItemXml(obj).decode("utf-8")
            if data != "" and wx.TheClipboard.Open():
                wx.TheClipboard.SetData(wx.TextDataObject(data))
                wx.TheClipboard.Close()

    def Paste(self, dummyEvent=None):
        eg.UndoHandler.Paste(self.document)

    def Clear(self, dummyEvent=None):
        eg.UndoHandler.Clear(self.document, self.document.selection)

    def GetItemXml(self, obj):
        stream = StringIO()
        stream.write('<?xml version="1.0" encoding="UTF-8" ?>\r\n')
        if obj == self.root:
            obj.GetXmlString(stream.write)
        else:
            buildStr = str(eg.revision)
            stream.write('<EventGhost Version="%s">\r\n' % buildStr)
            obj.GetXmlString(stream.write, "    ")
            stream.write('</EventGhost>')
        data = stream.getvalue()
        stream.close()
        return data


def preRun():
    fin1=open('yelpReviewsLondon.txt')
    fin2=open('yelpReviewsBerlin.txt')
    fin3=open('yelpReviewsParis.txt')
    fin4=open('yelpReviewsManchester.txt')
    fw=open('yelpReviewsAll.txt','w')

    for line in fin1:
        fw.write(line)
    for line in fin2:
        fw.write(line)
    for line in fin3:
        fw.write(line)
    for line in fin4:
        fw.write(line)

    fin1.close()
    fin2.close()
    fin3.close()
    fin4.close()
    fw.close()

def run():

    fin=open('yelpReviewsAll.txt')
    fw1=open('train_review_All.txt','w')
    fw2=open('test_review_All.txt','w')

    lineNo = 0

    for line in fin:
        lineNo = lineNo +1

        dataAll =line.lower().strip().split('\t')
        reviews = []
        goodForKids = 0
        goodForGroups = 0
        isWiFi = 1
        parking = 0
        outdoorSeating = 0
        x = len(dataAll)

        if x < 33 :
            lineNo = lineNo-1
            continue
        if len(dataAll[32]) < 50:
            lineNo = lineNo-1
            continue
        for i in range(33,x):
            data = dataAll[i].split(':')
            if data[0] == 'good for kids':
                if data[1] == 'yes':
                    goodForKids = 1
            if data[0] == 'wi-fi':
                if data[1] == 'no':
                    isWiFi = 0
            if data[0] == 'good for groups':
                if data[1] == 'yes':
                    goodForGroups = 1
            if data[0] == 'parking':
                if data[1].find('private lot') >= 0:
                    parking = 1
                elif data[1].find('garage') >= 0:
                    parking = 1
                elif data[1].find('valet') >= 0:
                    parking = 1
            if data[0] == 'outdoor seating':
                if data[1] == 'yes':
                    outdoorSeating = 1

        for i in range(3,33):
            reviews.append(dataAll[i])

        megaReview = ''
        for i in range(0,30):
            megaReview = megaReview + ' ' + reviews[i]

        if lineNo%10 < 8:

            fw1.write(megaReview+'\t'+str(goodForKids)
                                    +'\t'+str(isWiFi)
                                    +'\t'+str(goodForGroups)
                                    +'\t'+str(parking)
                                    +'\t'+str(outdoorSeating)+'\n')

        else:
            fw2.write(megaReview+'\t'+str(goodForKids)
                                    +'\t'+str(isWiFi)
                                    +'\t'+str(goodForGroups)
                                    +'\t'+str(parking)
                                    +'\t'+str(outdoorSeating)+'\n')

    print('Total reviews:'+str(lineNo))
    fin.close()
    fw1.close()
    fw2.close()

if __name__ == "__main__":
    preRun()
    run()


import os
import boto3
from botocore.exceptions import ClientError
import logging
import pickle

def s3_download(s3_bucket:str, filename:str, object_name:str):
    # check if directory exists, otherwise create
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))

    s3 = boto3.client('s3')
    s3.download_file(s3_bucket, object_name, filename)

def s3_upload(s3_bucket:str, filename:str, object_name=None):
    # If S3 object_name was not specified, use file_name
    if object_name is None:
        object_name = filename

    # Upload the file
    s3_client = boto3.client('s3')
    try:
        response = s3_client.upload_file(filename, s3_bucket, object_name)
    except ClientError as e:
        logging.error(e)
        return False
    return True

# save model to pickle file
def save_pickle(object, filename:str):
    pickle.dump(object, open(filename, 'wb'))

# load model from pickle file
def load_pickle(filename:str):
    object = pickle.load(open(filename, 'rb'))
    return object