from __future__ import print_function
import os,sys

import os
import json
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

from larlite import larlite
from larcv import larcv
import lardly

#input_larlite = sys.argv[1]
# input_larlite = "/home/jmills/workdir/michel_files/tracker_reco_Run000001-SubRun000586.root"
input_larlite = "/home/jmills/workdir/michel_files/tracker_reco_Run000001-SubRun000001.root"

input_mc =      "/home/jmills/workdir/michel_files/mcinfo-Run000001-SubRun000001.root"
# input_larcv   = "/home/jmills/workdir/michel_files/supera-Run000001-SubRun000586.root"
input_larcv   = "/home/jmills/workdir/michel_files/supera-Run000001-SubRun000001.root"

input_pgraph = "/home/jmills/workdir/michel_files/pgraph_file-Run000001-SubRun000001.root"
detdata = lardly.DetectorOutline()

entry = 12
# LARLITE
io_ll = larlite.storage_manager(larlite.storage_manager.kREAD)
io_ll.add_in_filename( input_larlite )
io_ll.open()
io_ll.go_to(entry)

io_ll_mc = larlite.storage_manager(larlite.storage_manager.kREAD)
io_ll_mc.add_in_filename( input_mc )
io_ll_mc.open()
io_ll_mc.go_to(entry)

# TRACK
evtrack = io_ll.get_data(larlite.data.kTrack,"trackReco")
evmctrack = io_ll_mc.get_data(larlite.data.kMCTrack,"mcreco")
evmcshower = io_ll_mc.get_data(larlite.data.kMCShower,"mcreco")

print("number of tracks: ",evtrack.size())
track_v = [lardly.data.visualize_larlite_track( evtrack[i], color=(255,0,0) ) for i in range(evtrack.size())]

mctrack_v =  lardly.data.visualize_larlite_event_mctrack( evmctrack )
mcshower_v = [ lardly.data.visualize3d_larlite_mcshower( evmcshower.at(x) ) for x in range(evmcshower.size()) ]
# mctrack_v = lardly.data.visualize_larlite_event_mctrack( io_ll_mc.get_data(larlite.data.kMCTrack, "mcreco"))
# mcshower_v = lardly.data.visualize_larlite_event_mcshower( evmcshower )

# vtx_v =  [ lardly.data.visualize_larlite_track_vtx( evtrack[i] ) for i in range(evtrack.size())  ]
# end_v =  [ lardly.data.visualize_larlite_track_end( evtrack[i] ) for i in range(evtrack.size())  ]

# LARCV
io_cv = larcv.IOManager(larcv.IOManager.kREAD,"supera",larcv.IOManager.kTickBackward)
io_cv.add_in_file( input_larcv )
#io_cv.reverse_all_products()
io_cv.initialize()
io_cv.read_entry(entry)

io_pgraph_cv = larcv.IOManager(larcv.IOManager.kREAD)
io_pgraph_cv.add_in_file( input_pgraph )
io_pgraph_cv.initialize()
io_pgraph_cv.read_entry(entry)

# IMAGE2D
ev_img = io_cv.get_data( larcv.kProductImage2D, "wire" )
img2d_v = ev_img.Image2DArray()

# Vertex
ev_michel = io_pgraph_cv.get_data( larcv.kProductPGraph, "mc_decays")
print("number of decays: ",ev_michel.PGraphArray().size())
color_michel = [0,255,0] #green
michel_v =  lardly.data.visualize3d_larcv_pgraph( ev_michel, color_michel )
pgraph2d_michel = lardly.data.visualize2d_larcv_pgraph( ev_michel,None,  color=color_michel )

ev_good = io_pgraph_cv.get_data( larcv.kProductPGraph, "good_candidates_reco")
print("number of good candidates (multi counting): ",ev_good.PGraphArray().size())
color_good = [0,255,255] #teal
good_v =  lardly.data.visualize3d_larcv_pgraph( ev_good, color_good )
pgraph2d_good = lardly.data.visualize2d_larcv_pgraph( ev_good,None, color=color_good )

ev_bad = io_pgraph_cv.get_data( larcv.kProductPGraph, "bad_candidates_reco")
print("number of bad candidates (multi counting): ",ev_bad.PGraphArray().size())
color_bad_2d = [0,0,0] # black
color_bad_3d = [255,255,0] # black

bad_v =  lardly.data.visualize3d_larcv_pgraph( ev_bad, color_bad_3d )
pgraph2d_bad = lardly.data.visualize2d_larcv_pgraph( ev_bad,None, color=color_bad_2d )

# ev_pix = io_cv.get_data( larcv.kProductPixel2D, "allreco" )
# pix_meta_v = [ ev_pix.ClusterMetaArray(p) for p in range(3) ]
# pix_arr_v = [ ev_pix.Pixel2DClusterArray()[p] for p in range(3) ]
# pixtraces = [ lardly.data.visualize_larcv_pixel2dcluster( pix_arr_v[0][i], pix_meta_v[0][0] ) for i in range(pix_arr_v[0].size()) ]

# detdata = lardly.DetectorOutline()

app = dash.Dash(
    __name__,
    meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}],
)

server = app.server

axis_template = {
    "showbackground": True,
    "backgroundcolor": "#141414",
    "gridcolor": "rgb(255, 255, 255)",
    "zerolinecolor": "rgb(255, 255, 255)",
}

plot_layout = {
    "title": "",
    "height":800,
    "margin": {"t": 0, "b": 0, "l": 0, "r": 0},
    "font": {"size": 12, "color": "white"},
    "showlegend": False,
    "plot_bgcolor": "#141414",
    "paper_bgcolor": "#141414",
    "scene": {
        "xaxis": axis_template,
        "yaxis": axis_template,
        "zaxis": axis_template,
        "aspectratio": {"x": 1, "y": 1, "z": 4},
        "camera": {"eye": {"x": 2, "y": 2, "z": 2},
                   "up":dict(x=0, y=1, z=0)},
        "annotations": [],
    },
}

testline = {
    "type":"scattergl",
    "x":[200,400,400,800],
    "y":[3200,3400,3800,4400],
    "mode":"markers",
    #"line":{"color":"rgb(255,255,255)","width":4},
    "marker":dict(size=10, symbol="triangle-up",color="rgb(255,255,255)"),
    }

app.layout = html.Div( [
    html.Div( [
        dcc.Graph(
            id="det3d",
            figure={
                "data": detdata.getlines()+track_v+ mctrack_v + michel_v + mcshower_v + good_v + bad_v,
                "layout": plot_layout,
            },
            config={"editable": True, "scrollZoom": False},
        )],
              className="graph__container"),
    html.Div( [
        dcc.Graph(
            id="image2d_plane0",
            figure={"data":[ lardly.data.visualize_larcv_image2d( img2d_v[0] )]+ pgraph2d_michel[0]+ pgraph2d_good[0]+ pgraph2d_bad[0], #+pixtraces
                    "layout":{"height":800} }),
        dcc.Graph(
            id="image2d_plane1",
            figure={"data":[lardly.data.visualize_larcv_image2d( img2d_v[1] )]+ pgraph2d_michel[1]+ pgraph2d_good[1]+ pgraph2d_bad[1],
                    "layout":{"height":800}}),
        dcc.Graph(
            id="image2d_plane2",
            figure={"data":[lardly.data.visualize_larcv_image2d( img2d_v[2] )]+ pgraph2d_michel[0]+ pgraph2d_good[2]+ pgraph2d_bad[2],
                    "layout":{"height":800}}),
        ] ),
    ] )

if __name__ == "__main__":
    app.run_server(debug=True)


"""@package NMF-visualization

Visualize NMF transcriptions.
"""

import argparse
import librosa
import numpy as np

from lib.CQT import CQTspectrogram
from lib.NMF import NMF
from lib.normalize import maxNormalize
from lib.normalize import RMSnormalize
from lib.normalize import sumNormalize
from lib.spectrogram import magnitudeSpectrogram
from lib.utils import createDir

NMF_DICTIONARY_PATH = "data/dictionaries/"
INSTRUMENT_INFO_PATH = "data/paths/"

def trainDictionary(instrument, instrumentRange = None, infoFile = None,
                    cqt = False, dictionaryPath = None, normalization = None,
                    Fs = 44100, fftSize = 2048, numOctaves = 8, octaveBins = 60,
                    **kwargs):
    """
    Train an instrument model with NMF.

    Keyword arguments:
    instrument -- the instrument name.
    instrumentRange -- the range of MIDI notes to cover as a tuple.
    infoFile -- the path to a textfile containg instrument note paths.
        (default = None)
    cqt -- whether to use CQT instead of STFT. (default = False)
    dictionaryPath -- path to save . (default = None)
    normalization -- the spectrogram normalization function.
        (default = None)
    Fs -- the sample rate. (default = SAMPLE_RATE)
    fftSize -- the number of FFT bins. (default =2048)
    numOctaves -- the number of CQT octaves. (default = 8)
    octaveBins -- the number of bins per CQT octavee. (default = 60)
    """

    kwargs.setdefault("fftSize", fftSize)
    kwargs.setdefault("numOctaves", numOctaves)
    kwargs.setdefault("octaveBins", octaveBins)

    if (infoFile is None):
        infoFile = INSTRUMENT_INFO_PATH + instrument + ".txt"

    with open(infoFile, "r") as f:
        lines = f.readlines()

    if (instrumentRange is None):
        minNote = -1
        maxNote = 0
        for line in lines:
            midi = int(line.split()[0])
            if (minNote == -1 or midi < minNote):
                minNote = midi
            elif (midi > maxNote):
                maxNote = midi
        instrumentRange = (minNote, maxNote)

    numNotes = instrumentRange[1] - instrumentRange[0] + 1

    if (cqt):
        numBins = octaveBins*numOctaves
    else:
        numBins = 1 + fftSize//2

    W = np.zeros((numNotes, numBins))

    for line in lines:
        line = line.split()
        i = int(line[0]) - instrumentRange[0]

        if (i < 0 or i >= numNotes):
            continue

        notePath = line[1]

        x, Fs = librosa.load(notePath, sr = Fs, mono = True)
        if (cqt):
            S = CQTspectrogram(x, Fs, **kwargs)
        else:
            S = magnitudeSpectrogram(x, Fs, **kwargs)

        if (not normalization is None):
            S = normalization(S, axis = 1)

        h, w = NMF(S, k = 1, **kwargs)
        W[i] = w.flatten()

    if (dictionaryPath is None):
        dictionaryPath = NMF_DICTIONARY_PATH

    path = dictionaryPath + instrument + ".npy"

    createDir(path)
    np.save(path, W.T)

if (__name__ == "__main__"):

    parser = argparse.ArgumentParser("Compute an NMF dictionary.")
    parser.add_argument("instrument", help = "The instrument to train a "
                        + "dictionary for.", type = str)
    parser.add_argument("--info",
                        help = "A textfile containing paths to note samples. "
                        + "(default = None)", type = str, default = None,
                        dest = "info")
    parser.add_argument("--norm",
                        help = "The spectrogram normalization. " +
                        "(default = 'max')", type = str, default = "max",
                        dest = "norm")
    parser.add_argument("-d",
                        help = "The destination file. (default = None)",
                        type = str, default = None, dest = "saveAs")
    args = parser.parse_args()

    if (args.norm.lower() == "max"):
        norm = maxNormalize
    elif (args.norm.lower() == "rms"):
        norm = RMSnormalize
    elif (args.norm.lower() == "sum"):
        norm = sumNormalize
    else:
        norm = None

    trainDictionary(args.insrument, normalization = norm,
                    dictionaryPath = args.dest)


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from tqdm import tqdm

import pydrake.autodiffutils
from pydrake.all import InitializeAutoDiff, ExtractGradient
from alpha_gradient.objective_function import ObjectiveFunction
from alpha_gradient.statistical_analysis import compute_mean, compute_variance_norm
from alpha_gradient.lipschitz_estimator import estimate_lipschitz_probability
from ball_with_wall import BallWithWall

objective = BallWithWall()

sweep = 100
xspace = np.linspace(0, np.pi/2, sweep)
Lbar = 0.1
alpha_lst = np.zeros(sweep)
value_lst = np.zeros(sweep)

for i in tqdm(range(len(xspace))):
    mean = xspace[i]
    sigma = 0.1
    trials = 1000
    subbatch_size = 3

    X = np.random.normal(mean, sigma, (trials, subbatch_size, 1))
    X_flatten = X.reshape((trials * subbatch_size, 1))
    y_flatten = objective.evaluate_batch(np.array([0]), X_flatten)
    y = y_flatten.reshape((trials, subbatch_size, 1))

    alpha_lst[i] = estimate_lipschitz_probability(X, y, Lbar)
    value_lst[i] = objective.evaluate(np.array([mean]), np.zeros(1))

normalized_value_lst = (value_lst - np.min(value_lst)) / (
    np.max(value_lst) - np.min(value_lst))

plt.figure()
plt.plot(xspace, alpha_lst, 'r-', label='Alpha')
plt.plot(xspace, normalized_value_lst, 'k-', label='Objective')
plt.legend()
plt.show()


"""Initial DB structure

Revision ID: 22d059a9f5f
Revises: None
Create Date: 2013-12-18 15:14:42.832658

"""

# revision identifiers, used by Alembic.
revision = '22d059a9f5f'
down_revision = None

from alembic import op
import sqlalchemy as sa

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('stations',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(), nullable=True),
    sa.Column('norm_name', sa.String(), nullable=True),
    sa.Column('url', sa.String(), nullable=True),
    sa.Column('logo', sa.LargeBinary(), nullable=True),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('name'),
    sa.UniqueConstraint('url')
    )
    op.create_table('state_info',
    sa.Column('key', sa.String(), nullable=False),
    sa.Column('_value', sa.String(), nullable=True),
    sa.PrimaryKeyConstraint('key')
    )
    op.create_table('keywords',
    sa.Column('text', sa.String(), nullable=False),
    sa.PrimaryKeyConstraint('text')
    )
    op.create_table('station_keywords',
    sa.Column('station_id', sa.Integer(), nullable=False),
    sa.Column('keyword', sa.String(), nullable=False),
    sa.ForeignKeyConstraint(['keyword'], ['keywords.text'], ),
    sa.ForeignKeyConstraint(['station_id'], ['stations.id'], ),
    sa.PrimaryKeyConstraint('station_id', 'keyword')
    )
    ### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('station_keywords')
    op.drop_table('keywords')
    op.drop_table('state_info')
    op.drop_table('stations')
    ### end Alembic commands ###


import requests

url = " https://fanyi.baidu.com/sug"
s = input("请输入您想查询的单词\n===>>>")
dat = {
    "kw": s
}
# 发送post请求,发送的数据必须放在字典中，通过data参数进行传递
resp = requests.post(url, data=dat)
print(resp.json())  # 将服务器返回的内容直接处理成json()    =>dict


#
# This app uses redis (key/value pair data store) to keep track
# of how many times ths web page has been visited.
#
from flask import Flask
from redis import Redis

app = Flask(__name__)

#
# In the Docker Compose environment, redis is the hostname
#
redis = Redis(host='redis', port=6379)

@app.route('/')
def hello():
    count = redis.incr('hits')
    return 'Hello World from Docker Compose! I have been seen {} times.\n'.format(count)

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)


# Main script for experimenting with training on a subgraph
from collections import Counter
import argparse
import numpy as np
import sys
import os
import json
import time
import random

import torch
import torch.nn as nn

from model import LinkPredictor
from reader import AtomicTSVReader, ConceptNetTSVReader, FB15kReader
import utils
import reader_utils
import evaluation_utils

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

def set_seeds(seed):
    random.seed(42)
    np.random.seed(42)
    torch.manual_seed(42)
    torch.cuda.manual_seed(42)

def load_data(dataset, reader_cls, data_dir, sim_relations):
    train_network = reader_cls(dataset)
    dev_network = reader_cls(dataset)
    test_network = reader_cls(dataset)

    train_network.read_network(data_dir=data_dir, split="train")
    train_network.print_summary()
    node_list = train_network.graph.iter_nodes()
    node_degrees = [node.get_degree() for node in node_list]
    degree_counter = Counter(node_degrees)
    avg_degree = sum([k * v for k, v in degree_counter.items()]) / sum([v for k, v in degree_counter.items()])
    print("Average Degree: ", avg_degree)

    dev_network.read_network(data_dir=data_dir, split="valid", train_network=train_network)
    test_network.read_network(data_dir=data_dir, split="test", train_network=train_network)

    word_vocab = train_network.graph.node2id

    # Add sim nodes
    if sim_relations:
        print("Adding sim edges..")
        train_network.add_sim_edges_bert()

    train_data, _ = reader_utils.prepare_batch_dgl(word_vocab, train_network, train_network)
    test_data, test_labels = reader_utils.prepare_batch_dgl(word_vocab, test_network, train_network)
    valid_data, valid_labels = reader_utils.prepare_batch_dgl(word_vocab, dev_network, train_network)

    return train_data, valid_data, test_data, valid_labels, test_labels, train_network

def get_model_name(args):

    name = '_subgraph_model_state.pth'
    name = "_" + args.gcn_type + "_" + args.decoder + name

    if args.sim_relations:
        name = "_sim_relations" + name

    if args.sim_sim:
        name = "_sim-sim" + name

    if args.bert_concat:
        name = "_bert_concat" + name

    if args.bert_mlp:
        name = "_bert_mlp" + name

    if args.tying:
        name = "_tying" + name

    if args.bert_sum:
        name = "_bert_sum" + name

    if args.input_layer == "bert":
        name = "_inp-bert" + name

    model_state_file = args.dataset + name
    if not os.path.exists(args.output_dir):
        os.makedirs(args.output_dir)
    model_state_file = os.path.join(args.output_dir, model_state_file)

    return model_state_file

def main(args):
    set_seeds(args.seed)

    # load graph data
    if args.dataset == "FB15K-237":
        dataset_cls = FB15kReader
        data_dir = "data/FB15k-237/"
    elif args.dataset == "atomic":
        dataset_cls = AtomicTSVReader
        data_dir = "data/atomic/"
    elif args.dataset == "conceptnet":
        dataset_cls = ConceptNetTSVReader
        data_dir = "data/ConceptNet/"
    else:
        raise ValueError("Invalid option for dataset.")

    # Store entity-wise dicts for filtered metrics
    train_data, valid_data, test_data, valid_labels, test_labels, train_network = load_data(args.dataset,
                                                                                dataset_cls,
                                                                                data_dir,
                                                                                args.sim_relations)
    num_nodes = len(train_network.graph.nodes)
    num_rels = len(train_network.graph.relations)
    all_tuples = train_data.tolist() + valid_data.tolist() + test_data.tolist()

    # for filtered ranking
    all_e1_to_multi_e2, all_e2_to_multi_e1 = reader_utils.create_entity_dicts(all_tuples, num_rels, args.sim_relations)

    # for training
    train_e1_to_multi_e2, train_e2_to_multi_e1 = reader_utils.create_entity_dicts(train_data.tolist(), num_rels,
                                                                                args.sim_relations)
    # the below dicts `include` sim relations
    sim_train_e1_to_multi_e2, sim_train_e2_to_multi_e1 = reader_utils.create_entity_dicts(train_data.tolist(), num_rels)

    # check cuda
    use_cuda = torch.cuda.is_available()
    if use_cuda and not args.no_cuda:
        torch.cuda.set_device(args.gpu)

    cpu_decoding = args.cpu_decoding

    # atomic graph is much larger, so we perform evaluation on cpu
    cpu_eval = True if args.dataset == "atomic" else False

    # create model
    model = LinkPredictor(num_nodes,
                          num_rels,
                          args,
                          use_cuda=use_cuda)

    # build graph
    graph_train_data = train_data
    test_graph, test_rel, test_norm = utils.build_test_graph(num_nodes, num_rels, graph_train_data)
    test_deg = test_graph.in_degrees(range(test_graph.number_of_nodes())).float().view(-1, 1)
    test_node_id = torch.arange(0, num_nodes, dtype=torch.long).view(-1, 1)
    test_rel = torch.from_numpy(test_rel).view(-1, 1)
    test_norm = torch.from_numpy(test_norm).view(-1, 1)

    # transfer graph data to gpu
    if use_cuda and not args.no_cuda and not cpu_decoding:
        test_node_id = test_node_id.cuda()
        test_norm = test_norm.cuda()
        test_rel = test_rel.cuda()

    # validation and testing triplets
    valid_data = torch.LongTensor(valid_data)
    test_data = torch.LongTensor(test_data)

    if use_cuda and not args.no_cuda and not cpu_eval:
        valid_data = valid_data.cuda()
        test_data = test_data.cuda()

    test_graph.ndata.update({'id': test_node_id, 'norm': test_norm})
    test_graph.edata['type'] = test_rel

    if use_cuda and not args.no_cuda:
        # model = nn.DataParallel(model, device_ids=[0,1])
        model = model.cuda()

    model_state_file = get_model_name(args)

    # writer = SummaryWriter("runs/" + model_state_file.replace(".pth",".log"))

    # check if only evaluation needs to be performed
    if args.eval_only:
        if args.load_model:
            model_state_file = args.load_model
        else:
            print("Please provide model path for evaluation (--load_model)")
            sys.exit(0)

        checkpoint = torch.load(model_state_file)

        if use_cuda and not args.no_cuda and cpu_eval:
            model.cpu()
            test_graph.ndata['id'] = test_graph.ndata['id'].cpu()
            test_graph.ndata['norm'] = test_graph.ndata['norm'].cpu()
            test_graph.edata['type'] = test_graph.edata['type'].cpu()
            model.decoder.no_cuda = True

        model.eval()
        model.load_state_dict(checkpoint['state_dict'])
        print(model)

        print("================DEV=================")
        mrr = evaluation_utils.ranking_and_hits(test_graph, model, valid_data, all_e1_to_multi_e2, train_network,
                                                fusion="graph-only", sim_relations=args.sim_relations,
                                                write_results=args.write_results, debug=args.debug)

        print("================TEST================")
        mrr = evaluation_utils.ranking_and_hits(test_graph, model, test_data, all_e1_to_multi_e2, train_network,
                                                fusion="graph-only", sim_relations=args.sim_relations, debug=args.debug)

        sys.exit(0)

    if os.path.isfile(model_state_file):
        print(model_state_file)
        overwrite = input('Model already exists. Overwrite? Y = yes, N = no\n')
        if overwrite.lower() == 'n':
            print("Quitting")
            sys.exit(0)
        elif overwrite.lower() != 'y':
            raise ValueError("Invalid Option")

    # build adj list and calculate degrees for sampling
    adj_list, degrees, sparse_adj_matrix, rel = utils.get_adj_and_degrees(num_nodes, num_rels, train_data)

    # remove sim edges from sampling_edge_ids (we sample from the original graph and then densify it)
    if args.sim_relations:
        sim_edge_ids = np.where(graph_train_data[:, 1] == num_rels - 1)[0]
        sampling_edge_ids = np.delete(np.arange(len(graph_train_data)), sim_edge_ids)
    else:
        sampling_edge_ids = None

    # optimizer
    optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)

    forward_time = []
    backward_time = []

    # training loop
    print("Starting training...")
    epoch = 0
    best_mrr = 0

    while True:
        model.train()
        epoch += 1

        cur_train_data = graph_train_data[:]

        # build dgl graph
        g, node_id, edge_type, node_norm, data, labels = \
            utils.generate_sampled_graph_and_labels(
                cur_train_data, args.graph_batch_size,
                num_rels, adj_list, degrees, args.negative_sample, args.sim_sim, args.sim_relations,
                sim_train_e1_to_multi_e2, sampling_edge_ids)

        node_id_copy = np.copy(node_id)
        node_dict = {v: k for k, v in dict(enumerate(node_id_copy)).items()}

        # set node/edge feature
        node_id = torch.from_numpy(node_id).view(-1, 1)
        edge_type = torch.from_numpy(edge_type)
        node_norm = torch.from_numpy(node_norm).view(-1, 1)

        if use_cuda and not args.no_cuda:
            node_id = node_id.cuda()
            edge_type, node_norm = edge_type.cuda(), node_norm.cuda()

        g.ndata.update({'id': node_id, 'norm': node_norm})
        g.edata['type'] = edge_type

        batch_size = args.decoder_batch_size
        e1_keys = list(train_e1_to_multi_e2.keys())
        sub_e1_keys = {}

        # Add inverse edges to training samples
        src, dst = np.concatenate((cur_train_data[:, 0], cur_train_data[:, 2])), \
                   np.concatenate((cur_train_data[:, 2], cur_train_data[:, 0]))
        rel = cur_train_data[:, 1]
        rel = np.concatenate((rel, rel + num_rels))
        cur_train_data = np.stack((src, rel, dst)).transpose()

        # The loop below constructs a dict for the decoding step
        # with the key (src, rel) and the value as the list of nodes present in the original graph
        # where the source and target nodes both belong to the list of sampled nodes in subgraph

        for e in cur_train_data:
            rel = e[1]
            # Don't use sim relations for decoding
            if args.sim_relations:
                if rel == num_rels - 1 or rel == (num_rels * 2) - 1:
                    continue
                elif rel >= num_rels:
                    rel -= 1

            if e[0] in node_id_copy and e[2] in node_id_copy:
                subgraph_src_idx = node_dict[e[0]]
                subgraph_tgt_idx = node_dict[e[2]]
                if (subgraph_src_idx, rel) not in sub_e1_keys:
                    sub_e1_keys[(subgraph_src_idx, rel)] = [subgraph_tgt_idx]
                else:
                    sub_e1_keys[(subgraph_src_idx, rel)].append(subgraph_tgt_idx)

        key_list = list(sub_e1_keys.keys())

        random.shuffle(key_list)
        cum_loss = 0.0

        for i in range(0, len(key_list), batch_size):

            optimizer.zero_grad()

            # compute graph embeddings
            graph_embeddings = model.get_graph_embeddings(g, epoch)
            #model.decoder.module.cur_embedding = graph_embeddings
            model.decoder.cur_embedding = graph_embeddings

            batch = key_list[i: i + batch_size]

            # Don't train with batches of size 1 and always set batch_size > 1 since batch norm
            # fails with batch_size=1
            if len(batch) == 1:
                continue

            e1 = torch.LongTensor([elem[0] for elem in batch])
            rel = torch.LongTensor([elem[1] for elem in batch])

            # e2 -> list of target nodes in subgraph
            e2 = [sub_e1_keys[(k[0], k[1])] for k in batch]
            batch_len = len(batch)

            if use_cuda and not args.no_cuda and not cpu_decoding:
                target = torch.cuda.FloatTensor(batch_len, node_id_copy.shape[0]).fill_(0)
                e1 = e1.cuda()
                rel = rel.cuda()
            else:
                target = torch.zeros((batch_len, node_id_copy.shape[0]))

            # construct target tensor
            for j, inst in enumerate(e2):
                target[j, inst] = 1.0

            # perform label smoothing
            target = ((1.0 - args.label_smoothing_epsilon) * target) + (1.0 / target.size(1))

            if cpu_decoding:
                graph_embeddings = graph_embeddings.cpu()
                model.decoder.cpu()
                model.decoder.no_cuda = True

            t0 = time.time()

            loss = model.get_score(e1, rel, target, graph_embeddings)
            loss = torch.mean(loss)
            cum_loss += loss.cpu().item()
            t1 = time.time()
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_norm)  # clip gradients
            optimizer.step()

            t2 = time.time()

            forward_time.append(t1 - t0)
            backward_time.append(t2 - t1)

            del graph_embeddings, target, batch, loss, e1, rel, e2

            # the below make training very slow
            # gc.collect()
            # torch.cuda.empty_cache()

        print("Epoch {:04d} | Loss {:.4f} | Best MRR {:.4f} | Forward {:.4f}s | Backward {:.4f}s".
              format(epoch, cum_loss, best_mrr, forward_time[-1], backward_time[-1]))
        # writer.add_scalar('data/loss', cum_loss , epoch)

        # Save model every 100 epochs
        # if epoch + 1 % 100==0:
        #    print("saving current model..")
        #    torch.save({'state_dict': model.state_dict(), 'epoch': epoch},
        #                 model_state_file)

        # validation
        if epoch % args.evaluate_every == 0:
            # perform validation on CPU when full graph is too large
            if use_cuda and not args.no_cuda and cpu_eval:
                model.cpu()
                test_graph.ndata['id'] = test_graph.ndata['id'].cpu()
                test_graph.ndata['norm'] = test_graph.ndata['norm'].cpu()
                test_graph.edata['type'] = test_graph.edata['type'].cpu()
                model.decoder.no_cuda = True

            model.eval()
            print("start eval")

            print("===========DEV============")
            mrr = evaluation_utils.ranking_and_hits(test_graph, model, valid_data, all_e1_to_multi_e2,
                                                    train_network, fusion="graph-only", sim_relations=args.sim_relations,
                                                    debug=args.debug, epoch=epoch)

            # writer.add_scalar('data/mrr', mrr, epoch)

            # save best model
            # torch.save({'state_dict': model.state_dict(), 'epoch': epoch},
            #                model_state_file)
            if mrr < best_mrr:
                if epoch >= args.n_epochs:
                    break
            else:
                best_mrr = mrr
                print("[saving best model so far]")
                torch.save({'state_dict': model.state_dict(), 'epoch': epoch},
                           model_state_file)

            metrics = {"best_mrr": best_mrr,
                       "cum_loss": cum_loss
                       }

            with open(os.path.join(args.output_dir, 'metrics.json'), 'w') as f:
                f.write(json.dumps(metrics))

            # transfer graph back to gpu device
            if use_cuda and not args.no_cuda and cpu_eval:
                model.cuda()
                test_graph.ndata['id'] = test_graph.ndata['id'].cuda()
                test_graph.ndata['norm'] = test_graph.ndata['norm'].cuda()
                test_graph.edata['type'] = test_graph.edata['type'].cuda()
                model.decoder.no_cuda = False

    print("training done")
    print("Mean forward time: {:4f}s".format(np.mean(forward_time)))
    print("Mean Backward time: {:4f}s".format(np.mean(backward_time)))

    # writer.export_scalars_to_json("./all_scalars.json")
    # writer.close()

    print("\nStart testing")

    # use best model checkpoint
    checkpoint = torch.load(model_state_file)
    model.eval()
    model.load_state_dict(checkpoint['state_dict'])
    print("Using best epoch: {}".format(checkpoint['epoch']))

    evaluation_utils.ranking_and_hits(test_graph, model, test_data, all_e1_to_multi_e2, train_network, fusion="graph-only",
                                      sim_relations=args.sim_relations)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Options for Commonsense Knowledge Base Completion')

    # General
    parser.add_argument("-d", "--dataset", type=str, required=True,
                        help="dataset to use")
    parser.add_argument("--sim_relations", action='store_true', default=False,
                        help="add similarity edges when constructing graph")
    parser.add_argument("--sim_sim", action='store_true', default=False,
                        help="add sim-sim edges to graph")
    parser.add_argument("--load_model", type=str, default=None, help="Path to model file")
    parser.add_argument("--decoder", type=str, default='ConvTransE', help="decoder used to compute scores")
    parser.add_argument("--n-epochs", type=int, default=200,
                        help="number of minimum training epochs")
    parser.add_argument("--evaluate-every", type=int, default=10,
                        help="perform evaluation every n epochs")
    parser.add_argument("--output_dir", type=str, required=False, default="saved_models",
                        help="output directory to store metrics and model file")
    parser.add_argument("--bert_concat", action='store_true', default=False,
                        help="concat bert embeddings before decoder layer")
    parser.add_argument("--bert_sum", action='store_true', default=False,
                        help="sum bert embeddings before decoder layer")
    parser.add_argument("--bert_mlp", action='store_true', default=False,
                        help="use mlp after concatenated bert+gcn embeddings before decoder layer")
    parser.add_argument("--tying", action='store_true', default=False,
                        help="tie input bert layer to gcn with concatenated tensor before decoding")
    parser.add_argument("--cpu_decoding", action='store_true', default=False,
                        help="perform decoding on cpu")
    parser.add_argument("--eval_only", action='store_true', default=False,
                        help="only evaluate using an existing model")
    parser.add_argument("--write_results", action='store_true', default=False,
                        help="write top-k candidate tuples for evaluation set to file")
    parser.add_argument("--eval-batch-size", type=int, default=500,
                        help="batch size when evaluating")
    parser.add_argument("--gpu", type=int, default=-1,
                        help="gpu")
    parser.add_argument("--no_cuda", action='store_true', default=False,
                        help="prevents using cuda")
    parser.add_argument("--seed", type=int, default=42,
                        help="random seed value")
    parser.add_argument("--debug", action='store_true', default=False,
                        help="use fewer eval instances in debugging mode")

    # GCN
    parser.add_argument("--init_embedding_dim", type=int, default=200,
                        help="embedding dimension of input to GCN")
    parser.add_argument("--input_layer", type=str, default="lookup",
                        help="initialization layer for GCN")
    parser.add_argument("--n-bases", type=int, default=100,
                        help="number of weight blocks for each relation (for RGCN)")
    parser.add_argument("--n-layers", type=int, default=2,
                        help="number of propagation rounds")
    parser.add_argument("--gcn_type", type=str, default="WGCNAttentionLayer",
                        help="type of GCN to be used (class name)")

    # Miscellaneous Hyperparameters
    parser.add_argument("--lr", type=float, default=1e-4,
                        help="learning rate")
    parser.add_argument("--dropout", type=float, default=0.2,
                        help="dropout probability")
    parser.add_argument("--input_dropout", type=float, default=0.2,
                        help="input dropout")
    parser.add_argument("--feature_map_dropout", type=float, default=0.2,
                        help="feature map dropout")
    parser.add_argument("--label_smoothing_epsilon", type=float, default=0.1,
                        help="epsilon for performing label smoothing")
    parser.add_argument("--embedding_dim", type=int, default=200,
                        help="output embedding dimension of GCN")
    parser.add_argument("--n-hidden", type=int, default=200,
                        help="number of hidden units")
    parser.add_argument("--use_bias", action='store_true', default=False,
                        help="use bias")
    parser.add_argument("--regularization", type=float, default=0.1,
                        help="regularization weight")
    parser.add_argument("--grad-norm", type=float, default=1.0,
                        help="norm to clip gradient to")
    parser.add_argument("--graph-batch-size", type=int, default=30000,
                        help="number of edges to sample in each iteration")
    parser.add_argument("--negative-sample", type=int, default=0,
                        help="number of negative samples per positive sample")
    parser.add_argument("--decoder_batch_size", type=int, default=128,
                        help="batch size for decoder")
    parser.add_argument("--layer_norm", action='store_true', default=False,
                        help="use layer normalization on embeddings fed to decoder")

    args = parser.parse_args()
    print(args)
    try:
        main(args)
    except KeyboardInterrupt:
        print('Interrupted')
        # writer.export_scalars_to_json("./all_scalars.json")
        # writer.close()


from state_ae.data import get_loader
import matplotlib.pyplot as plt

loader = get_loader(dataset="mnist", total_samples=2, image_size=(64, 64))

i, img = list(enumerate(loader))[0]

plt.axis('off')
plt.imshow(img[0].permute(1,2,0), cmap="Greys_r")
plt.show()
plt.axis('off')
plt.imshow(img[1].permute(1,2,0), cmap="Greys_r")
plt.show()
plt.axis('off')
plt.imshow(img[2].permute(1,2,0), cmap="Greys_r")
plt.show()


# -*- coding: utf-8 -*-
import torch #has pytorch which contains dynamic graph. Helps to build the gradients to update weight for back propagation.
from torch.autograd import Variable #stores a tensor and a gradient in a single variable.
import cv2 #to only draw rectancles around the image, not used for any detection.
from data import BaseTransform, VOC_CLASSES as labelmap #to transform as per the neural network and for mapping.
from ssd import build_ssd # constructor which will build the architecture of ssd
import imageio #to process the images in the video

#detect function to detect a single image. imageio will be used for a video
def detect(frame, net, transform): #4 tranformation are to be done before feeding it in the neural network.
    height, width = frame.shape[:2]
    t_frame = transform(frame)[0] #1st tranform for right dimentions and colour.
    x = torch.from_numpy(t_frame).permute(2,0,1) #2nd transform to convert numpy array to tensor state.
    x = Variable(x.unsqueeze(0)) #3rd and 4th transform to make it into batches and assign it to a variable.
    y = net(x) #apply x to the neural network and store it in y.\
    detections = y.data #Tensor detections contain data attribute of y.
    scale = torch.Tensor([width, height, width, height]) #to normalize the position of image between 0 and 1
    # detections = [batch, no of classes, no of occurences, [score, x0, y0, x1, y1]]
    for i in range(detections.size(1)):
        j = 0
        while detections[0,i,j,0] >= 0.6: #if no of occurences crosses the thrushold
            pt = (detections[0,i,j,1:] * scale).numpy() #assigns the coordinates
            cv2.rectangle(frame, (int(pt[0]), int(pt[1])), (int(pt[2]), int(pt[3])), (255,0,0), 2)
            cv2.putText(frame, labelmap[i - 1], (int(pt[0]), int(pt[1])), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 2, cv2.LINE_AA)
            j += 1
    return frame

#Creating SSD neural network
net = build_ssd('test') #we use test phase cause we have a trained model
net.load_state_dict(torch.load('ssd300_mAP_77.43_v2.pth', map_location = lambda storage, loc:storage)) #the weights are attributed to net
transform = BaseTransform(net.size, (104/256.0, 117/256.0, 123/256.0)) #tranform the image according to the trained neural network.

#doing object detection on a video
reader = imageio.get_reader('input.mp4')
fps = reader.get_meta_data()['fps']
writer = imageio.get_writer('output.mp4', fps = fps)
for i,frame in enumerate(reader):
     frame = detect(frame, net.eval(), transform)
     writer.append_data(frame)
     print(i)
writer.close()


import json
import os
from twilio.rest.lookups import TwilioLookupsClient
from twilio.rest import TwilioRestClient
from flask import render_template, url_for, request, jsonify
from flask.ext.login import login_required
from twilio import twiml
from app import csrf
from .. import db
from ..models import EditableHTML, Resource, Rating, Descriptor, OptionAssociation, RequiredOptionDescriptor
from . import main
from wtforms.fields import SelectMultipleField, TextAreaField
from ..single_resource.forms import SingleResourceForm
from datetime import datetime
from collections import OrderedDict

@main.route('/')
def index():
    req_opt_desc = RequiredOptionDescriptor.query.all()
    req_opt_id = -1
    if req_opt_desc:
        req_opt_desc = req_opt_desc[0]
        req_opt_desc = Descriptor.query.filter_by(
            id=req_opt_desc.descriptor_id
        ).first()
        if req_opt_desc is not None:
            req_opt_id = req_opt_desc.id
    options = Descriptor.query.all()
    options = [o for o in options if len(o.text_resources) == 0 and o.id != req_opt_id]
    options_dict = {}
    for o in options:
        # Sort descriptor values alphabetically in filters
        o.values.sort()
        options_dict[o.name] = o.values
    # Sort descriptors alphabetically
    options_dict = OrderedDict(sorted(options_dict.items(), key=lambda x: x[0]))
    req_options = {}
    if req_opt_desc:
        for val in req_opt_desc.values:
            req_options[val] = False
        # Sort required descriptor values alphabetically in filters
        req_options = OrderedDict(sorted(req_options.items(), key=lambda x: x[0]))
    return render_template('main/index.html', options=options_dict, req_options=req_options, req_desc=req_opt_desc)

@main.route('/get-resources')
def get_resources():
    resources = Resource.query.all()
    resources_as_dicts = Resource.get_resources_as_dicts(resources)
    return json.dumps(resources_as_dicts)

@main.route('/search-resources')
def search_resources():
    name = request.args.get('name')
    if name is None:
        name = ""
    req_options = request.args.getlist('reqoption')
    if req_options is None:
        req_options = []
    # case insensitive search
    resource_pool = Resource.query.filter(Resource.name.ilike('%{}%'.format(name))).all()
    req_opt_desc = RequiredOptionDescriptor.query.all()
    if req_opt_desc:
        req_opt_desc = req_opt_desc[0]
        req_opt_desc = Descriptor.query.filter_by(
            id=req_opt_desc.descriptor_id
        ).first()
    resources = []
    if req_opt_desc and len(req_options) > 0:
        int_req_options = []
        for o in req_options:
            if str(o) in req_opt_desc.values:
                int_req_options.append(req_opt_desc.values.index(str(o)))
        for resource in resource_pool:
            associations = OptionAssociation.query.filter_by(
                resource_id=resource.id,
                descriptor_id=req_opt_desc.id
            )
            for a in associations:
                if a.option in int_req_options:
                    resources.append(resource)
                    break
    opt_options = request.args.getlist('optoption')
    option_map = {}
    # Create a dict, option_map, that maps from option names to a list of user selected values
    for opt in opt_options:
        if opt != "null":
            option_val = opt.split(',')
            for opt_val in option_val:
                key_val = opt_val.split(':')
                if key_val[0] in option_map:
                    option_map[key_val[0]].append(key_val[1])
                else:
                    option_map[key_val[0]] = [key_val[1]]

    descriptors = Descriptor.query.all()
    new_pool = resource_pool
    if len(req_options) > 0:
        new_pool = resources
        resources = []
    # Iterate through resources and check that there's a match for all of the options
    # that the user selected. If there is, add that resource to the list of resources
    for resource in new_pool:
        number_of_options_found = 0
        for opt in option_map.keys():
            opt_descriptors = OptionAssociation.query.filter_by(
                resource_id=resource.id
            )
            for desc in opt_descriptors:
                if desc.descriptor.name == opt:
                    if desc.descriptor.values[desc.option] in option_map[opt]:
                        number_of_options_found += 1
                        break
        if number_of_options_found == len(option_map.keys()):
            resources.append(resource)
    resources_as_dicts = Resource.get_resources_as_dicts(resources)
    return json.dumps(resources_as_dicts)

@main.route('/get-associations/<int:resource_id>')
def get_associations(resource_id):
    resource = Resource.query.get(resource_id)
    associations = {}
    if resource is None:
        return json.dumps(associations)
    for td in resource.text_descriptors:
        associations[td.descriptor.name] = td.text
    for od in resource.option_descriptors:
        val = od.descriptor.values[od.option]
        values = set()
        # multiple option association values
        if associations.get(od.descriptor.name):
            curr = associations.get(od.descriptor.name)
            curr.append(val)
            values = set(curr)
        else:
            values.add(val)
        associations[od.descriptor.name] = list(values)
    return json.dumps(associations)

@main.route('/about')
def about():
    editable_html_obj = EditableHTML.get_editable_html('about')
    return render_template('main/about.html',
                           editable_html_obj=editable_html_obj)

@main.route('/health')
def health():
    editable_html_obj = EditableHTML.get_editable_html('health')
    return render_template('main/health.html',
                           editable_html_obj=editable_html_obj)

@main.route('/rights')
def rights():
    editable_html_obj = EditableHTML.get_editable_html('rights')
    return render_template('main/rights.html',
                           editable_html_obj=editable_html_obj)

@main.route('/hotlines')
def hotlines():
   editable_html_obj = EditableHTML.get_editable_html('hotlines')
   return render_template('main/hotlines.html',
                          editable_html_obj=editable_html_obj)

@main.route('/overview')
@login_required
def overview():
   editable_html_obj = EditableHTML.get_editable_html('overview')
   return render_template('main/overview.html',
                          editable_html_obj=editable_html_obj)

@main.route('/instructions')
def instructions():
   editable_html_obj = EditableHTML.get_editable_html('instructions')
   return render_template('main/instructions.html',
                          editable_html_obj=editable_html_obj)

@main.route('/adulting')
def adulting():
 editable_html_obj = EditableHTML.get_editable_html('adulting')
 return render_template('main/adulting.html',
                        editable_html_obj=editable_html_obj)

@main.route('/update-editor-contents', methods=['POST'])
@login_required
def update_editor_contents():
    """Update the contents of an editor."""
    edit_data = request.form.get('edit_data')
    editor_name = request.form.get('editor_name')
    editor_contents = EditableHTML.query.filter_by(
        editor_name=editor_name).first()
    if editor_contents is None:
        editor_contents = EditableHTML(editor_name=editor_name)
    editor_contents.value = edit_data
    db.session.add(editor_contents)
    db.session.commit()
    return 'OK', 200

@csrf.exempt
@main.route('/send-sms', methods=['POST'])
def send_sms():
    sid = os.environ.get('TWILIO_ACCOUNT_SID')
    auth = os.environ.get('TWILIO_AUTH_TOKEN')
    client = TwilioLookupsClient(account=sid, token=auth)
    send_client = TwilioRestClient(account=sid, token=auth)
    if request is not None:
        phone_num= request.json['number']
        resourceID = request.json['id']
        curr_res = Resource.query.get(resourceID)
        name = "Name: " + curr_res.name
        address = "Address: " + curr_res.address
        message = name +"\n" + address
        try:
            number = client.phone_numbers.get(phone_num, include_carrier_info=False)
            num = number.phone_number
            send_client.messages.create(
                to=num,
                from_=os.environ.get('TWILIO_NUMBER'),
                body=message)
            return jsonify(status='success')
        except:
            return jsonify(status='error')

@csrf.exempt
@main.route('/rating-post', methods =['POST'])
def post_rating():
    if request is not None:
            time = datetime.now()
            star_rating = request.json['rating']
            comment = request.json['review']
            resourceID = request.json['id']
            if comment and star_rating:
                rating = Rating(submission_time=time,
                                rating=star_rating,
                                review=comment,
                                resource_id=resourceID)
                db.session.add(rating)
                db.session.commit()
            elif star_rating:
                rating = Rating(submission_time=time,
                                rating=star_rating,
                                resource_id=resourceID)
                db.session.add(rating)
                db.session.commit()
    return jsonify(status='success')


# vim: set fileencoding=utf-8:

from arnaldo.modules import Arnaldigno, comanda
from arnaldo.brain import brain
import time
import re
import datetime

runicode = r"u'\\N{(.*?)}'"

class BAM(Arnaldigno):
    def __init__(self, *args):
        super(BAM, self).__init__(*args)
        self.BAM = None

    @comanda(".")
    def BAMBAM(self, e, match):
        brain.set(e.source.nick, time.time())
        t = e.arguments[0]
        runi = re.search(runicode, t)
        if runi is not None:
            try:
                self.r(e, "%s" % eval(runi.group()))
            except Exception:
                pass
        if self.BAM == t:
            self.r(e, self.BAM)
            self.BAM = None
        else:
            try:
                if self.BAM.lower() == self.BAM and self.BAM.upper() == t:
                    marks = re.compile("([!?.;:]+)$")
                    m = marks.search(t)
                    if m:
                        m = m.groups()[0]
                        t = marks.sub("", t)
                    else:
                        m = ""
                    t = re.sub("i?[aeiou]$", "", t, flags=re.IGNORECASE)
                    self.r(e, "%sISSIMO%s" % (t, m))
                    self.BAM = None
                else:
                    self.BAM = t
            except:
                self.BAM = t

        return True

    @comanda("^arnaldo hai visto (.+)\\?")
    def chilhavisto(self, e, match):
        try:
            ggallin = match.groups()[0]
        except:
            ggallin = None

        if not ggallin:
            return

        try:
            ts = brain.get(ggallin)
            if ts:
                response = "chiaro il %s" % datetime.datetime.fromtimestamp(
                    float(ts)
                ).strftime("%d/%m/%y %H:%M:%S")
            else:
                response = "macche'"
            self.r(e, response)
        except:
            pass


import json

dict = {'Python2': '.py2', 'C++2': '.cpp2', 'Java2': '.java2'}
data = json.load(open('dict.json'))
data.update(dict)

json.dump(data, open('recent.json', "w"))
#for first time make json
#json = json.dumps(dict)

f = open("dict.json", "w")
f.write(json)
f.close()


import sys, os
import selectors
import json
import io
import struct
import string
import random
import pickle
from functools import reduce

sys.path.append(os.path.abspath(os.path.join('.')))
sys.path.append(os.path.abspath(os.path.join('..')))

import cryptography
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from dominoes.deck_utils import Player
from utils import Colors as Colors
from security.symCiphers import AESCipher
from security.asymCiphers import readPublicKeyFromPEM, RSAKeychain
from security.handCommit import *
from security.hashFunctions import *

# Main socket code from https://realpython.com/python-sockets/

class Message:
    def __init__(self, selector, sock, addr, request, player, keychain, player_cc, aes_cipher=None, cheater=None):
        self.selector = selector
        self.sock = sock
        self.addr = addr
        self.player = player
        self.keychain = keychain
        self.cc = player_cc
        self.aes_cipher = aes_cipher
        self.aes_player_keys = {}
        self.exchange_aes = None
        self.request = request
        self._recv_buffer = b""
        self._send_buffer = b""
        self._request_queued = False
        self.response = None
        self.cheater = cheater

    def process_events(self, mask):
        if mask & selectors.EVENT_READ:
            self.read()
        if mask & selectors.EVENT_WRITE:
            self.write()

    def read(self):
        self._read()
        self.process_response()

    def write(self):
        if not self._request_queued:
            self.queue_request()

        self._write()

        if self._request_queued:
            if not self._send_buffer:
                # Set selector to listen for read events, we're done writing.
                self._set_selector_events_mask("r")

    def close(self):
        print("closing connection to", self.addr)
        try:
            self.selector.unregister(self.sock)
        except Exception as e:
            print(
                "error: selector.unregister() exception for",
                f"{self.addr}: {repr(e)}",
            )

        try:
            self.sock.close()
        except OSError as e:
            print(
                "error: socket.close() exception for",
                f"{self.addr}: {repr(e)}",
            )
        finally:
            # Delete reference to socket object for garbage collection
            self.sock = None

    def queue_request(self):
        if "content" in self.request:
            content = self.request["content"]
        elif "action" in self.request:
            content = self.request
        req = {
            "content_bytes": self._pickle_encode(content),
        }
        message = self._create_message(**req)
        self._send_buffer += message
        self._request_queued = True

    def process_response(self):
        data = self._recv_buffer
        self.response = self._pickle_decode(data)
        #print("received response", repr(self.response), "from", self.addr)
        self._process_response_json_content()
        self._recv_buffer = b""

    # -----------------------------------------------------------Private Methods------------------------------------------------------------------

    def _set_selector_events_mask(self, mode):
        """Set selector to listen for events: mode is 'r', 'w', or 'rw'."""
        if mode == "r":
            events = selectors.EVENT_READ
        elif mode == "w":
            events = selectors.EVENT_WRITE
        elif mode == "rw":
            events = selectors.EVENT_READ | selectors.EVENT_WRITE
        else:
            raise ValueError(f"Invalid events mask mode {repr(mode)}.")
        self.selector.modify(self.sock, events, data=self)

    def _read(self):
        try:
            # Should be ready to read
            data = self.sock.recv(16384)
        except BlockingIOError:
            # Resource temporarily unavailable (errno EWOULDBLOCK)
            pass
        else:
            if data:
                self._recv_buffer += data
            else:
                raise RuntimeError("Peer closed.")

    def _write(self):
        if self._send_buffer:
            #print("sending", repr(self._send_buffer), "to", self.addr)
            try:
                # Should be ready to write
                sent = self.sock.send(self._send_buffer)
            except BlockingIOError:
                # Resource temporarily unavailable (errno EWOULDBLOCK)
                pass
            else:
                self._send_buffer = self._send_buffer[sent:]

    def _json_encode(self, obj, encoding):
        return json.dumps(obj, ensure_ascii=False).encode(encoding)

    def _json_decode(self, json_bytes, encoding):
        tiow = io.TextIOWrapper(io.BytesIO(json_bytes), encoding=encoding, newline="")
        obj = json.load(tiow)
        tiow.close()
        return obj

    def _pickle_encode(self, obj):
        return pickle.dumps(obj)

    def _pickle_decode(self, pickle_bytes):
        return pickle.loads(pickle_bytes)

    def _create_message(self, content_bytes):
        message = content_bytes
        return message

    def _create_request(self, action):
        return dict(content=dict(action=action))

    def _handle_login(self):
        server_cert_PEM = self.response.get("server_cert")
        server_cert = cryptography.x509.load_pem_x509_certificate(server_cert_PEM, default_backend())
        server_pub_key_PEM = server_cert.public_key().public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)
        nickname = ''.join(random.choices(string.ascii_uppercase + string.digits, k=4))  # input(data["msg"])
        #signature, data = self.cc.signData(nickname)
        cert = self.cc.get_signature_cert()
        # cc_pubKey = self.cc.get_pubKey()
        print(Colors.BYellow + "Your name is " + Colors.BBlue + nickname + Colors.Color_Off)
        msg = {"action": "req_login", "pubkey": self.keychain.exportPubKey(), "msg": nickname, "cert": cert}
        signature, data = self.cc.signData(pickle.dumps(msg))
        msg.update({"signature": signature, "data": data})
        self.player = Player(nickname, self.sock, self.cheater)
        self.player.server_pub_key = readPublicKeyFromPEM(server_pub_key_PEM)
        return msg

    def _handle_you_host(self):
        print(Colors.Yellow, "Checking if session key message was compromised", Colors.Color_Off)
        if not self.keychain.verify_sign(self.response.get("session_key"), self.response.get("signed_session_key"), self.player.server_pub_key):
                print(Colors.Red, "Messages has been compromised. Shutting Down!", Colors.Color_Off)
                exit(-1)
        print(Colors.BGreen, "Message integrity not compromised", Colors.Color_Off)
        aes_secret = self.keychain.decrypt(self.response.get("session_key"))
        self.aes_cipher = AESCipher(aes_secret)
        print("Session", aes_secret)
        self.player.server_aes_cipher = AESCipher(aes_secret)
        self.player.host = True
        print(Colors.Blue + "Player " + self.player.name + "is hosting the game!" + Colors.Color_Off)

    def _handle_new_player(self):
        if "session_key" in self.response:
            print(Colors.Yellow, "Checking if session key message was compromised", Colors.Color_Off)
            if not self.keychain.verify_sign(self.response.get("session_key"), self.response.get("signed_session_key"), self.player.server_pub_key):
                print(Colors.Red, "Messages has been compromised. Shutting Down!", Colors.Color_Off)
                exit(-1)
            print(Colors.BGreen, "Message integrity not compromised", Colors.Color_Off)
            aes_secret = self.keychain.decrypt(self.response.get("session_key"))
            print("Session", aes_secret)
            self.aes_cipher = AESCipher(aes_secret)
            self.player.server_aes_cipher = AESCipher(aes_secret)
        print(self.response.get("msg"))
        print("There are " + str(self.response.get("nplayers")) + "\\" + str(self.response.get("game_players")))
        self.player.nplayers = self.response.get("nplayers")

    def _handle_key_exchange(self):
        print(self.response.get("msg"))
        if "session_keys" in self.response:
            aes_exchange_keys = {}
            aes_keys = {}
            signed_aes_exchange_keys = {}
            signed_aes_keys = {}
            print(Colors.Yellow, "Checking if server message was compromised", Colors.Color_Off)
            if not self.keychain.verify_sign(pickle.dumps(self.response.get("session_keys")), self.response.get("signed_session_keys"), self.player.server_pub_key):
                print(Colors.Red, "Key Exchange has been compromised. Shutting Down!", Colors.Color_Off)
                exit(-1)
            print(Colors.BGreen, "Key Exchange integrity not compromised", Colors.Color_Off)
            players_pub_keys = self.response.get("session_keys")
            list_of_keys = list(players_pub_keys.keys())
            for keys in list_of_keys:
                if keys not in self.player.name:
                    self.exchange_aes = AESCipher()
                    encrypted_secret = self.keychain.encrypt(self.exchange_aes.secret,
                                                             readPublicKeyFromPEM(players_pub_keys[keys]))
                    aes_exchange_keys[keys] = encrypted_secret
                    signed_aes_exchange_keys[keys] = self.keychain.sign(encrypted_secret)
                    aes_keys[self.player.name] = aes_exchange_keys
                    signed_aes_keys[self.player.name] = signed_aes_exchange_keys
                    self.player.aes_player_keys_dec[keys] = self.exchange_aes
            msg = {"action": "aes_exchange", "aes_keys": aes_keys, "signed_aes_keys": signed_aes_keys}
        return msg

    def _handle_receiving_aes(self):
        if "aes_key" in self.response:
            aes_key = self.response.get("aes_key")
            signed_aes_key = self.response.get("signed_aes_key")
            print(aes_key)
            if self.player.name in self.response.get("player_receive"):
                for key in aes_key:
                    print(Colors.Yellow, "Checking if session key between clients message was compromised", Colors.Color_Off)
                    if not self.keychain.verify_sign(aes_key[key], signed_aes_key[key], readPublicKeyFromPEM(self.player.player_pub_keys[key])):
                        print(Colors.Red, key, "session key has been compromised. Shutting Down!", Colors.Color_Off)
                        exit(-1)
                    print(Colors.BGreen, key, "session key not compromised", Colors.Color_Off)
                    self.player.aes_player_keys[key] = aes_key[key]
                    print(aes_key[key])
                    print(self.player.aes_player_keys[key])

    def _handle_keys_exchanged(self):
        print(self.response.get("msg"))
        list_of_keys = list(self.player.aes_player_keys.keys())
        for key in list_of_keys:
            aes_secret = self.keychain.decrypt(self.player.aes_player_keys[key])
            self.player.aes_player_keys_dec[key] = AESCipher(aes_secret)

        print(self.player.name)
        for secret in self.player.aes_player_keys_dec:
            print("RESULTADO", secret, self.player.aes_player_keys_dec[secret].secret)

        if len(
                self.player.aes_player_keys_dec) >= self.player.nplayers - 1 and not self.player.already_have_player_keys:
            msg = {"action": "finished_setup"}
            self.player.already_have_player_keys = True
            return msg

    def _handle_waiting_for_host_as_host(self):
        input(Colors.BGreen + "PRESS ENTER TO START THE GAME" + Colors.Color_Off)
        msg = {"action": "start_game"}
        return msg

    def _handle_waiting_for_host_as_player(self):
        print(self.response.get("msg"))

    def _handle_host_start_game(self):
        print(self.response.get("msg"))
        msg = {"action": "get_game_properties"}
        return msg

    def _handle_randomization_stage(self):
        deck = self.response.get("pseudo_deck")
        new_deck = []

        self.player.randomized_tuple_mapping = {}

        print(Colors.Yellow + "Ciphering each piece in deck" + Colors.Color_Off)

        for piece in deck:
            new_cipher = AESCipher()
            ciphertext, nonce, auth_tag = new_cipher.encrypt_aes_gcm(pickle.dumps(piece))
            # If collision exists, generates new key encrypts again
            while ciphertext in new_deck:
                print(Colors.Red, "Cipher already existed. Generating a new one", Colors.Color_Off)
                new_cipher = AESCipher()
                ciphertext, nonce, auth_tag = new_cipher.encrypt_aes_gcm(pickle.dumps(piece))

            self.player.randomized_tuple_mapping[new_cipher.secret] = (ciphertext, nonce, auth_tag)
            new_deck.append(ciphertext)

        random.shuffle(new_deck)

        return {'action': 'next_randomization_step', 'deck': new_deck}

    def _handle_start_selection_stage(self):
        # Picks a piece from the deck or passes, shuffles and sends to another player
        pseudo_deck = self.response.get("deck")
        padding = self.response.get("padding")
        self.player.npieces = self.response.get("pieces_per_player")

        print(Colors.Yellow + "Do you pick or pass?" + Colors.Color_Off)

        if random.random() < 0.05:
            print(Colors.Green + "Selecting a piece" + Colors.Color_Off)
            random.shuffle(pseudo_deck)
            padding.append(os.urandom(sys.getsizeof(pseudo_deck[-1])))
            self.player.encrypted_hand.append(pseudo_deck.pop())
        else:
            random.shuffle(pseudo_deck)

        players_nicks = list(self.player.aes_player_keys_dec.keys())
        player_to_send_deck = random.choice(players_nicks)

        encrypted_message = pickle.dumps({'action': "selection_stage", "deck": pseudo_deck,
                                          'pieces_per_player': self.response.get("pieces_per_player"),
                                          "stock_low": self.response.get("stock_low"), "padding": padding})

        encrypted_tuple = self.player.aes_player_keys_dec[player_to_send_deck].encrypt_aes_gcm(encrypted_message)

        msg = {'action': 'send_to_player', 'sender': self.player.name, 'rec': player_to_send_deck,
               'to_send': encrypted_tuple}

        return msg

    def _handle_selection_stage(self):
        # Picks a piece from the deck or passes, shuffles and sends to another player
        pseudo_deck = self.response.get("deck")
        padding = self.response.get("padding")
        self.player.npieces = self.response.get("pieces_per_player")
        print(Colors.Yellow + "Do you pick, substitute or pass?" + Colors.Color_Off)

        players_nicks = list(self.player.aes_player_keys_dec.keys())

        if len(self.player.encrypted_hand) < self.player.npieces:
            if random.random() < 0.05:
                print(Colors.Green + "Selecting a piece" + Colors.Color_Off)
                random.shuffle(pseudo_deck)
                padding.append(os.urandom(sys.getsizeof(pseudo_deck[-1])))
                self.player.encrypted_hand.append(pseudo_deck.pop())
            elif random.random() < 0.50 and len(self.player.encrypted_hand) > 0:
                # Substitute already selected pieces
                number_of_pieces_to_sub = random.randint(1, len(self.player.encrypted_hand))
                print(Colors.Green + "Substituting", number_of_pieces_to_sub, "pieces" + Colors.Color_Off)

                # For a certain number of pieces, take a new piece from deck and add one from hand.
                for piece in range(0, number_of_pieces_to_sub):
                    piece_to_put_in_deck = self.player.encrypted_hand.pop()
                    self.player.encrypted_hand.append(pseudo_deck.pop())
                    pseudo_deck.append(piece_to_put_in_deck)
                    random.shuffle(pseudo_deck)
            else:
                print(Colors.Green + "Passing" + Colors.Color_Off)
                random.shuffle(pseudo_deck)

        if len(pseudo_deck) > self.response.get("stock_low"):
            player_to_send_deck = random.choice(players_nicks)

            encrypted_message = pickle.dumps({'action': "selection_stage", "deck": pseudo_deck,
                                              'pieces_per_player': self.response.get("pieces_per_player"),
                                              'stock_low': self.response.get('stock_low'), "padding": padding})

            encrypted_tuple = self.player.aes_player_keys_dec[player_to_send_deck].encrypt_aes_gcm(encrypted_message)

            msg = {'action': 'send_to_player', 'sender': self.player.name, 'rec': player_to_send_deck,
                   'to_send': encrypted_tuple}
        else:
            print(Colors.Green + "Stock has reached low level. Stopping selection" + Colors.Color_Off)
            msg = {'action': 'selection_over', "deck": pseudo_deck}

        return msg

    def _handle_commit_hand(self):
        print(Colors.Yellow, "Generating hand commitment with starting encrypted deck", Colors.Color_Off)
        self.player.hand_commit = HandCommit(self.player.encrypted_hand.copy())

        signed_commit = self.keychain.sign(pickle.dumps(self.player.hand_commit.publishCommit()))

        print(Colors.Yellow, "Sending hand commitment", Colors.Color_Off)

        msg = {"action": 'send_commit', "commit": (self.player.hand_commit.publishCommit(), signed_commit)}

        return msg

    def _handle_validate_selection(self):
        self.player.players_commits = self.response.get("commits")
        print(self.player.player_pub_keys)
        for player in self.player.players_commits:
            if not self.keychain.verify_sign(pickle.dumps(self.player.players_commits[player][0]),
                                             self.player.players_commits[player][1],
                                             readPublicKeyFromPEM(self.player.player_pub_keys[player])):
                print(Colors.BRed + "GAME NOT VALID" + Colors.Color_Off)
                exit(1)

        self.player.pseudo_starting_stock = self.response.get('stock')
        return {"action": "hands_validated"}

    def _handle_reveal_keys(self):
        print(self.response.get("msg"))

        key_tuple_dict = {}

        keys_to_send = [keys for keys in self.player.randomized_tuple_mapping.keys() if
                        self.player.randomized_tuple_mapping[keys][0] not in self.player.pseudo_starting_stock]

        for key in keys_to_send:
            key_tuple_dict[self.player.randomized_tuple_mapping[key]] = key

        aux_encrypted_hand = []

        for piece in self.player.encrypted_hand:
            for tuple_piece in key_tuple_dict:
                if piece == tuple_piece[0]:
                    decipher = AESCipher(key_tuple_dict[tuple_piece])
                    deciphered_piece = pickle.loads(decipher.decrypt_aes_gcm(tuple_piece))
                    aux_encrypted_hand.append(deciphered_piece)

        self.player.encrypted_hand = aux_encrypted_hand

        print('size', sys.getsizeof(key_tuple_dict))
        print('dict', key_tuple_dict)

        return {'action': 'revealed_keys', 'keys_dict': key_tuple_dict}

    def _handle_keys_to_reveal(self):
        key_tuple_dict = self.response.get("keys_dict")
        aux_encrypted_hand = []

        print(Colors.BYellow + "Revealing Pieces" + Colors.Color_Off)

        for piece in self.player.encrypted_hand:
            for tuple_piece in key_tuple_dict:
                if piece == tuple_piece[0]:
                    decipher = AESCipher(key_tuple_dict[tuple_piece])
                    deciphered_piece = pickle.loads(decipher.decrypt_aes_gcm(tuple_piece))
                    aux_encrypted_hand.append(deciphered_piece)

        self.player.encrypted_hand = aux_encrypted_hand

        return {"action": "waiting_for_keys"}

    def _handle_piece_key_to_reveal(self):
        key_tuple_dict = self.response.get("key_dict")

        print(Colors.BYellow + "Revealing Piece" + Colors.Color_Off)

        for tuple_piece in key_tuple_dict:
            if tuple_piece[0] == self.player.new_piece:
                decipher = AESCipher(key_tuple_dict[tuple_piece])
                deciphered_piece = pickle.loads(decipher.decrypt_aes_gcm(tuple_piece))

        self.player.new_piece = deciphered_piece
        self.player.collected_keys[deciphered_piece] = key_tuple_dict

        if self.response.get("more"):
            msg = {"action": "request_piece_reveal", 'new_piece': self.player.new_piece,
                   'new_stock': self.player.pseudo_starting_stock}
        else:
            # Maybe needs to go ciphered for safety reasons?
            msg = {"action": "request_piece_deanon", "piece": deciphered_piece}

        return msg

    def _handle_keys_sent(self):
        return {'action': 'waiting_for_keys'}

    def _handle_start_deanon_stage(self):
        pub_key_list = self.response.get("pub_key_list")
        padding = self.response.get("padding")
        self.player.npieces = self.response.get("pieces_per_player")

        if random.random() < 0.05:
            print(Colors.BGreen + "Adding Public Key To Array" + Colors.Color_Off)
            padding.pop()
            padding.insert(0, None)
            tuple_to_add = self.player.encrypted_hand.pop()
            new_key = RSAKeychain(2048)
            self.player.tuple_keychains[tuple_to_add] = new_key
            pub_key_list[tuple_to_add[0]] = new_key.exportPubKey()
        else:
            print(Colors.BGreen + "Passing" + Colors.Color_Off)

        players_nicks = list(self.player.aes_player_keys_dec.keys())
        player_to_send_deck = random.choice(players_nicks)

        encrypted_message = pickle.dumps({'action': "deanon_stage", "pub_key_list": pub_key_list,
                                          'pieces_per_player': self.response.get("pieces_per_player"),
                                          "max_pieces": self.response.get("max_pieces"), "padding": padding})

        encrypted_tuple = self.player.aes_player_keys_dec[player_to_send_deck].encrypt_aes_gcm(encrypted_message)

        msg = {'action': 'send_to_player', 'sender': self.player.name, 'rec': player_to_send_deck,
               'to_send': encrypted_tuple}

        return msg

    def _handle_deanon_stage(self):
        pub_key_list = self.response.get("pub_key_list")
        padding = self.response.get("padding")
        self.player.npieces = self.response.get("pieces_per_player")

        players_nicks = list(self.player.aes_player_keys_dec.keys())

        if len(self.player.encrypted_hand) > 0:
            if random.random() < 0.05:
                print(Colors.BGreen + "Adding Public Key To Array" + Colors.Color_Off)
                padding.pop()
                padding.insert(0, None)
                new_key = RSAKeychain(2048)
                tuple_to_add = self.player.encrypted_hand.pop()
                self.player.tuple_keychains[tuple_to_add] = new_key
                pub_key_list[tuple_to_add[0]] = new_key.exportPubKey()
            else:
                print(Colors.BGreen + "Passing" + Colors.Color_Off)

        sum_check_done = sum(item is not None for item in pub_key_list)
        if sum_check_done < self.response.get('max_pieces'):
            player_to_send_deck = random.choice(players_nicks)

            encrypted_message = pickle.dumps({'action': "deanon_stage", "pub_key_list": pub_key_list,
                                              'pieces_per_player': self.response.get("pieces_per_player"),
                                              'max_pieces': self.response.get('max_pieces'), "padding": padding})

            encrypted_tuple = self.player.aes_player_keys_dec[player_to_send_deck].encrypt_aes_gcm(encrypted_message)

            msg = {'action': 'send_to_player', 'sender': self.player.name, 'rec': player_to_send_deck,
                   'to_send': encrypted_tuple}
        else:
            msg = {'action': 'deanon_prep_over', "pub_key_list": pub_key_list}

        return msg

    def _handle_decipher_tiles(self):
        tiles_to_decipher = self.response.get("ciphered_tiles")

        for tuple_piece in self.player.tuple_keychains:
            tile_to_decipher = tiles_to_decipher[tuple_piece[0]]
            tile_index = tuple_piece[0]
            cipher = self.player.tuple_keychains[tuple_piece]
            tile, tile_key = pickle.loads(cipher.decrypt(tile_to_decipher))
            if not hashFunctions.check_sha256_digest_from_list(tuple_piece[1], [str.encode(str(tile_index)), tile_key,
                                                                                str.encode(str(tile))]):
                print(Colors.Red + "SERVER IS CHEATING!" + Colors.Color_Off)
                exit(-1)
            self.player.insertInHand(tile)

        print(Colors.Green + "Hand has been de-anonymized!" + Colors.Color_Off)
        print("Hand -> " + ' '.join(map(str, self.player.hand)))
        return {"action": "ready_to_play"}

    def _handle_reveal_piece_key(self):
        key_tuple_dict = {}
        self.player.pseudo_starting_stock = self.response.get("new_stock")

        key_to_send = [keys for keys in self.player.randomized_tuple_mapping.keys() if
                       self.player.randomized_tuple_mapping[keys][0] in self.response.get("new_piece")]

        for key in key_to_send:
            key_tuple_dict[self.player.randomized_tuple_mapping[key]] = key

        print('size', sys.getsizeof(key_tuple_dict))
        print('dict', key_tuple_dict)

        return {'action': 'revealed_key_for_piece', 'key_dict': key_tuple_dict}

    def _handle_secret_message(self):
        if self.response.get('sender') == "server":
            cipher = self.player.server_aes_cipher
        else:
            cipher = self.player.aes_player_keys_dec[self.response.get('sender')]

        encrypted_tuple = self.response.get('msg')

        deciphered_msg = pickle.loads(cipher.decrypt_aes_gcm(encrypted_tuple))

        return deciphered_msg

    def _handle_insert_in_hand(self):
        tile, key = pickle.loads(self.response.get("new_tile"))
        print(Colors.Yellow + "Checking if server is not cheating" + Colors.Color_Off)
        if not hashFunctions.check_sha256_digest_from_list(self.player.new_piece[1],
                                                           [str.encode(str(self.player.new_piece[0])), key,
                                                            str.encode(str(tile))]):
            print(Colors.Red + "SERVER IS CHEATING!" + Colors.Color_Off)
            exit(-1)

        print(Colors.Green + "Tile is valid. Inserting in hand" + Colors.Color_Off, tile)
        self.player.insertInHand(tile)

        msg = self.player.play()
        if msg.get("action") == 'play_piece':
            piece_signature = self.keychain.sign(pickle.dumps(msg.get("piece")))
            msg.update({"signed_piece": piece_signature})
        return msg

    def _handle_validate_this_play(self):
        msg = {"action": "play_piece_ep"}
        self.player.nplayers = self.response.get("nplayers")
        self.player.npieces = self.response.get("npieces")
        self.player.pieces_per_player = self.response.get("pieces_per_player")
        self.player.in_table = self.response.get("in_table")
        last_table = self.request.get("last_table")

        if self.response.get("last_player") != self.player.name:
            print(Colors.Yellow + "Validating last played piece signature" + Colors.Color_Off)
            signed_piece = self.response.get("signed_piece")
            last_piece_played = self.response.get("last_piece")
            last_player = self.response.get("last_player")
            if not self.keychain.verify_sign(pickle.dumps(last_piece_played), signed_piece,
                                             readPublicKeyFromPEM(self.player.player_pub_keys[last_player])):
                print(Colors.Red + "This signature is not valid" + Colors.Color_Off)
                exit(-1)
            print(Colors.Green + "Last play signature is valid!" + Colors.Color_Off)

            if (self.player.validate(last_piece_played, last_table)):
                print("I don't know if the player cheated!")
                msg.update({"player_cheated": False})
            else:
                print("Cheated!")
                msg.update({"player_cheated": True})

            msg.update({"signed_player_cheated": self.keychain.sign(pickle.dumps(msg.get("player_cheated")))})

        return msg

    def _handle_rcv_game_properties(self):
        self.player.nplayers = self.response.get("nplayers")
        self.player.npieces = self.response.get("npieces")
        self.player.pieces_per_player = self.response.get("pieces_per_player")
        self.player.in_table = self.response.get("in_table")
        player_name = self.response.get("next_player")

        if self.response.get("next_player") == self.player.name:
            player_name = Colors.BRed + "YOU" + Colors.Color_Off
        print("deck -> ", len(self.player.pseudo_starting_stock), "pieces remaining")
        print("hand -> " + ' '.join(map(str, self.player.hand)))
        print("in table -> " + ' '.join(map(str, self.response.get("in_table"))) + "\n")
        print("Current player ->", player_name)
        print("next Action ->", self.response.get("next_action"))
        if self.player.name == self.response.get("next_player"):

            if self.response.get("next_action") == "get_piece":
                if not self.player.ready_to_play:
                    # input("Press ENter \n\n")
                    random.shuffle(self.player.deck)
                    piece = self.player.deck.pop()
                    self.player.insertInHand(piece)
                    msg = {"action": "get_piece", "deck": self.player.deck}
                    return msg
            if self.response.get("next_action") == "play":
                # input(Colors.BGreen+"Press ENter \n\n"+Colors.Color_Off)
                if self.player.isCheater:
                    msg = self.player.cheat_play()
                else:
                    msg = self.player.play()
                if msg.get("action") == 'play_piece':
                    piece_signature = self.keychain.sign(pickle.dumps(msg.get("piece")))
                    msg.update({"signed_piece": piece_signature})
                return msg

    def _handle_report_score(self):
        hand_commits_confirmations = self.response.get("hand_commits_confirmation")
        print(Colors.Yellow + "Validating all hand commits" + Colors.Color_Off)

        signed_msg = self.response.pop("signed_msg", None)

        print(Colors.Yellow, "Checking if Report Score message from server was compromised", Colors.Color_Off)
        if not self.keychain.verify_sign(pickle.dumps(self.response), signed_msg, self.player.server_pub_key):
                print(Colors.Red, "Report Score has been compromised. Shutting Down!", Colors.Color_Off)
                exit(-1)
        print(Colors.BGreen, "Report Score action integrity not compromised", Colors.Color_Off)

        for player_name in self.player.players_commits:

            if not verifyHandCommit(self.player.players_commits[player_name][0],
                                    hand_commits_confirmations[player_name]):
                print(Colors.Red + player_name + " Sent an Invalid Hand Commit" + Colors.Color_Off)
            else:
                print(Colors.Green + player_name + " Sent a valid Hand Commit" + Colors.Color_Off)

        print(Colors.Yellow + "Calculating score for this game" + Colors.Color_Off)

        remaining_hands = self.response.get("remaining_hands")
        score_history = {}
        score = 0

        if self.response.get("winner") == "TIE":
            winner = None
            for player_name in remaining_hands:
                score = 0
                for piece in remaining_hands[player_name]:
                    score += piece.values[0].value + piece.values[1].value
                score_history[player_name] = score
            for player in score_history:
                if winner is None:
                    winner = player
                elif score_history[winner] > score_history[player]:
                    winner = player
            score = 0
            for player_name in score_history:
                if player_name != winner:
                    score += score_history[player_name]
        else:
            winner = self.response.get("winner")
            for player_name in remaining_hands:
                if player_name != winner:
                    for piece in remaining_hands[player_name]:
                        score += piece.values[0].value + piece.values[1].value

        print(Colors.Green, "I expect the winner to be ", winner, Colors.Color_Off)
        print(Colors.Green, "I expect the score to be ", score, Colors.Color_Off)
        self.player.calculated_score = score
        self.player.expected_winner = winner
        msg = {"action": "score_report", "score": self.player.calculated_score,
               "possible_winner": self.player.expected_winner}
        msg.update({"signed_msg": self.keychain.sign(pickle.dumps(msg))})
        return msg

    def _handle_reveal_everything(self):
        print(Colors.Yellow, "Checking if reveal everything message was compromised", Colors.Color_Off)
        if not self.keychain.verify_sign(pickle.dumps(self.response.get("action")), self.response.get("signed_action"), self.player.server_pub_key):
                print(Colors.Red, "Reveal action has been compromised. Shutting Down!", Colors.Color_Off)
                exit(-1)
        print(Colors.BGreen, "Reveal action integrity not compromised", Colors.Color_Off)
        next_action = self.response.get("next_act")
        if next_action == "validate_protest":
            print(Colors.Red + "There has been a protest!\n" + Colors.Color_Off)
        else:
            print(Colors.Yellow + "Game Ended!\n" + Colors.Color_Off)
            print("hand -> " + ' '.join(map(str, self.player.hand)))
            print("end table -> " + ' '.join(map(str, self.response.get("in_table"))) + "\n")
        print(Colors.Green + "Revealing everything to server!" + Colors.Color_Off)
        msg = {"action": next_action, "tile_keys": self.player.randomized_tuple_mapping,
               'hand_commit_confirmation': self.player.hand_commit.publishConfirmation(),
               "remaining_hand": self.player.hand, "collected_keys": self.player.collected_keys}
        msg.update({"signed_msg": self.keychain.sign(pickle.dumps(msg))})
        return msg

    def _handle_end_game(self):

        signed_msg = self.response.pop("signed_msg", None)

        print(Colors.Yellow, "Checking if End Game message from server was compromised", Colors.Color_Off)
        if not self.keychain.verify_sign(pickle.dumps(self.response), signed_msg, self.player.server_pub_key):
                print(Colors.Red, "End Game has been compromised. Shutting Down!", Colors.Color_Off)
                exit(-1)
        print(Colors.BGreen, "End Game action integrity not compromised", Colors.Color_Off)

        winner = self.response.get("winner")
        score = self.response.get("score")
        print(Colors.Yellow, "Checking server score and winner", Colors.Color_Off)
        if winner != self.player.expected_winner:
            print(Colors.Red, "I don't agree with winner", Colors.Color_Off)
            exit(-1)
        if score != self.player.calculated_score:
            print(Colors.Red, "I don't agree with score", Colors.Color_Off)
            exit(-1)
        print(Colors.Green, "Agreeing with score and winner!", Colors.Color_Off)
        if self.response.get("winner") == self.player.name:
            winner = Colors.BRed + "YOU" + Colors.Color_Off
            print(Colors.BGreen + "End GAME, THE WINNER IS: " + winner)
            print(Colors.ICyan + "Your Score: " + str(score) + Colors.Color_Off)
            signature, data = self.cc.signData(pickle.dumps(str(score)))
            msg = {"action": "assign_score", "signed_score": signature, "data": data, "player": self.player.name}
            return msg
        else:
            winner = Colors.BBlue + winner + Colors.Color_Off
            print(Colors.BGreen + "End GAME, THE WINNER IS: " + winner)
            print(("{} {} {} earned {} points {}".format(Colors.ICyan, winner, Colors.ICyan, str(score),
                                                         Colors.Color_Off)))

    def _handle_wait(self):
        print(self.response.get("msg"))

    def _handle_disconnect(self):
        self.close()
        input("PRESS ANY KEY TO EXIT ")
        print(self.response.get("msg"))
        sys.exit(0)

    def _process_response_json_content(self):
        # ADD CLIENT ACTIONS TO MESSAGES HERE
        content = self.response
        action = content.get("action")
        if action == "secret_message":
            deciphered = self._handle_secret_message()
            action = deciphered.get("action")
            self.response = deciphered
        if action == "login":
            response = self._handle_login()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "you_host":
            self._handle_you_host()
        elif action == "new_player":
            self._handle_new_player()
        elif action == "send_pub_keys":
            print(self.response.get("msg"))
            print(Colors.Yellow, "Checking if server message was compromised", Colors.Color_Off)
            if not self.keychain.verify_sign(pickle.dumps(self.response.get("pub_keys")), self.response.get("signed_pub_keys"), self.player.server_pub_key):
                print(Colors.Red, "Public Keys Exchange has been compromised. Shutting Down!", Colors.Color_Off)
                exit(-1)
            print(Colors.BGreen, "Public Keys Exchange integrity not compromised", Colors.Color_Off)
            self.player.player_pub_keys = self.response.get('pub_keys')
        elif action == "key_exchange":
            response = self._handle_key_exchange()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "receiving_aes":
            self._handle_receiving_aes()
        elif action == "keys_exchanged":
            response = self._handle_keys_exchanged()
            if response is not None:
                message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                                  self.aes_cipher)
                self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "waiting_for_host":
            if self.player.host:
                response = self._handle_waiting_for_host_as_host()
                message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                                  self.aes_cipher)
                self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
            else:
                self._handle_waiting_for_host_as_player()
        elif action == "host_start_game":
            response = self._handle_host_start_game()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "randomization_stage":
            response = self._handle_randomization_stage()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "start_selection_stage":
            response = self._handle_start_selection_stage()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "selection_stage":
            response = self._handle_selection_stage()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "commit_hand":
            response = self._handle_commit_hand()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "validate_selection":
            response = self._handle_validate_selection()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "reveal_keys":
            response = self._handle_reveal_keys()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "keys_to_reveal":
            response = self._handle_keys_to_reveal()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "keys_sent":
            response = self._handle_keys_sent()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "start_deanon_stage":
            response = self._handle_start_deanon_stage()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "deanon_stage":
            response = self._handle_deanon_stage()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "decipher_tiles":
            response = self._handle_decipher_tiles()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "reveal_piece_key":
            response = self._handle_reveal_piece_key()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "piece_key_to_reveal":
            response = self._handle_piece_key_to_reveal()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "insert_in_hand":
            response = self._handle_insert_in_hand()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "validate_this_play":
            response = self._handle_validate_this_play()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "rcv_game_properties":
            response = self._handle_rcv_game_properties()
            if response is not None:
                message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                                  self.aes_cipher)
                self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "report_score":
            response = self._handle_report_score()
            if response is not None:
                message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                                  self.aes_cipher)
                self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "reveal_everything":
            response = self._handle_reveal_everything()
            message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                              self.aes_cipher)
            self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "end_game":
            response = self._handle_end_game()
            if response is not None:
                message = Message(self.selector, self.sock, self.addr, response, self.player, self.keychain, self.cc,
                                  self.aes_cipher)
                self.selector.modify(self.sock, selectors.EVENT_WRITE, data=message)
        elif action == "wait":
            self._handle_wait()
        elif action == "disconnect":
            self._handle_disconnect()