import copy
import warnings
from typing import Optional, Tuple

from vyper import ast as vy_ast
from vyper.lll import compile_lll, optimizer
from vyper.old_codegen import parser
from vyper.old_codegen.global_context import GlobalContext
from vyper.semantics import set_data_positions, validate_semantics
from vyper.typing import InterfaceImports, StorageLayout

class CompilerData:
    """
    Object for fetching and storing compiler data for a Vyper contract.

    This object acts as a wrapper over the pure compiler functions, triggering
    compilation phases as needed and providing the data for use when generating
    the final compiler outputs.

    Attributes
    ----------
    vyper_module : vy_ast.Module
        Top-level Vyper AST node
    vyper_module_folded : vy_ast.Module
        Folded Vyper AST
    global_ctx : GlobalContext
        Sorted, contextualized representation of the Vyper AST
    lll_nodes : LLLnode
        LLL used to generate deployment bytecode
    lll_runtime : LLLnode
        LLL used to generate runtime bytecode
    assembly : list
        Assembly instructions for deployment bytecode
    assembly_runtime : list
        Assembly instructions for runtime bytecode
    bytecode : bytes
        Deployment bytecode
    bytecode_runtime : bytes
        Runtime bytecode
    """

    def __init__(
        self,
        source_code: str,
        contract_name: str = "VyperContract",
        interface_codes: Optional[InterfaceImports] = None,
        source_id: int = 0,
        no_optimize: bool = False,
    ) -> None:
        """
        Initialization method.

        Arguments
        ---------
        source_code : str
            Vyper source code.
        contract_name : str, optional
            The name of the contract being compiled.
        interface_codes: Dict, optional
            Interfaces that may be imported by the contracts during compilation.
            * Formatted as as `{'interface name': {'type': "json/vyper", 'code': "interface code"}}`
            * JSON interfaces are given as lists, vyper interfaces as strings
        source_id : int, optional
            ID number used to identify this contract in the source map.
        """
        self.contract_name = contract_name
        self.source_code = source_code
        self.interface_codes = interface_codes
        self.source_id = source_id
        self.no_optimize = no_optimize

    @property
    def vyper_module(self) -> vy_ast.Module:
        if not hasattr(self, "_vyper_module"):
            self._vyper_module = generate_ast(self.source_code, self.source_id, self.contract_name)

        return self._vyper_module

    @property
    def vyper_module_folded(self) -> vy_ast.Module:
        if not hasattr(self, "_vyper_module_folded"):
            self._vyper_module_folded, self._storage_layout = generate_folded_ast(
                self.vyper_module, self.interface_codes
            )

        return self._vyper_module_folded

    @property
    def storage_layout(self) -> StorageLayout:
        if not hasattr(self, "_storage_layout"):
            self._vyper_module_folded, self._storage_layout = generate_folded_ast(
                self.vyper_module, self.interface_codes
            )

        return self._storage_layout

    @property
    def global_ctx(self) -> GlobalContext:
        if not hasattr(self, "_global_ctx"):
            self._global_ctx = generate_global_context(
                self.vyper_module_folded, self.interface_codes
            )

        return self._global_ctx

    def _gen_lll(self) -> None:
        # fetch both deployment and runtime LLL
        self._lll_nodes, self._lll_runtime = generate_lll_nodes(self.global_ctx, self.no_optimize)

    @property
    def lll_nodes(self) -> parser.LLLnode:
        if not hasattr(self, "_lll_nodes"):
            self._gen_lll()
        return self._lll_nodes

    @property
    def lll_runtime(self) -> parser.LLLnode:
        if not hasattr(self, "_lll_runtime"):
            self._gen_lll()
        return self._lll_runtime

    @property
    def assembly(self) -> list:
        if not hasattr(self, "_assembly"):
            self._assembly = generate_assembly(self.lll_nodes)
        return self._assembly

    @property
    def assembly_runtime(self) -> list:
        if not hasattr(self, "_assembly_runtime"):
            self._assembly_runtime = generate_assembly(self.lll_runtime)
        return self._assembly_runtime

    @property
    def bytecode(self) -> bytes:
        if not hasattr(self, "_bytecode"):
            self._bytecode = generate_bytecode(self.assembly)
        return self._bytecode

    @property
    def bytecode_runtime(self) -> bytes:
        if not hasattr(self, "_bytecode_runtime"):
            self._bytecode_runtime = generate_bytecode(self.assembly_runtime)
        return self._bytecode_runtime

def generate_ast(source_code: str, source_id: int, contract_name: str) -> vy_ast.Module:
    """
    Generate a Vyper AST from source code.

    Arguments
    ---------
    source_code : str
        Vyper source code.
    source_id : int
        ID number used to identify this contract in the source map.
    contract_name : str
        Name of the contract.

    Returns
    -------
    vy_ast.Module
        Top-level Vyper AST node
    """
    return vy_ast.parse_to_ast(source_code, source_id, contract_name)

def generate_folded_ast(
    vyper_module: vy_ast.Module, interface_codes: Optional[InterfaceImports]
) -> Tuple[vy_ast.Module, StorageLayout]:
    """
    Perform constant folding operations on the Vyper AST.

    Arguments
    ---------
    vyper_module : vy_ast.Module
        Top-level Vyper AST node

    Returns
    -------
    vy_ast.Module
        Folded Vyper AST
    StorageLayout
        Layout of variables in storage
    """
    vy_ast.validation.validate_literal_nodes(vyper_module)

    vyper_module_folded = copy.deepcopy(vyper_module)
    vy_ast.folding.fold(vyper_module_folded)
    validate_semantics(vyper_module_folded, interface_codes)
    vy_ast.expansion.expand_annotated_ast(vyper_module_folded)
    symbol_tables = set_data_positions(vyper_module_folded)

    return vyper_module_folded, symbol_tables

def generate_global_context(
    vyper_module: vy_ast.Module,
    interface_codes: Optional[InterfaceImports],
) -> GlobalContext:
    """
    Generate a contextualized AST from the Vyper AST.

    Arguments
    ---------
    vyper_module : vy_ast.Module
        Top-level Vyper AST node
    interface_codes: Dict, optional
        Interfaces that may be imported by the contracts.

    Returns
    -------
    GlobalContext
        Sorted, contextualized representation of the Vyper AST
    """
    return GlobalContext.get_global_context(vyper_module, interface_codes=interface_codes)

def generate_lll_nodes(
    global_ctx: GlobalContext, no_optimize: bool
) -> Tuple[parser.LLLnode, parser.LLLnode]:
    """
    Generate the intermediate representation (LLL) from the contextualized AST.

    This phase also includes LLL-level optimizations.

    This function returns two values, one for generating deployment bytecode and
    the other for generating runtime bytecode. The remaining compilation phases
    may be called with either value, depending on the desired final output.

    Arguments
    ---------
    global_ctx : GlobalContext
        Contextualized Vyper AST

    Returns
    -------
    (LLLnode, LLLnode)
        LLL to generate deployment bytecode
        LLL to generate runtime bytecode
    """
    lll_nodes, lll_runtime = parser.parse_tree_to_lll(global_ctx)
    if not no_optimize:
        lll_nodes = optimizer.optimize(lll_nodes)
        lll_runtime = optimizer.optimize(lll_runtime)
    return lll_nodes, lll_runtime

def generate_assembly(lll_nodes: parser.LLLnode, no_optimize: bool = False) -> list:
    """
    Generate assembly instructions from LLL.

    Arguments
    ---------
    lll_nodes : str
        Top-level LLL nodes. Can be deployment or runtime LLL.

    Returns
    -------
    list
        List of assembly instructions.
    """
    assembly = compile_lll.compile_to_assembly(lll_nodes, no_optimize=no_optimize)

    if _find_nested_opcode(assembly, "DEBUG"):
        warnings.warn(
            "This code contains DEBUG opcodes! The DEBUG opcode will only work in "
            "a supported EVM! It will FAIL on all other nodes!"
        )
    return assembly

def _find_nested_opcode(assembly, key):
    if key in assembly:
        return True
    else:
        sublists = [sub for sub in assembly if isinstance(sub, list)]
        return any(_find_nested_opcode(x, key) for x in sublists)

def generate_bytecode(assembly: list) -> bytes:
    """
    Generate bytecode from assembly instructions.

    Arguments
    ---------
    assembly : list
        Assembly instructions. Can be deployment or runtime assembly.

    Returns
    -------
    bytes
        Final compiled bytecode.
    """
    return compile_lll.assembly_to_evm(assembly)[0]


import sys
import json
import sql
from dateutil import parser as dt

def getPackageId(package, ecosystem):
    ''' returns packageId if exists, else creates '''
    selectQ = 'select id from package where name=%s and ecosystem=%s'
    results = sql.execute(selectQ,(package,ecosystem))
    if not results:
        insertQ = 'insert into package values (null,%s,%s)'
        sql.execute(insertQ,(package,ecosystem))
        results = sql.execute(selectQ,(package,ecosystem))
    return results[0]['id']

with open("../snyk/data/ghsa.json") as file:
    data = json.load(file)

for advisory in data:

    id = advisory['ghsaId']
    score = advisory['cvss'].get('score',None)
    vector = advisory['cvss'].get('vectorString',None)

    cve = None
    for e in advisory['identifiers']:
        if e['type']=='CVE':
            if e['value'] != '':
                cve=e['value']
                assert cve.count('CVE') == 1

    origin = advisory['origin']
    publish = dt.parse(advisory['publishedAt'])
    severity = advisory['severity']
    summary = advisory['summary']
    withdrawn =  advisory['withdrawnAt']
    if withdrawn:
        withdrawn = dt.parse(withdrawn)

    q='insert into advisory values(%s,%s,%s,%s,%s,%s,%s,%s,%s)'
    print(id,score,vector,cve,origin,publish,severity, summary,withdrawn)
    sql.execute(q,(id,score,vector,cve,origin,publish,severity, summary,withdrawn))

    for e in advisory['vulnerabilities']['edges']:
        data = e['node']
        package = data['package']['name']
        ecosystem = data['package']['ecosystem']
        packageId = getPackageId(package, ecosystem)
        if data['firstPatchedVersion'] is None:
            patch = None
        else:
            patch = data['firstPatchedVersion']['identifier']
        range = data['vulnerableVersionRange']
        q='insert into advisory_package values(%s,%s,%s,%s)'
        print(id,packageId,patch,range)
        sql.execute(q,(id,packageId,patch,range))


from random import randint, shuffle

import discord
import modules.math.strmath as strmath
import modules.math.wolfram as wolfram

import modules.math.latex as latex
from command import command
from util import client, theme_color, error_color
from logger import log

# import urllib.parse

@command('math')
async def math(message, args):
    result = strmath.evaluate(args)
    if result.type == 'error':
        if result.data.traceback != '':
            embed = discord.Embed(color=error_color, title=result.data.message, description='```\n%s```' % result.data.traceback)
        else:
            embed = discord.Embed(color=error_color, title=result.data.message)
    else:
        embed = discord.Embed(color=theme_color, title='Result', description=result.data)
    await client.send_message(message.channel, embed=embed)

@command('rand')
async def rand(message, args):
    try:
        if ',' in args:
            split_args = args.split(',')
            a, b = int(split_args[0].strip()), int(split_args[1].strip())
            await client.send_message(message.channel, str(randint(a, b)))
        else:
            await client.send_message(message.channel, str(randint(1, int(args))))
    except (TypeError, ValueError):
        await client.send_message(message.channel, '**Invalid position(s).**')

@command('shuffle')
async def shuffle_text(message, args):
    lst = [x.strip(',') for x in args.split(' ')]
    shuffle(lst)
    await client.send_message(message.channel, ', '.join(lst))

@command('tex')
async def generate_latex(message, args):
    code = args.strip('`')
    try:
        image = latex.generate(code)
    except Exception as e:
        await client.send_message(message.channel, embed=discord.Embed(color=error_color, description=str(e)))
        return
    await client.send_file(message.channel, image, filename='latex.png')

@command('wolfram')
async def wolfram_alpha(message, args):
    try:
        result = wolfram.query(args)
        image = wolfram.join_images(result)
        await client.send_file(message.channel, image, filename='wolfram.png')
        icon_url = 'https://cdn.iconscout.com/public/images/icon/free/png-512/wolfram-alpha-logo-3225dad81e65984f-512x512.png'
        embed = discord.Embed(color=16039746)
        embed.set_footer(text='Powered by Wolfram | Alpha Computational Knowledge Engine', icon_url=icon_url)
        await client.send_message(message.channel, embed=embed)
    except Exception as e:
        await client.send_message(message.channel, embed=discord.Embed(color=error_color, description=str(e)))

@command('equation')
async def generate_equation(message, args):
    try:
        image = latex.generate_eqn(args)
        await client.send_file(message.channel, image, filename='equation.png')
    except Exception as e:
        log(message.server.id, str(e))
        await client.send_message(message.channel, embed=discord.Embed(color=error_color, description='Failed to generate equation.'))


from preprocessing.loading import *
from utils import *
import logging, os
import gcsfs
from joblib import Parallel, delayed
from tqdm import tqdm
from db import RedisDB
from math import ceil

logging.basicConfig(level = logging.INFO)

GOOGLE_APPLICATION_CREDENTIALS = os.getenv('GOOGLE_APPLICATION_CREDENTIALS')
GOOGLE_PROJECT_ID = os.getenv('GOOGLE_PROJECT_ID')
BELLY_LOCATION = os.getenv('BELLY_LOCATION')
CHUNK_SIZE = int(os.getenv('GUT_CHUNK_SIZE'))
REDIS_HOST = os.getenv('REDIS_HOST')
REDIS_PORT = int(os.getenv('REDIS_PORT'))

def load_tweets(fps, fs, tz):
    db = RedisDB(REDIS_HOST, REDIS_PORT)
    tweets = get_tweets(fps, fs, tz)
    db.load(tweets)

def process(limit=None, mode='parallel'):
    fs = gcsfs.GCSFileSystem(project=GOOGLE_PROJECT_ID,
                             token=GOOGLE_APPLICATION_CREDENTIALS,
                             access='read_write')
    fps = sorted(fs.ls(BELLY_LOCATION))

    tz = 'Asia/Kolkata'
    fps = filter_start_time(fps, tz)

    if limit:
        fps = fps[:int(limit)]

    logging.info(f'Processing {len(fps)} files')

    if mode == 'parallel':
        Parallel(n_jobs=-1)(delayed(load_tweets)(list(f), fs, tz)
                            for f in tqdm(chunk(CHUNK_SIZE, fps),
                                          total=ceil(len(fps)/CHUNK_SIZE)))
    else:
        load_tweets(fps, fs, tz)

    logging.info(f'Loaded all tweets')

from clize import run
if __name__ == '__main__':
    run(process)


# _*_ coding:utf-8 _*_

import pika
from multiprocessing import Process, current_process
from settings import *

class FlowCollect(Process):
    """流量收集类"""
    def __init__(self, url, instance):
        super(FlowCollect, self).__init__()
        self.url = url
        self.instance = instance
        self.name = instance["id"]
        self.flag = True

    def run(self):
        flowID = -1
        headers = {"Accept": "application/json"}
        connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.1.180'))
        channel = connection.channel()
        channel.queue_declare(queue='flow', durable=True)
        while True and self.flag:
            r = requests.get(self.url + "&flowID=" + str(flowID), headers=headers, timeout=3)
            if r.status_code != 200:
                break
            flows = r.json()
            if len(flows) == 0:
                connection.sleep(1)
                continue
            flowID = flows[0]["flowID"]
            flows_insert = []
            for f in flows:
                flow = {}
                flow["tenant_id"] = self.instance["tenant_id"]
                flow["tenant_name"] = self.instance["tenant_name"]
                flow["instance_id"] = self.instance["id"]
                flow["instance_name"] = self.instance["name"]
                flow["port_id"] = self.instance["interfaceAttachments"][0]["port_id"]
                flow["mac_addr"] = self.instance["interfaceAttachments"][0]["mac_addr"].replace(":", "").upper()
                flow["fixed_ip"] = self.instance["interfaceAttachments"][0]["fixed_ips"][0]["ip_address"]
                for ips in self.instance["addresses"].values():
                    if len(ips) == 2:
                        for ip in ips:
                            if ip["OS-EXT-IPS:type"] == "floating":
                                flow["floating_ip"] = ip["addr"]
                    else:
                        flow["floating_ip"] = "0.0.0.0"

                flow["hypervisor_name"] = self.instance["OS-EXT-SRV-ATTR:hypervisor_hostname"]
                flow["timestap"] = f["end"]
                flowkeys = f["flowKeys"].split(',')
                flow["macsource"] = flowkeys[0]
                flow["macdestination"] = flowkeys[1]
                flow["ipsource"] = flowkeys[2]
                flow["ipdestination"] = flowkeys[3]
                flow["ipprotocol"] = int(flowkeys[4])
                flow["size"] = float(f["value"])
                flow["srcport_or_icmptype"] = int(flowkeys[5])
                flow["dstport_or_icmpcode"] = int(flowkeys[6])
                flow["tcpflags"] = flowkeys[7]
                flow["httpmethod"] = flowkeys[8]
                flow["extension"] = "{}"
                flows_insert.append(flow)

            channel.basic_publish(exchange='',
                                  routing_key='flow',
                                  body=json.dumps(flows_insert),
                                  properties=pika.BasicProperties(
                                      delivery_mode=2,  # make message persistent
                                      )
                                  )
            # print " [%s] Sent %r" % (current_process().pid, json.dumps(flows_insert))
            connection.sleep(1)
        channel.close()
        connection.close()