import logging
from typing import Callable
from typing import List

import geopandas as gp
import knime.types.geospatial as gt
import knime_extension as knext

LOGGER = logging.getLogger(__name__)

############################################
# Column selection helper
############################################

__DEF_GEO_COL_LABEL = "Geometry column"
__DEF_GEO_COL_DESC = "Select the Geometry column to use"

__TYPE_GEO = "org.knime.geospatial.core.data.cell.Geo"
__TYPE_POINT = "GeoPointCell"
__TYPE_LINE = "GeoLineCell"
__TYPE_POLYGON = "GeoPolygonCell"
__TYPE_COLLECTION = "GeoCollectionCell"
__TYPE_MULTI_POINT = "GeoMultiPointCell"
__TYPE_MULTI_LINE = "GeoMultiLineCell"
__TYPE_MULTI_POLYGON = "GeoMultiPolygonCell"

def geo_point_col_parameter(
    label: str = __DEF_GEO_COL_LABEL,
    description: str = __DEF_GEO_COL_DESC,
) -> knext.ColumnParameter:
    """
    Returns a column selection parameter that only supports GeoPoint columns.
    @return: Column selection parameter for point columns
    """
    return typed_geo_col_parameter(label, description, is_geo_point)

def geo_col_parameter(
    label: str = __DEF_GEO_COL_LABEL,
    description: str = __DEF_GEO_COL_DESC,
) -> knext.ColumnParameter:
    """
    Returns a column selection parameter that supports Geo columns.
    @return: Column selection parameter for all Geo columns
    """
    return typed_geo_col_parameter(label, description, is_geo)

def typed_geo_col_parameter(
    label: str = __DEF_GEO_COL_LABEL,
    description: str = __DEF_GEO_COL_DESC,
    type_filter: Callable[[knext.Column], bool] = None,
) -> knext.ColumnParameter:
    """
    Returns a column selection parameter that allows the user to select all columns that are compatible with
    the provided type filter.
    @return: Column selection parameter
    """
    return knext.ColumnParameter(
        label=label,
        description=description,
        column_filter=type_filter,
        include_row_key=False,
        include_none_column=False,
    )

def is_numeric(column: knext.Column) -> bool:
    """
    Checks if column is numeric e.g. int, long or double.
    @return: True if Column is numeric
    """
    return (
        column.ktype == knext.double()
        or column.ktype == knext.int32()
        or column.ktype == knext.int64()
    )

def is_string(column: knext.Column) -> bool:
    """
    Checks if column is string
    @return: True if Column is string
    """
    return column.ktype == knext.string()

def is_boolean(column: knext.Column) -> bool:
    """
    Checks if column is boolean
    @return: True if Column is boolean
    """
    return column.ktype == knext.boolean()

def is_numeric_or_string(column: knext.Column) -> bool:
    """
    Checks if column is numeric or string
    @return: True if Column is numeric or string
    """
    return (
        column.ktype == knext.double()
        or column.ktype == knext.int32()
        or column.ktype == knext.int64()
        or column.ktype == knext.string()
    )

def is_binary(column: knext.Column) -> bool:
    """
    Checks if column is binary
    @return: True if Column is binary
    """
    return column.ktype == knext.blob

def is_geo(column: knext.Column) -> bool:
    """
    Checks if column is compatible to the GeoValue interface and thus returns true for all geospatial types such as:
    GeoPointCell, GeoLineCell, GeoPolygonCell, GeoMultiPointCell, GeoMultiLineCell, GeoMultiPolygonCell, ...
    @return: True if Column Type is GeoValue compatible
    """
    return __is_type_x(column, __TYPE_GEO)

def is_geo_point(column: knext.Column) -> bool:
    """
    Checks if column is a GeoPointCell.
    @return: True if Column Type is a GeoPointCell
    """
    return __is_type_x(column, __TYPE_POINT)

def is_geo_line(column: knext.Column) -> bool:
    """
    Checks if column is a GeoLineCell.
    @return: True if Column Type is a GeoLineCell
    """
    return __is_type_x(column, __TYPE_LINE)

def is_geo_polygon(column: knext.Column) -> bool:
    """
    Checks if column is a GeoPolygonCell.
    @return: True if Column Type is a GeoPolygonCell
    """
    return __is_type_x(column, __TYPE_POLYGON)

def is_geo_collection(column: knext.Column) -> bool:
    """
    Checks if column is a GeoCollectionCell.
    @return: True if Column Type is a GeoCollectionCell
    """
    return __is_type_x(column, __TYPE_COLLECTION)

def is_geo_multi_point(column: knext.Column) -> bool:
    """
    Checks if column is a GeoMultiPointCell.
    @return: True if Column Type is a GeoMultiPointCell
    """
    return __is_type_x(column, __TYPE_MULTI_POINT)

def is_geo_multi_line(column: knext.Column) -> bool:
    """
    Checks if column is a GeoMultiLineCell.
    @return: True if Column Type is a GeoMultiLineCell
    """
    return __is_type_x(column, __TYPE_MULTI_LINE)

def is_geo_multi_polygon(column: knext.Column) -> bool:
    """
    Checks if column is a GeoMultiPolygonCell.
    @return: True if Column Type is a GeoMultiPolygonCell
    """
    return __is_type_x(column, __TYPE_MULTI_POLYGON)

def __is_type_x(column: knext.Column, type: str) -> bool:
    """
    Checks if column contains the given type whereas type can be :
    GeoPointCell, GeoLineCell, GeoPolygonCell, GeoMultiPointCell, GeoMultiLineCell, GeoMultiPolygonCell, ...
    @return: True if Column Type is a GeoLogical Point
    """
    return (
        isinstance(column.ktype, knext.LogicalType)
        and type in column.ktype.logical_type
    )

############################################
# GeoPandas node class decorator
############################################
def geo_node_description(short_description: str, description: str, references: dict):
    """This decorator takes the provided information and generates a standardized node description
    for nodes that are based on GeoPandas functionality."""

    def set_description(node_factory):
        s = f"{short_description}\n"
        s += f"{description}\n\n"
        # s += "___\n\n"  # separator line between description and general part
        s += "The node is based on the [GeoPandas](https://geopandas.org/) project and uses the following function"
        if references is not None:
            if len(references) > 1:
                s += "s"
            s += ":"
            s += "\n\n"
            for key in references:
                s += f"- [{key}]({references[key]})\n"
        node_factory.__doc__ = s
        return node_factory

    return set_description

def census_node_description(short_description: str, description: str, references: dict):
    """This decorator takes the provided information and generates a standardized node description
    for nodes that are based on GeoPandas functionality."""

    def set_description(node_factory):
        s = f"{short_description}\n"
        s += f"{description}\n\n"
        # s += "___\n\n"  # separator line between description and general part
        s += "The node is based on the data from [US Census](https://www.census.gov/)  and here are related data sources and references"
        if references is not None:
            if len(references) > 1:
                s += "s"
            s += ":"
            s += "\n\n"
            for key in references:
                s += f"- [{key}]({references[key]})\n"
        node_factory.__doc__ = s
        return node_factory

    return set_description

def osm_node_description(short_description: str, description: str, references: dict):
    """This decorator takes the provided information and generates a standardized node description
    for nodes that are based on GeoPandas functionality."""

    def set_description(node_factory):
        s = f"{short_description}\n"
        s += f"{description}\n\n"
        # s += "___\n\n"  # separator line between description and general part
        s += "The node is based on the project [OSMnx](https://github.com/gboeing/osmnx)  and here are related tools and references"
        if references is not None:
            if len(references) > 1:
                s += "s"
            s += ":"
            s += "\n\n"
            for key in references:
                s += f"- [{key}]({references[key]})\n"
        node_factory.__doc__ = s
        return node_factory

    return set_description

def pd_node_description(short_description: str, description: str, references: dict):
    """This decorator takes the provided information and generates a standardized node description
    for nodes that are based on GeoPandas functionality."""

    def set_description(node_factory):
        s = f"{short_description}\n"
        s += f"{description}\n\n"
        # s += "___\n\n"  # separator line between description and general part
        s += "The node is based on the package [pandas](https://pandas.pydata.org/docs/reference/api/pandas.read_parquet.html)  and here are related tools and references"
        if references is not None:
            if len(references) > 1:
                s += "s"
            s += ":"
            s += "\n\n"
            for key in references:
                s += f"- [{key}]({references[key]})\n"
        node_factory.__doc__ = s
        return node_factory

    return set_description

def pulp_node_description(short_description: str, description: str, references: dict):
    """This decorator takes the provided information and generates a standardized node description
    for nodes that are based on GeoPandas functionality."""

    def set_description(node_factory):
        s = f"{short_description}\n"
        s += f"{description}\n\n"
        # s += "___\n\n"  # separator line between description and general part
        s += "The node is based on the package [PuLP](https://coin-or.github.io/pulp/)  and here are related tools and references"
        if references is not None:
            if len(references) > 1:
                s += "s"
            s += ":"
            s += "\n\n"
            for key in references:
                s += f"- [{key}]({references[key]})\n"
        node_factory.__doc__ = s
        return node_factory

    return set_description

############################################
# GeoPandas helper
############################################
def load_geo_data_frame(
    input_table: knext.Table,
    column: knext.Column,
    exec_context: knext.ExecutionContext = None,
    load_msg: str = "Loading Geo data frame...",
    done_msg: str = "Geo data frame loaded. Start computation...",
) -> gp.GeoDataFrame:
    """Creates a GeoDataFrame from the given input table using the provided column as geo column."""
    if exec_context:
        exec_context.set_progress(0.0, load_msg)
    gdf = gp.GeoDataFrame(input_table.to_pandas(), geometry=column)
    if exec_context:
        exec_context.set_progress(0.3, done_msg)
    return gdf

def to_table(
    gdf: gp.GeoDataFrame,
    exec_context: knext.ExecutionContext = None,
    done_msg: str = "Computation done",
) -> knext.Table:
    """Returns a KNIME table representing the given GeoDataFrame."""
    if exec_context:
        exec_context.set_progress(1.0, done_msg)
    return knext.Table.from_pandas(gdf)

############################################
# General helper
############################################

__DEF_PLEASE_SELECT_COLUMN = "Please select a column"

def column_exists_or_preset(
    context: knext.ConfigurationContext,
    column: str,
    schema: knext.Schema,
    func: Callable[[knext.Column], bool] = None,
    none_msg: str = "No compatible column found in input table",
) -> str:
    """
    Checks that the given column is not None and exists in the given schema. If none is selected it returns the
    first column that is compatible with the provided function. If none is compatible it throws an exception.
    """
    if column is None:
        for c in schema:
            if func(c):
                context.set_warning(f"Preset column to: {c.name}")
                return c.name
        raise knext.InvalidParametersError(none_msg)
    __check_col_and_type(column, schema, func)
    return column

def geo_column_exists(
    column: str,
    schema: knext.Schema,
    none_msg: str = __DEF_PLEASE_SELECT_COLUMN,
) -> None:
    """
    Checks that the given column is not None, of type geo and exists in the given schema otherwise it throws an exception
    """
    return column_exists(column, schema, is_geo, none_msg)

def column_exists(
    column: str,
    schema: knext.Schema,
    func: Callable[[knext.Column], bool] = None,
    none_msg: str = __DEF_PLEASE_SELECT_COLUMN,
) -> None:
    """
    Checks that the given column is not None and exists in the given schema otherwise it throws an exception
    """
    if column is None:
        raise knext.InvalidParametersError(none_msg)
    __check_col_and_type(column, schema, func)

def __check_col_and_type(
    column: str,
    schema: knext.Schema,
    check_type: Callable[[knext.Column], bool] = None,
) -> None:
    """
    Checks that the given column exists in the given schema and that it matches the given type_check function.
    """
    # Check that the column exists in the schema and that it has a compatible type
    try:
        existing_column = schema[column]
        if check_type is not None and not check_type(existing_column):
            raise knext.InvalidParametersError(
                f"Column '{str(column)}' has incompatible data type"
            )
    except IndexError:
        raise knext.InvalidParametersError(
            f"Column '{str(column)}' not available in input table"
        )

def columns_exist(
    columns: List[str],
    schema: knext.Schema,
    func: Callable[[knext.Column], bool] = lambda c: True,
    none_msg: str = __DEF_PLEASE_SELECT_COLUMN,
) -> None:
    """
    Checks that the given columns are not None and exist in the given schema otherwise it throws an exception
    """
    for col in columns:
        column_exists(col, schema, func, none_msg)

def fail_if_column_exists(
    column_name: str, input_schema: knext.Schema, msg: str = None
):
    """Checks that the given column name does not exists in the input schema.
    Can be used to check that a column is not accidentally overwritten."""
    if column_name in input_schema.column_names:
        if msg is None:
            msg = f"Column '{column_name}' exists"
        raise knext.InvalidParametersError(msg)

def check_canceled(exec_context: knext.ExecutionContext) -> None:
    """
    Checks if the user has canceled the execution and if so throws a RuntimeException
    """
    if exec_context.is_canceled():
        raise RuntimeError("Execution canceled")


import config
import time
import random

def record(r):
    # ADD OUR OWN DATA
    r['report-time'] = str(int(time.time()))

    # SANITIZE
    out = {}
    for (k,v) in r.iteritems():
        v = v.replace('\t','    ').replace('\n','\t').replace('\r','\t')
        out[k] = v
    data = ""
    for (k,v) in out.iteritems():
        data += "%s = %s \n" % (k,v)

    # CALCULATE FILENAME
    base = config.config('error-base')
    if not base:
        base = '../../error'

    filename = "%s/error_%u_%u.txt" % (base,time.time(),random.getrandbits(32))

    # WRITE IT
    f = open(filename,"wb")
    print >>f,data.encode('utf-8')
    f.close()


### Module imports ###
import os
import requests
import shutil
import subprocess

### Global Variables ###

### Class declarations ###

### Function declarations ###
def download_and_extract(url, save_path):
    """
    Download and extract zip file to folder

    Args:
        url: url to download zip file
        save_path: folder to save data
    """
    local_filename = url.split('/')[-1]
    file_path = os.path.join(save_path, local_filename)
    folder_path = os.path.join(save_path, '.'.join(local_filename.split('.')[:-1]))
    # download file
    with requests.get(url, stream=True) as r:
        with open(file_path, 'wb') as f:
            shutil.copyfileobj(r.raw, f)

    # create folder to extract
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)

    # unzip
    subprocess.run(['unzip', file_path, '-d', folder_path])

    # remove zip file
    subprocess.run(['rm', file_path])

if __name__ == '__main__':
    download_and_extract('http://data.vision.ee.ethz.ch/cvl/DIV2K/DIV2K_train_HR.zip', 'data')
    download_and_extract('http://data.vision.ee.ethz.ch/cvl/DIV2K/DIV2K_valid_HR.zip', 'data')


from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np
from argparse import ArgumentParser

def parse_args():
    ap = ArgumentParser()
    ap.add_argument(
        "-e",
        "--epochs",
        type=float,
        default=100,
        help="# of epochs"
    )
    ap.add_argument(
        "-a",
        "--alpha",
        type=float,
        default=0.01,
        help="Learning rate"
    )
    ap.add_argument(
        "-b",
        "--batch-size",
        type=int,
        default=32,
        help="Size of SGD mini-batches"
    )
    return vars(ap.parse_args())

def generate_2_class_classification_problem():
    X, y = make_blobs(
        n_samples=1000,
        n_features=2,
        centers=2,
        cluster_std=1.5,
        random_state=1
    )
    y = y.reshape((y.shape[0], 1))
    # Insert a column of 1s as the last entry in the feature matrix to treat the
    # bias as a trainable parameter
    X = np.c_[X, np.ones(X.shape[0])]
    return X, y

def sigmoid_activation(x):
    return 1.0 / (1 + np.exp(-x))

def predict(X, W):
    preds = sigmoid_activation(X.dot(W))
    preds[preds <= 0.5] = 0
    preds[preds > 0] = 1
    return preds

def next_batch(X, y, batch_size):
    for i in np.arange(0, X.shape[0], batch_size):
        yield (X[i:(i + batch_size)], y[i:(i + batch_size)])

def plot_classification(test_X, test_y):
    plt.style.use("ggplot")
    plt.figure()
    plt.title("data")
    plt.scatter(
        test_X[:, 0],
        test_X[:, 1],
        marker="o",
        c=test_y.ravel().tolist(),
        s=30
    )

def plot_losses(epochs_count, losses):
    plt.style.use("ggplot")
    plt.figure()
    plt.plot(np.arange(0, epochs_count), losses)
    plt.title("Training loss")
    plt.xlabel("Epoch #")
    plt.ylabel("Loss")
    plt.show()

args = parse_args()
epochs_count = args["epochs"]
batch_size = args["batch_size"]
learning_rate = args["alpha"]
X, y = generate_2_class_classification_problem()
train_X, test_X, train_y, test_y = train_test_split(
    X,
    y,
    test_size=0.5,
    random_state=42
)
W = np.random.randn(X.shape[1], 1)
losses = []
print("[INFO] Training")
for epoch in np.arange(0, epochs_count):
    total_epoch_loss = []
    for batch_X, batch_y in next_batch(train_X, train_y, batch_size):
        preds = sigmoid_activation(batch_X.dot(W))
        error = preds - batch_y
        # Perform gradient descent update on a single batch rather than epoch
        gradient = batch_X.T.dot(error)
        W += -learning_rate * gradient
        total_epoch_loss.append(np.sum(error ** 2))
    loss = np.average(total_epoch_loss)
    losses.append(loss)
    if epoch == 0 or (epoch + 1) % 5 == 0:
        print(f"[INFO] epoch={int(epoch + 1)}, loss={loss:.7f}")
print("[INFO] Evaluating")
preds = predict(test_X, W)
print(classification_report(test_y, preds))
plot_classification(test_X, test_y)
plot_losses(epochs_count, losses)


from random_word import RandomWords
import random

#pip3 install random-words
r = RandomWords()

all_heroes = ['Ana', 'Ashe', 'Baptiste', 'Bastion', 'Brigitte', 'DIVA', 'Doomfist', 'Echo', 'Genji', 'Hanzo', 'Junkrat', 'Lucio',
 'McCree', 'Mei', 'Mercy', 'Moira', 'Orisa' , 'Pharah', 'Reaper' ,'Reinhardt', 'Roadhog', 'Sigma', 'Soldier', 'Sombra', 'Symmetra', 'Torbjorn', 'Tracer', 'Widowmaker',
 'Winston', 'Hammond', 'Zarya', 'Zenyatta']

all_tanks = [5, 16, 19, 20, 21, 28, 29, 30]

all_supports = [0, 2, 4, 11, 14, 15, 31]

all_damage = [1, 3, 6, 7, 8, 9, 10, 12, 13, 17, 18, 22, 23, 24, 25, 26, 27]

# order is tanks 0, damage 1, support 2

brawl_comp = [[19, 30], [12, 18, 22], [15, 11, 4]]

dive_comp = [[5, 28, 29], [6, 7, 8, 17, 23, 26], [0, 11, 14, 31]]

bunker_comp = [[16, 20, 21, 19], [1, 3, 9, 10, 22, 24, 25, 27], [2, 14, 15]]

class Hero:

    def __init__(self, id_number, role_type, play_style, map_user, map_controller, support_out, NV_power, V_power, Vpower_CAP, NVpower_CAP, fun, abilities, ultimate):
        self.id_number = id_number
        self.name = all_heroes[id_number]
        self.role_type = role_type #0 is tank, 1 is dps and 2 is support
        self.play_style = play_style #0 is dive, 1 is brawl, 2 is bunker
        self.map_user = map_user #1-100 based on if they need positioning or healthpacks
        self.map_controller = map_controller #1-100 based on how well they control positioning
        self.support_out = support_out #1-100 based on amount of support (healing + utility)
        self.NV_power = NV_power #Non-visible power, based on position control and tanking ability | 1-100
        self.V_power = V_power #Visible power, based on damage output
        self.Vpower_CAP = Vpower_CAP
        self.NVpower_CAP = NVpower_CAP
        self.Support_X_Vpower = Vpower_CAP / V_power
        self.Support_X_NVpower = NVpower_CAP / NV_power
        self.fun = fun
        self.abilities = abilities
        self.ultimate = ultimate[1]
        self.ultimateType = ultimate[0] # either 0 for used on teammates, or 1 for enemies, or 2 for CC

class Player:

    def __init__(self, rank_start, rank_end, role_variable, player_id):
        self.name = "BigGamer" + str(random.randint(10,500))
        self.rank = random.randint(rank_start, rank_end)
        self.player_id = player_id
        player_average = rank_start + rank_end / 2
        # making sure 2-2-2 is enforced
        self.role_variable = role_variable
        self.mainID = None
        if role_variable == 0:
            self.mainID = random.choice(all_tanks)
        if role_variable == 1:
            self.mainID = random.choice(all_supports)
        if role_variable == 2:
            self.mainID = random.choice(all_damage)
        self.swap_willingness = random.randint(1,100)
        self.char_being_played = self.mainID
        self.last_played = self.mainID
        self.current_value = 1
        self.deaths = 0
        self.eliminations = 0
        self.heroes_played = {self.mainID: {self.mainID: 1, 'value': 1}}
        self.fun_had = 100
        self.personality_type = str(random.choices(
            [1,2,3,4,5],
            weights = [0.1, 0.2, 0.3, ( player_average / 7000), (player_average / 7000)],
            k = 1
        )[0]) # 1-5. with 1 being the most hostile, and 5 being the best teammate you can get
        self.frustration = (5 - int(self.personality_type)) * random.randint(1,8)

    def swapHero(self, new_hero):
        self.last_played = self.char_being_played
        self.char_being_played = new_hero
        if new_hero in self.heroes_played:
            self.heroes_played[new_hero][new_hero] += 1
            self.heroes_played[new_hero]['value'] = self.heroes_played[new_hero]['value'] + self.current_value
        else:
            self.heroes_played[new_hero] = {new_hero : 1, 'value' : self.current_value}
        self.current_value = 0
        print(self.name + " has swapped to " + all_heroes[self.char_being_played])

    def addValue(self):
        if self.char_being_played in self.heroes_played:
            self.heroes_played[self.char_being_played]['value'] += self.current_value
            self.current_value = 0
        else:
            self.heroes_played[self.char_being_played] = {current_hero : 1, 'value' : self.current_value}
            self.current_value = 0

# id    role type   play style   map user    map controller    support out     Non vis power   vis power   vis cap   nonvis cap   fun  abilities  ultimate
# [1 ]  {  2   }   [    3     ]  [  4   ]   [     5       ]    [    6    ]     {    7      }   [    8  ]   [ 9   ]   [   10   ]  [11]  [  12   ]  [ 13   ]

Ana = Hero(0, 2, 2, 80, 15, 85, 5, 5, 10, 15, 65, ['Sleep Dart', 'Biotic Grenade', 'Biotic Rifle'], [0,['Nanoboost', 'Biotic Rifle', 'Biotic Grenade']])
Ashe = Hero(1, 1, 2, 60, 20, 0, 15, 90, 130, 15, 80, ['Dynamite', 'Coach Gun'], [1,['Bob']])
Baptiste = Hero(2, 2, 2, 5, 10, 95, 5, 80, 85, 15, 60, ['Biotic Launcher'], [0,['Amplification Matrix']])
Bastion = Hero(3, 1, 2, 70, 75, 5, 25, 60, 90, 35, 60, ['Sentry Mode', 'Recon Mode'], [1,['Tank Mode']])
Brigitte = Hero(4, 2, 1, 20, 75, 80, 25, 35, 40, 35, 10, ['Flail', 'Shield Bash', 'Whip'], [0,['Inspire', 'Rally', 'Repair Throw']])
DIVA = Hero(5, 0, 0, 5, 80, 0, 40, 30, 35, 50, 40, ['Rockets', 'Fusion Cannons', 'Self-Destruct'], [0,['Defense Matrix']])
Doomfist = Hero(6, 1, 0, 90, 20, 0, 25, 65, 75, 30, 55, ['Slam', 'Upper Cut', 'Punch'], [1,['Meteor Strike']])
Echo = Hero(7, 1, 0, 30, 70, 0, 15, 95, 180, 25, 90, ['Tri-Shot', 'Sticky Bombs', 'Focusing Beam'], [1,['Duplicated Hero']])
Genji = Hero(8, 1, 0, 50, 10, 0, 10, 70, 90, 15, 90, ['Shuriken', 'Deflect'], [1,['Dragonblade']])
Hanzo = Hero(9, 1, 2, 30, 40, 0, 15, 90, 140, 25, 85, ['Storm Bow', 'Storm Arrow'], [1,['Dragonstrike']])
Junkrat = Hero(10, 1, 2, 30, 25, 0, 15, 50, 70, 20, 80, ['Frag Launcher', 'Mine', 'Trap'], [1,['Riptire']])
Lucio = Hero(11, 2, 0, 40, 5, 40, 5, 10, 10, 25, 80, ['Sonic Amplifier', 'Boop'], [0,['Crossfade', 'Sound Barrier']])
McCree = Hero(12, 1, 1, 70, 70, 0, 25, 80, 110, 30, 80, ['Revolver', 'Fan the Hammer'], [2,['Flash']])
Mei = Hero(13, 1, 1, 30, 60, 0, 25, 80, 90, 35, 65, ['Ice-cicle', 'Freeze'], [2,['Blizzard', 'Freeze']])
Mercy = Hero(14, 2, 0, 5, 10, 90, 5, 5, 10, 30, 80, ['Blaster'], [0,['Healing Beam', 'Resurrection']])
Moira = Hero(15, 2, 1, 5, 40, 75, 5, 20, 25, 15, 40, ['Biotic Grasp', 'Biotic Orb', 'Coalescence'], [0,['Coalescence', 'Biotic Orb', 'Biotic Grasp']])
Orisa = Hero(16, 0, 2, 40, 65, 0, 55, 30, 35, 75, 5, ['Fusion Driver', 'Pull'], [0,['Supercharger', 'Barrier']])
Pharah = Hero(17, 1, 0, 20, 10, 0, 10, 40, 90, 40, 70, ['Rocket Launcher'], [1,['Barrage']])
Reaper = Hero(18, 1, 1, 50, 10, 0, 20, 60, 65, 25, 75, ['Shotguns'], [1,['Death Blossom']])
Reinhardt = Hero(19, 0, 1, 20, 35, 0, 55, 30, 50, 65, 30, ['Hammer', 'Firestrike', 'Charge'], [2,['Earthshatter']])
Roadhog = Hero(20, 0, 2, 20, 70, 0, 45, 65, 80, 65, 80, ['Scrap Gun', 'Hook'], [1,['Whole Hog']])
Sigma = Hero(21, 0, 2, 10, 40, 0, 65, 60, 80, 85, 70, ['Hyperspheres', 'Rock', 'Gravitic Flux'], [2,['Rock']])
Soldier = Hero(22, 1, 1, 35, 20, 10, 20, 65, 80, 25, 75, ['Pulse Rifle', 'Helix Missile', 'Tactical Visor'], [0,['Biotic Field']])
Sombra = Hero(23, 1, 0, 30, 90, 15, 25, 60, 70, 20, 60, ['Machine Pistol'], [2,['EMP', 'Hack']])
Symmetra = Hero(24, 1, 2, 20, 50, 0, 5, 55, 70, 20, 40, ['Photon Beam', 'Sentry Turret'], [0,['Photon Barrier']])
Torbjorn = Hero(25, 1, 2, 10, 40, 0, 15, 65, 75, 25, 75, ['Rivet Gun', 'Hammer', 'Turret'], [1,['Molten Core']])
Tracer = Hero(26, 1, 0, 80, 35, 0, 35, 95, 110, 35, 85, ['Pulse Pistols'], [1,['Pulse Bomb']])
Widowmaker = Hero(27, 1, 2, 80, 40, 0, 5, 40, 80, 15, 75, ['Sniper'], [1,['Venom Mine']])
Winston = Hero(28, 0, 0, 50, 35, 0, 55, 20, 35, 65, 40, ['Tesla Cannon', 'Jump Pack'], [1,['Primal Rage']])
Hammond = Hero(29, 0, 0, 80, 90, 0, 90, 65, 70, 95, 75, ['Quad Cannons', 'Roll', 'Piledriver'], [1,['Mines']])
Zarya = Hero(30, 0, 1, 10, 20, 0, 45, 65, 75, 55, 70, ['Particle Cannon'], [2,['Graviton Surge']])
Zenyatta = Hero(31, 2, 0, 90, 5, 55, 5, 85, 100, 15, 70, ['Orbs of Destruction'], [0,['Orb of Harmony', 'Transcendence']])

# order is tanks 0, damage 1, support 2
meta_comp = [[31,29], [26, 7, 1, 9], [14, 4, 2]]

# These are all lists of things for players to say in and after the games
player_names = ['TickleTrain', 'MercyMain', 'BedWetter11', 'MoiraHeadshotGod', 'BigGamer', 'HarambeFanatic44', 'JeffLuvr', 'GMEBuyer', 'Teltu', 'BradyIsCool', 'Alex', 'BaseballLover64',
'TomBradyFan', 'GodGamer', 'CoolKid141', 'OboePlayer91', 'Alteffour', 'Gnome', 'GenjiOneTrick', 'Hannibal', 'GhanaFuneral', 'RageWolf55', 'FallenWarfare', 'TwistedTea', 'Throwing', 'Jessica', 'Feester', 'RyuKen', 'BigPete', 'HighGround']

choosing_hero_sentences_angriest = ['bro, i f****** hate this hero', 'this s*** is garbage', 'I hate this dogs*** meta', 'F*** THIS F****** GAME', 'im about to f****** throw']
choosing_hero_sentences_upset = ["i guess ill play this guy", 'am i really stuck on this hero', 'i shouldnt have queued']
not_playing_synergy = ['i dont really like this comp, im just gonna go ', 'not a fan of this comp, im gonna play ', 'dont feel like playing that comp, imma play ']
not_playing_synergy_angry = ['bro f*** that dumba** comp, im playin ', 'nah that comp is dumb as f***, im goin ', 'F*** THAT S*** IM GOING ']
choosing_hero_random = ['whats up guys?', 'wuz goo', 'whats crackin', 'yo', 'Yo', 'Hey']

unhappy_with_supports = ['can out supports swap?', 'yo, supports. you there?', 'hey supports, can one of you swap?']
angry_with_supports = ['our supports are dogs***', 'WHY THE F*** CANT THE SUPPORTS HEAL ME????', 'our supports are absolutely braindead', 'zzz supports WTF ARE YOU DOING???', 'support diff']

unhappy_with_tanks = ['can our tanks swap?', 'yo, tanks. you there?', 'tanks, can one of you swap?']
angry_with_tanks = ['our tanks are dogs***', 'TANKS PRESS W KEY', 'our tanks are F****** braindead', 'gg. tanks are dogs***']

unhappy_with_damage = ['can our dps swap?', 'yo, dps. you there?', 'dps, can one of you swap?']
angry_with_damage = ['our dps are dogs***', 'DPS TURN ON YOUR MONITORS', 'our dps are F****** braindead', 'pretty sure our dps are throwing']

end_of_game_angry = ['was i the only one playing?', 'our team actually hard threw', 'F*** THIS GAME', 'serious question. was out team throwing?', 'pretty sure the rest of my team didnt have their monitors on', 'wish i had 5 avoid slots']
end_of_game = ['I feel very, very small... please hold me...', "I'm trying to be a nicer person. It's hard, but I'm trying, guys.", "I'm wrestling with some insecurity issues in my life but thank you for playing with me.", 'gg' 'GGs', 'good try', 'gg go next']

# Create a range for the ranks of the players
rank_start = int(input("Enter rank range start: "))
rank_end = int(input("Enter rank range end: "))
games = int(input('Enter amount of matches to play: '))

# Both team lists
red_team = []
blue_team = []

# Generate the players, and make it 2-2-2
for team_used in [red_team, blue_team]:
    for player_used in range(6):
        role_type = None
        if player_used == 0 or player_used == 1:
            role_type = 0
        elif player_used == 2 or player_used == 3:
            role_type = 1
        else:
            role_type = 2
        team_used.append(Player(rank_start, rank_end, role_type, (str([red_team, blue_team].index(team_used)) + str(player_used))))

for x in red_team:
    print(x.name + "'s main is " + all_heroes[x.mainID] + ", they're on red team, and their personality type is: " + x.personality_type)

for x in blue_team:
    print(x.name + "'s main is " + all_heroes[x.mainID] + ", they're on blue team, and their personality type is: " + x.personality_type)

# ---------------------- First game ---------------------------------

# First hero pick

#setting global team vars to keep track of
red_team_support = 0
red_team_damage = 0
red_team_tanking = 0
red_team_damage_amplified = 0
red_team_tanking_amplified = 0
red_team_team_swap_num = 0

blue_team_support = 0
blue_team_damage = 0
blue_team_tanking = 0
blue_team_damage_amplified = 0
blue_team_tanking_amplified = 0
blue_team_team_swap_num = 0

num_iter = -1

for current_team in [red_team, blue_team]:
    num_iter += 1
    sr_average = (rank_start + rank_end) / 2
    swap_said = False
    comp_synergy = {}
    meta_synergy = []
    used_char_list = []
    synergy_use_list = None

    person_to_suggest = None
    # these numbers are for flat support, visible power and non visible

    if num_iter == 0:
        team_support = red_team_support
        team_damage = red_team_damage
        team_tanking = red_team_tanking
        team_damage_amplified = red_team_damage_amplified
        team_tanking_amplified = red_team_tanking_amplified
        team_swap_num = red_team_team_swap_num
    else:
        team_support = blue_team_support
        team_damage = blue_team_damage
        team_tanking = blue_team_tanking
        team_damage_amplified = blue_team_damage_amplified
        team_tanking_amplified = blue_team_tanking_amplified
        team_swap_num = blue_team_team_swap_num

    # Creating lists to see how close the teams are to synergy or meta comp
    for i in current_team:
        player_style = eval(all_heroes[i.mainID]).play_style
        if player_style in comp_synergy:
            comp_synergy[player_style] += 1
        else:
            comp_synergy[player_style] = 1

        if player_style in meta_comp:
            meta_synergy += player_style

    comp_synergy_key = max(comp_synergy, key=comp_synergy.get)

    # Check if their team is closer to meta comp, or a certain synergy

    if len(meta_synergy) > comp_synergy[comp_synergy_key]:
        team_swap_num = 3
        synergy_use_list = meta_comp.copy()
        #this means meta comp
    else:
        team_swap_num = comp_synergy_key
        if comp_synergy_key == 0:
            synergy_use_list = [dive_comp[0].copy(), dive_comp[1].copy(), dive_comp[2].copy()]
        elif comp_synergy_key == 1:
            synergy_use_list = [brawl_comp[0].copy(), brawl_comp[1].copy(), brawl_comp[2].copy()]
        else:
            synergy_use_list = [bunker_comp[0].copy(), bunker_comp[1].copy(), bunker_comp[2].copy()]
        #this will swap to the number. ex 0 means dive 1 is brawl and 2 is bunker

    chars_picked = []
    for x in current_team:

        # if elect to swap to a comp with synergy
        #if the synergy comp is dive
        type_string = None
        if team_swap_num == 0:
            type_string = 'dive_comp'
        elif team_swap_num == 1:
            type_string = 'brawl_comp'
        elif team_swap_num == 2:
            type_string = 'bunker_comp'
        else:
            type_string = 'meta_comp'

        if swap_said == False:
            print(x.name + ": Lets go " + type_string[0 : (len(type_string) - 5)] + ' ' + type_string[(len(type_string) - 4) : (len(type_string))])
            person_to_suggest = x.name
            swap_said = True
        # 0 in role is tank
        # synergy order is tank 0, damage 1, support 2
        swap_var = None
        hero_type_temp = None
        if x.role_variable == 0:
            hero_type_temp = eval(type_string + '[0]')
        elif x.role_variable == 1:
            hero_type_temp = eval(type_string + '[1]')
        else:
            hero_type_temp = eval(type_string + '[2]')
        compar_chars = []
        synergy_delete = None
        for f in hero_type_temp:
            if f in synergy_use_list[x.role_variable]:
                compar_chars.append(f)

        random_sr_int = random.randint(200, 350)

        pick_list = []
        options_pick_list = []

        for l in range(len(synergy_use_list[x.role_variable])):

            loop_tank = eval(all_heroes[synergy_use_list[x.role_variable][l]])

            # order is tank 0, damage 1, support 2
            #create chances the hero will get picked based on the team's support, damage, rank and how fun the hero is
            if x.role_variable == 0: #tank
                chance_primary_pick = (loop_tank.NVpower_CAP * (max(1, (team_support/50)))) + loop_tank.NV_power #Primary is tanking / map control ability
                chance_secondary_pick = (loop_tank.Vpower_CAP * (max(1, ((team_damage + team_tanking)/190)))) + loop_tank.V_power #secondary is damage
                chance_fun_pick = loop_tank.fun * ((5350 - sr_average) / 1000) #fun
            elif x.role_variable == 1: #damage
                chance_primary_pick = (loop_tank.V_power * max(1, (team_damage/50))) + loop_tank.NV_power #Primary is damage and cc
                chance_secondary_pick = (loop_tank.V_power * max(1, ((team_support + loop_tank.Vpower_CAP)/90))) #secondary is damage with support
                chance_fun_pick = loop_tank.fun * ((5650 - sr_average) / 1000) #fun
            else:
                chance_primary_pick = (loop_tank.V_power * max(1, (team_tanking_amplified/50)) + loop_tank.NV_power) #Primary healing out
                chance_secondary_pick = (loop_tank.V_power * max(1, ((team_damage_amplified + loop_tank.Vpower_CAP)/70))) #secondary is utility
                chance_fun_pick = loop_tank.fun * ((5450 - sr_average) / 1000) #fun

            pick_list.append(chance_fun_pick + chance_primary_pick + chance_secondary_pick)
            options_pick_list.append(loop_tank.id_number)

                #add some past picks here

        high_sr_var = random.choices(
            options_pick_list,
            weights = pick_list,
            k=1
        )[0]

        add_random_char = random.choices(
            [1,2,3],
            weights=[9000 + (sr_average * 3), max(1000, ((4600 - sr_average) * (6 - int(x.personality_type)))), (4600 - sr_average) * (6 - int(x.personality_type))],
            k=1

        )[0]
        first_check = False
        if add_random_char == 2:
            def random_char_func():
                global high_sr_var

                first_check = True
                if x.role_variable == 0:
                    high_sr_var = eval(all_heroes[random.choice(all_tanks)]).id_number
                elif x.role_variable == 1:
                    high_sr_var = eval(all_heroes[random.choice(all_damage)]).id_number
                else:
                    high_sr_var = eval(all_heroes[random.choice(all_supports)]).id_number
                if high_sr_var in used_char_list:
                    random_char_func()
                elif high_sr_var not in synergy_use_list[x.role_variable]:
                    swapping_quote = random.choices(
                        [1,2],
                        weights = [350, ((6 - int(x.personality_type)) * x.frustration)],
                        k=1

                    )[0]
                    if swapping_quote == 1:
                        print(x.name + ': ' + random.choice(not_playing_synergy) + all_heroes[high_sr_var])
                    else:
                        print(x.name + ': ' + random.choice(not_playing_synergy_angry) + all_heroes[high_sr_var])

            if first_check == False:
                random_char_func()
        high_sr_var = eval(all_heroes[high_sr_var])
        used_char_list.append(high_sr_var.id_number)
        team_support += high_sr_var.support_out
        team_damage += high_sr_var.V_power
        team_tanking += high_sr_var.NV_power
        team_damage_amplified += high_sr_var.Vpower_CAP
        team_tanking_amplified += (high_sr_var.NVpower_CAP + high_sr_var.map_controller)
        if num_iter == 0:
            red_team_support = team_support
            red_team_damage = team_damage
            red_team_tanking = team_tanking
            red_team_damage_amplified = team_damage_amplified
            red_team_tanking_amplified = team_tanking_amplified
            red_team_team_swap_num = team_swap_num
        else:
            blue_team_support = team_support
            blue_team_damage = team_damage
            blue_team_tanking = team_tanking
            blue_team_damage_amplified = team_damage_amplified
            blue_team_tanking_amplified = team_tanking_amplified
            blue_team_team_swap_num = team_swap_num
        x.swapHero(high_sr_var.id_number)
        if high_sr_var.id_number in synergy_use_list[x.role_variable]:
            synergy_use_list[x.role_variable].remove(high_sr_var.id_number)

           # eval(compar_chars[f])

#----------------------------------------------------------------------------------------
#------------------ Function for swapping after fights-----------------------------------
#----------------------------------------------------------------------------------------

def losing_team_swap(team, team_id):
    if team_id == 'red':
        global red_team_support
        global red_team_damage
        global red_team_tanking
        global red_team_damage_amplified
        global red_team_tanking_amplified
        global red_team_team_swap_num
    elif team_id == 'blue':
        global blue_team_support
        global blue_team_damage
        global blue_team_tanking
        global blue_team_damage_amplified
        global blue_team_tanking_amplified
        global blue_team_team_swap_num

    for current_team in range(1):
        sr_average = (rank_start + rank_end) / 2
        swap_said = False
        comp_synergy = {}
        meta_synergy = []
        used_char_list = []
        synergy_use_list = None

        person_to_suggest = None
        # these numbers are for flat support, visible power and non visible

        if team_id == 'red':
            team_support = red_team_support
            team_damage = red_team_damage
            team_tanking = red_team_tanking
            team_damage_amplified = red_team_damage_amplified
            team_tanking_amplified = red_team_tanking_amplified
            team_swap_num = red_team_team_swap_num
        elif team_id == 'blue':
            team_support = blue_team_support
            team_damage = blue_team_damage
            team_tanking = blue_team_tanking
            team_damage_amplified = blue_team_damage_amplified
            team_tanking_amplified = blue_team_tanking_amplified
            team_swap_num = blue_team_team_swap_num

        meta_chance = 0
        bunker_chance = 0
        brawl_chance = 0
        dive_chance = 0
        for i in range(len(team)):
            #if tank
            if team[i].role_variable == 0:
                #check for meta comp
                for l in range(len(meta_comp[0])):
                    #check if initial dict key matches
                    if meta_comp[0][l] in team[i].heroes_played:
                        #then subtract valu
                        meta_chance += (team[i].heroes_played[meta_comp[0][l]]['value'] - team[i].heroes_played[meta_comp[0][l]][meta_comp[0][l]])
                #check for buner
                for l in range(len(bunker_comp[0])):
                    if bunker_comp[0][l] in team[i].heroes_played:
                        bunker_chance += (team[i].heroes_played[bunker_comp[0][l]]['value'] - team[i].heroes_played[bunker_comp[0][l]][bunker_comp[0][l]])
                for l in range(len(brawl_comp[0])):
                    if brawl_comp[0][l] in team[i].heroes_played:
                        brawl_chance += (team[i].heroes_played[brawl_comp[0][l]]['value'] - team[i].heroes_played[brawl_comp[0][l]][brawl_comp[0][l]])
                for l in range(len(dive_comp[0])):
                    if dive_comp[0][l] in team[i].heroes_played:
                        dive_chance += (team[i].heroes_played[dive_comp[0][l]]['value'] - team[i].heroes_played[dive_comp[0][l]][dive_comp[0][l]])
            #if support
            if team[i].role_variable == 1:
                #check for meta comp
                for l in range(len(meta_comp[2])):
                    #check if initial dict key matches
                    if meta_comp[2][l] in team[i].heroes_played:
                        #then subtract valu
                        meta_chance += (team[i].heroes_played[meta_comp[2][l]]['value'] - team[i].heroes_played[meta_comp[2][l]][meta_comp[2][l]])
                #check for buner
                for l in range(len(bunker_comp[2])):
                    if bunker_comp[2][l] in team[i].heroes_played:
                        bunker_chance += (team[i].heroes_played[bunker_comp[2][l]]['value'] - team[i].heroes_played[bunker_comp[2][l]][bunker_comp[2][l]])
                for l in range(len(brawl_comp[2])):
                    if brawl_comp[2][l] in team[i].heroes_played:
                        brawl_chance += (team[i].heroes_played[brawl_comp[2][l]]['value'] - team[i].heroes_played[brawl_comp[2][l]][brawl_comp[2][l]])
                for l in range(len(dive_comp[2])):
                    if dive_comp[2][l] in team[i].heroes_played:
                        dive_chance += (team[i].heroes_played[dive_comp[2][l]]['value'] - team[i].heroes_played[dive_comp[2][l]][dive_comp[2][l]])
            if team[i].role_variable == 2:
                #check for meta comp
                for l in range(len(meta_comp[1])):
                    #check if initial dict key matches
                    if meta_comp[1][l] in team[i].heroes_played:
                        #then subtract valu
                        meta_chance += (team[i].heroes_played[meta_comp[1][l]]['value'] - team[i].heroes_played[meta_comp[1][l]][meta_comp[1][l]])
                #check for buner
                for l in range(len(bunker_comp[1])):
                    if bunker_comp[1][l] in team[i].heroes_played:
                        bunker_chance += (team[i].heroes_played[bunker_comp[1][l]]['value'] - team[i].heroes_played[bunker_comp[1][l]][bunker_comp[1][l]])
                for l in range(len(brawl_comp[1])):
                    if brawl_comp[1][l] in team[i].heroes_played:
                        brawl_chance += (team[i].heroes_played[brawl_comp[1][l]]['value'] - team[i].heroes_played[brawl_comp[1][l]][brawl_comp[1][l]])
                for l in range(len(dive_comp[1])):
                    if dive_comp[1][l] in team[i].heroes_played:
                        dive_chance += (team[i].heroes_played[dive_comp[1][l]]['value'] - team[i].heroes_played[dive_comp[1][l]][dive_comp[1][l]])

        print('meta chance: ' + str(meta_chance) + ' dive chance: ' + str(dive_chance) + ' brawl chance: ' + str(brawl_chance) + ' bunker chance: ' + str(bunker_chance))

        # Creating lists to see how close the teams are to synergy or meta comp
        for i in team:
            player_style = eval(all_heroes[i.mainID]).play_style
            if player_style in comp_synergy:
                comp_synergy[player_style] += 1
            else:
                comp_synergy[player_style] = 1

            if player_style in meta_comp:
                meta_synergy += player_style

        comp_synergy_key = max(comp_synergy, key=comp_synergy.get)

        # Check if their team is closer to meta comp, or a certain synergy

        #adding some randomness and main priority for comp  synergy swaps
        added_weighting = random.choices(
            [1,2,3,4,5],
            weights= [10, meta_chance, dive_chance, brawl_chance, bunker_chance],
            k=1

        )[0]

        meta_count = len(meta_synergy)
        if added_weighting == 2:
            meta_count += meta_chance
            team_swap_num = 3
            synergy_use_list = meta_comp.copy()
        elif added_weighting == 3 or added_weighting == 4 or added_weighting == 5:
            # 3 is dive, 4 is brawl, and 5 is bunker
            if added_weighting == 3:
                synergy_use_list = [dive_comp[0].copy(), dive_comp[1].copy(), dive_comp[2].copy()]
                team_swap_num = 0
            elif added_weighting == 4:
                synergy_use_list = [brawl_comp[0].copy(), brawl_comp[1].copy(), brawl_comp[2].copy()]
                team_swap_num = 1
            else:
                synergy_use_list = [bunker_comp[0].copy(), bunker_comp[1].copy(), bunker_comp[2].copy()]
                team_swap_num = 2

        elif added_weighting == 1:

            if meta_count > comp_synergy[comp_synergy_key]:
                team_swap_num = 3
                synergy_use_list = meta_comp.copy()
                #this means meta comp
            else:
                team_swap_num = comp_synergy_key
                if comp_synergy_key == 0:
                    synergy_use_list = [dive_comp[0].copy(), dive_comp[1].copy(), dive_comp[2].copy()]
                elif comp_synergy_key == 1:
                    synergy_use_list = [brawl_comp[0].copy(), brawl_comp[1].copy(), brawl_comp[2].copy()]
                else:
                    synergy_use_list = [bunker_comp[0].copy(), bunker_comp[1].copy(), bunker_comp[2].copy()]
                #this will swap to the number. ex 0 means dive 1 is brawl and 2 is bunker

        chars_picked = []
        for x in team:

            # if elect to swap to a comp with synergy
            #if the synergy comp is dive
            type_string = None
            if team_swap_num == 0:
                type_string = 'dive_comp'
            elif team_swap_num == 1:
                type_string = 'brawl_comp'
            elif team_swap_num == 2:
                type_string = 'bunker_comp'
            else:
                type_string = 'meta_comp'

            if swap_said == False:
                print(x.name + ": Lets swap to " + type_string[0 : (len(type_string) - 5)] + ' ' + type_string[(len(type_string) - 4) : (len(type_string))])
                person_to_suggest = x.name
                swap_said = True
            # 0 in role is tank
            # synergy order is tank 0, damage 1, support 2
            swap_var = None
            hero_type_temp = None
            if x.role_variable == 0:
                hero_type_temp = eval(type_string + '[0]')
            elif x.role_variable == 1:
                hero_type_temp = eval(type_string + '[1]')
            else:
                hero_type_temp = eval(type_string + '[2]')
            compar_chars = []
            synergy_delete = None
            for f in hero_type_temp:
                if f in synergy_use_list[x.role_variable]:
                    compar_chars.append(f)

            random_sr_int = random.randint(200, 350)

            pick_list = []
            options_pick_list = []

            for l in range(len(synergy_use_list[x.role_variable])):

                loop_tank = eval(all_heroes[synergy_use_list[x.role_variable][l]])

                # order is tank 0, damage 1, support 2
                #create chances the hero will get picked based on the team's support, damage, rank and how fun the hero is
                if x.role_variable == 0: #tank
                    chance_primary_pick = (loop_tank.NVpower_CAP * (max(1, (team_support/50)))) + loop_tank.NV_power #Primary is tanking / map control ability
                    chance_secondary_pick = (loop_tank.Vpower_CAP * (max(1, ((team_damage + team_tanking)/190)))) + loop_tank.V_power #secondary is damage
                    chance_fun_pick = loop_tank.fun * ((5350 - sr_average) / 1000) #fun
                elif x.role_variable == 1: #damage
                    chance_primary_pick = (loop_tank.V_power * max(1, (team_damage/50))) + loop_tank.NV_power #Primary is damage and cc
                    chance_secondary_pick = (loop_tank.V_power * max(1, ((team_support + loop_tank.Vpower_CAP)/90))) #secondary is damage with support
                    chance_fun_pick = loop_tank.fun * ((5650 - sr_average) / 1000) #fun
                else:
                    chance_primary_pick = (loop_tank.V_power * max(1, (team_tanking_amplified/50)) + loop_tank.NV_power) #Primary healing out
                    chance_secondary_pick = (loop_tank.V_power * max(1, ((team_damage_amplified + loop_tank.Vpower_CAP)/70))) #secondary is utility
                    chance_fun_pick = loop_tank.fun * ((5450 - sr_average) / 1000) #fun

                pick_list.append(chance_fun_pick + chance_primary_pick + chance_secondary_pick)
                options_pick_list.append(loop_tank.id_number)

                    #add some past picks here

            high_sr_var = random.choices(
                options_pick_list,
                weights = pick_list,
                k=1
            )[0]

            add_random_char = random.choices(
                [1,2,3],
                weights=[9000 + (sr_average * 3), max(1000, ((4600 - sr_average) * (6 - int(x.personality_type)))), (4600 - sr_average) * (6 - int(x.personality_type))],
                k=1

            )[0]
            first_check = False
            if add_random_char == 2:
                def random_char_func():
                    global high_sr_var

                    first_check = True
                    if x.role_variable == 0:
                        high_sr_var = eval(all_heroes[random.choice(all_tanks)]).id_number
                    elif x.role_variable == 1:
                        high_sr_var = eval(all_heroes[random.choice(all_damage)]).id_number
                    else:
                        high_sr_var = eval(all_heroes[random.choice(all_supports)]).id_number
                    if high_sr_var in used_char_list:
                        random_char_func()
                    elif high_sr_var not in synergy_use_list[x.role_variable]:
                        swapping_quote = random.choices(
                            [1,2],
                            weights = [350, ((6 - int(x.personality_type)) * x.frustration)],
                            k=1

                        )[0]
                        if swapping_quote == 1:
                            print(x.name + ': ' + random.choice(not_playing_synergy) + all_heroes[high_sr_var])
                        else:
                            print(x.name + ': ' + random.choice(not_playing_synergy_angry) + all_heroes[high_sr_var])

                if first_check == False:
                    random_char_func()
            high_sr_var = eval(all_heroes[high_sr_var])
            used_char_list.append(high_sr_var.id_number)
            team_support += high_sr_var.support_out
            team_damage += high_sr_var.V_power
            team_tanking += high_sr_var.NV_power
            team_damage_amplified += high_sr_var.Vpower_CAP
            team_tanking_amplified += (high_sr_var.NVpower_CAP + high_sr_var.map_controller)
            if team_id == 'red':
                red_team_support = team_support
                red_team_damage = team_damage
                red_team_tanking = team_tanking
                red_team_damage_amplified = team_damage_amplified
                red_team_tanking_amplified = team_tanking_amplified
                red_team_team_swap_num = team_swap_num
            elif team_id == 'blue':
                blue_team_support = team_support
                blue_team_damage = team_damage
                blue_team_tanking = team_tanking
                blue_team_damage_amplified = team_damage_amplified
                blue_team_tanking_amplified = team_tanking_amplified
                blue_team_team_swap_num = team_swap_num
            x.swapHero(high_sr_var.id_number)
            if high_sr_var.id_number in synergy_use_list[x.role_variable]:
                synergy_use_list[x.role_variable].remove(high_sr_var.id_number)
#---------------------------- End function for later swapping--------------------------------

#---------------------------- fighting ------------------------------------------------------

print('red team support is: ' + str(red_team_support))
print('blue team support is: ' + str(blue_team_support))

#During round
#this will swap to the number. ex 0 means dive 1 is brawl and 2 is bunker, 3 is meta

red_games_won = 0
blue_games_won = 0
games_tied = 0

for x in range(games):
    red_match_total = 0
    blue_match_total = 0
    for q in range(8):

        def fun_calculation(team):
            overall_return = 0
            if team == 'red':
                for i in red_team:
                    overall_return += i.fun_had * (1 + int(i.personality_type))
            elif team == 'blue':
                for i in blue_team:
                    overall_return += i.fun_had * (1 + int(i.personality_type))
            print(overall_return)
            return overall_return

        overall_red = (red_team_damage * (200 - red_team_support)) + (red_team_damage_amplified * red_team_support) + (red_team_tanking * max(10, (80 - red_team_support))) + (red_team_tanking_amplified * red_team_support) + fun_calculation('red')
        overall_blue = (blue_team_damage * (200 - blue_team_support)) + (blue_team_damage_amplified * blue_team_support) + (blue_team_tanking * max(10, (80 - blue_team_support))) + (blue_team_tanking_amplified * blue_team_support) + fun_calculation('blue')

        advantage = random.choices(
            [1,2],
            weights= [overall_red, overall_blue],
            k=1

        )
        # advantage 1 is red, and 2 is blue

        red_stats = []
        blue_stats = []

        for i in red_team:
            red_char_playing = eval(all_heroes[i.char_being_played])
            red_stats.append([i, (red_char_playing.V_power * (200 - red_team_support)) + (red_char_playing.Vpower_CAP * red_team_support) + (red_char_playing.NV_power * max(1, (80 - red_team_support))) + (red_char_playing.NVpower_CAP * red_team_support) + ((red_char_playing.fun - i.frustration) * 3)])
        for i in blue_team:
            blue_char_playing = eval(all_heroes[i.char_being_played])
            blue_stats.append([i, (blue_char_playing.V_power * (200 - blue_team_support)) + (blue_char_playing.Vpower_CAP * blue_team_support) + (blue_char_playing.NV_power * max(1, (80 - blue_team_support))) + (blue_char_playing.NVpower_CAP * blue_team_support) + ((blue_char_playing.fun - i.frustration) * 3)])

        red_dead = 0
        blue_dead = 0
        living_red_team = red_team.copy()
        living_blue_team = blue_team.copy()
        dead_people = []
        dead_people_ids = []
        temp_red_stats = red_stats.copy()
        temp_blue_stats = blue_stats.copy()
        for w in range(6):

            range_options = random.choice([0,1,2,3,4,5])
            winner_single = random.choices(
                [1,2],
                weights= [red_stats[w][1], blue_stats[w][1]],
                k=1
            )[0]
            loser_id = 0
            # if red wins the fight
            if winner_single == 1:
                loser_var = blue_stats[w][0]
                loser_var.frustration += 2
                loser_var.fun_had += -2
                blue_list = []
                blue_id_list = []
                for o in temp_blue_stats:
                    if o[0] not in dead_people:
                        blue_list.append(o[1])
                        blue_id_list.append(o[0])

                person_killed = random.choices(
                    blue_id_list,
                    weights = blue_list,
                    k=1
                )[0]

                #if the person killed was a tank
                mean_comment = random.choices(
                    [1,2],
                    weights= [700, person_killed.frustration * (5 - int(person_killed.personality_type))],
                    k=1

                )

                #when they die
                for u in temp_blue_stats:
                    if u[0] == person_killed:
                        temp_blue_stats.remove(u)

                dead_people.append(person_killed)
                blue_dead += 1
                random_killer = random.choice(red_team)
                random_killer.addValue()
                random_killer.eliminations += 1
                print(person_killed.name + ' was killed by ' + random_killer.name + "'s " + str(random.choice(eval(all_heroes[random_killer.char_being_played]).abilities)))
                living_blue_team.remove(person_killed)

                if mean_comment == 2:
                    # if person killed was a tank
                    if person_killed.role_variable == 0:
                        if random.choice([1,2]) == 1:
                            random_comment = random.choice(angry_with_supports)
                            for q in blue_team:
                                if blue_team[q].role_variable == 1:
                                    blue_team[q].frustration += 10
                                    blue_team[q].fun_had += -10
                        else:
                            random_comment = random.choice(angry_with_damage)
                            for q in blue_team:
                                if blue_team[q].role_variable == 2:
                                    blue_team[q].frustration += 10
                                    blue_team[q].fun_had += -10
                    # if person killed was a support
                    if person_killed.role_variable == 1:
                        if random.choice([1,2]) == 1:
                            random_comment = random.choice(angry_with_tanks)
                            for q in blue_team:
                                if blue_team[q].role_variable == 0:
                                    blue_team[q].frustration += 10
                                    blue_team[q].fun_had += -10
                        else:
                            random_comment = random.choice(angry_with_damage)
                            for q in blue_team:
                                if blue_team[q].role_variable == 2:
                                    blue_team[q].frustration += 10
                                    blue_team[q].fun_had += -10
                    # if person killed was a dps
                    if person_killed.role_variable == 2:
                        if random.choice([1,2]) == 1:
                            random_comment = random.choice(angry_with_supports)
                            for q in blue_team:
                                if blue_team[q].role_variable == 1:
                                    blue_team[q].frustration += 10
                                    blue_team[q].fun_had += -10
                        else:
                            random_comment = random.choice(angry_with_tanks)
                            for q in blue_team:
                                if blue_team[q].role_variable == 0:
                                    blue_team[q].frustration += 10
                                    blue_team[q].fun_had += -10
                    print(person_killed.name + ': ' + random_comment)
                else:
                    regular_comment = random.choices(
                        [1,2],
                        weights= [20000, (int(person_killed.personality_type) * sr_average)],
                        k=1
                    )
                    if regular_comment == 2:
                        if person_killed.role_variable == 0:

                            if random.choice([1,2]) == 1:
                                random_comment = random.choice(unhappy_with_supports)
                            else:
                                random_comment = random.choice(unhappy_with_damage)

                        elif person_killed.role_variable == 1:

                            if random.choice([1,2]) == 1:
                                random_comment = random.choice(unhappy_with_tanks)
                            else:
                                random_comment = random.choice(unhappy_with_damage)

                        elif person_killed.role_variable == 2:

                            if random.choice([1,2]) == 1:
                                random_comment = random.choice(unhappy_with_tanks)
                            else:
                                random_comment = random.choice(unhappy_with_supports)

                        print(person_killed.name + ': ' + random_comment)

                person_killed.deaths += 1
                person_killed.frustration += 5
                person_killed.fun_had += -5

            elif winner_single == 2:
                loser_var = red_stats[w][0]
                loser_var.frustration += 2
                loser_var.fun_had += -2
                red_list = []
                red_id_list = []
                for o in temp_red_stats:
                    if o[0] not in dead_people:
                        red_list.append(o[1])
                        red_id_list.append(o[0])

                person_killed = random.choices(
                    red_id_list,
                    weights = red_list,
                    k=1
                )[0]

                #if the person killed was a tank
                mean_comment = random.choices(
                    [1,2],
                    weights= [700, person_killed.frustration * (5 - int(person_killed.personality_type))],
                    k=1

                )

                #when they die
                hero_player_killed = eval(all_heroes[person_killed.char_being_played])
                for u in temp_red_stats:
                    if u[0] == person_killed:
                        temp_red_stats.remove(u)
                dead_people.append(person_killed)
                red_dead += 1
                random_killer = random.choice(blue_team)
                random_killer.current_value += 1
                random_killer.addValue()
                living_red_team = red_team.copy()
                random_killer.eliminations += 1
                print(person_killed.name + ' was killed by ' + random_killer.name + "'s " + str(random.choice(eval(all_heroes[random_killer.char_being_played]).abilities)))

                if mean_comment == 2:
                    # if person killed was a tank
                    if person_killed.role_variable == 0:
                        if random.choice([1,2]) == 1:
                            random_comment = random.choice(angry_with_supports)
                            for q in red_team:
                                if red_team[q].role_variable == 1:
                                    red_team[q].frustration += 10
                                    red_team[q].fun_had += -10
                        else:
                            random_comment = random.choice(angry_with_damage)
                            for q in red_team:
                                if red_team[q].role_variable == 2:
                                    red_team[q].frustration += 10
                                    red_team[q].fun_had += -10
                    # if person killed was a support
                    if person_killed.role_variable == 1:
                        if random.choice([1,2]) == 1:
                            random_comment = random.choice(angry_with_tanks)
                            for q in red_team:
                                if red_team[q].role_variable == 0:
                                    red_team[q].frustration += 10
                                    red_team[q].fun_had += -10
                        else:
                            random_comment = random.choice(angry_with_damage)
                            for q in red_team:
                                if red_team[q].role_variable == 2:
                                    red_team[q].frustration += 10
                                    red_team[q].fun_had += -10
                    # if person killed was a dps
                    if person_killed.role_variable == 2:
                        if random.choice([1,2]) == 1:
                            random_comment = random.choice(angry_with_supports)
                            for q in red_team:
                                if red_team[q].role_variable == 1:
                                    red_team[q].frustration += 10
                                    red_team[q].fun_had += -10
                        else:
                            random_comment = random.choice(angry_with_tanks)
                            for q in red_team:
                                if red_team[q].role_variable == 0:
                                    red_team[q].frustration += 10
                                    red_team[q].fun_had += -10
                    print(person_killed.name + ': ' + random_comment)
                else:
                    regular_comment = random.choices(
                        [1,2],
                        weights= [20000, (int(person_killed.personality_type) * sr_average)],
                        k=1
                    )
                    if regular_comment == 2:
                        if person_killed.role_variable == 0:

                            if random.choice([1,2]) == 1:
                                random_comment = random.choice(unhappy_with_supports)
                            else:
                                random_comment = random.choice(unhappy_with_damage)

                        elif person_killed.role_variable == 1:

                            if random.choice([1,2]) == 1:
                                random_comment = random.choice(unhappy_with_tanks)
                            else:
                                random_comment = random.choice(unhappy_with_damage)

                        elif person_killed.role_variable == 2:

                            if random.choice([1,2]) == 1:
                                random_comment = random.choice(unhappy_with_tanks)
                            else:
                                random_comment = random.choice(unhappy_with_supports)
                        print(person_killed.name + ': ' + random_comment)

                person_killed.deaths += 1
                person_killed.frustration += 5
                person_killed.fun_had += -5

                # 0 tank, 1 support, 2dps

        if red_dead > blue_dead:
            for i in living_red_team:
                random_killer = random.choice(blue_team)
                print(i.name + ' was killed by ' + random_killer.name + "'s " + str(random.choice(eval(all_heroes[random_killer.char_being_played]).abilities)))
                random_killer.current_value += 1
                random_killer.addValue()
                random_killer.eliminations += 1
                random_killer.frustration -= 5
                random_killer.fun_had += 5
                blue_match_total += 1
            losing_team_swap(red_team, 'red')
        elif red_dead < blue_dead:
            for i in living_blue_team:
                random_killer = random.choice(red_team)
                print(i.name + ' was killed by ' + random_killer.name + "'s " + str(random.choice(eval(all_heroes[random_killer.char_being_played]).abilities)))
                random_killer.current_value += 1
                random_killer.addValue()
                random_killer.eliminations += 1
                random_killer.frustration -= 5
                random_killer.fun_had += 5
                red_match_total += 1
            losing_team_swap(blue_team, 'blue')

    if red_match_total > blue_match_total:
        red_games_won += 1
        for l in range(len(red_team)):
            red_team[l].current_value += 1
            red_team[l].addValue()
            red_team[l].fun_had += 10
            red_team[l].frustration -= 5
            end_choice = random.choices(
                [1,2],
                weights= [2,4],
                k=1

            )[0]
            if end_choice == 1:
                what_to_say = random.choices(
                    [1,2],
                    weights = [900000, (red_team[l].frustration * (6 - int(red_team[l].personality_type)) * (4800 - sr_average))]

                )[0]
                if what_to_say == 1:
                    print('(Red Team)' + red_team[l].name + ': ' + random.choice(end_of_game))
                elif what_to_say == 2:
                    print('(Red Team)' + red_team[l].name + ': ' + random.choice(end_of_game_angry))
        for l in range(len(blue_team)):
            blue_team[l].fun_had -= 35
            blue_team[l].frustration += 15
            end_choice = random.choices(
                [1,2],
                weights= [2,4],
                k=1

            )[0]
            if end_choice == 1:
                what_to_say = random.choices(
                    [1,2],
                    weights = [900000, (blue_team[l].frustration * (6 - int(blue_team[l].personality_type)) * (4800 - sr_average))]

                )[0]
                if what_to_say == 1:
                    print('(Blue Team)' + blue_team[l].name + ': ' + random.choice(end_of_game))
                elif what_to_say == 2:
                    print('(Blue Team)' + blue_team[l].name + ': ' + random.choice(end_of_game_angry))

    elif blue_match_total > red_match_total:
        blue_games_won += 1
        for l in range(len(red_team)):
            red_team[l].fun_had -= 35
            red_team[l].frustration -= 5
            end_choice = random.choices(
                [1,2],
                weights= [2,4],
                k=1

            )[0]
            if end_choice == 1:
                what_to_say = random.choices(
                    [1,2],
                    weights = [900000, (red_team[l].frustration * (6 - int(red_team[l].personality_type)) * (4800 - sr_average))]

                )[0]
                if what_to_say == 1:
                    print('(Red Team)' + red_team[l].name + ': ' + random.choice(end_of_game))
                elif what_to_say == 2:
                    print('(Red Team)' + red_team[l].name + ': ' + random.choice(end_of_game_angry))
        for l in range(len(blue_team)):
            blue_team[l].current_value += 1
            blue_team[l].addValue()
            blue_team[l].fun_had += 10
            blue_team[l].frustration += 15
            end_choice = random.choices(
                [1,2],
                weights= [2,4],
                k=1

            )[0]
            if end_choice == 1:
                what_to_say = random.choices(
                    [1,2],
                    weights = [900000, (blue_team[l].frustration * (6 - int(blue_team[l].personality_type)) * (4800 - sr_average))]

                )[0]
                if what_to_say == 1:
                    print('(Blue Team)' + blue_team[l].name + ': ' + random.choice(end_of_game))
                elif what_to_say == 2:
                    print('(Blue Team)' + blue_team[l].name + ': ' + random.choice(end_of_game_angry))
    else:
        games_tied += 1
        for l in range(len(red_team)):
            red_team[l].fun_had -= 10
            red_team[l].frustration += 5
            end_choice = random.choices(
                [1,2],
                weights= [2,4],
                k=1

            )[0]
            if end_choice == 1:
                what_to_say = random.choices(
                    [1,2],
                    weights = [900000, (red_team[l].frustration * (6 - int(red_team[l].personality_type)) * (4800 - sr_average))]

                )[0]
                if what_to_say == 1:
                    print('(Red Team)' + red_team[l].name + ': ' + random.choice(end_of_game))
                elif what_to_say == 2:
                    print('(Red Team)' + red_team[l].name + ': ' + random.choice(end_of_game_angry))
        for l in range(len(blue_team)):
            blue_team[l].fun_had -= 10
            blue_team[l].frustration += 5
            end_choice = random.choices(
                [1,2],
                weights= [2,4],
                k=1

            )[0]
            if end_choice == 1:
                what_to_say = random.choices(
                    [1,2],
                    weights = [900000, (blue_team[l].frustration * (6 - int(blue_team[l].personality_type)) * (4800 - sr_average))]

                )[0]
                if what_to_say == 1:
                    print('(Blue Team)' + blue_team[l].name + ': ' + random.choice(end_of_game))
                elif what_to_say == 2:
                    print('(Blue Team)' + blue_team[l].name + ': ' + random.choice(end_of_game_angry))

print('red matches won = ' + str(red_games_won))
print('blue matches won = ' + str(blue_games_won))
print('matches tied = ' + str(games_tied))

# End of round
#------------------------------------
# For each player, update the heroes played with the current one
#for current_team in [red_team, blue_team]:
    #for x in current_team:
        # Check to see if they swapped that round
       # if x.last_played != x.char_being_played:
        #    if x.char_being_played in x.heroes_played:
        #        x.heroes_played[x.char_being_played] += 1
       #     else:
       #         x.heroes_played[x.char_being_played] = 1
     #   x.last_played = x.char_being_played

# ----------------------- End of Game -----------------------------------


import datetime
import pickle
import time

import numpy as np
import pandas as pd

class InputProcessor:
    def __init__(self, source_csv_dir):
        self.source_csv_dir = source_csv_dir
        self.source_df = pd.read_csv(source_csv_dir, header=0)

        self.process_df = self.source_df.copy()
        self.entity_map_csv_dir = '../entity_map.csv'
        self.entity_map, self.num_entity, self.num_rel = self.data_analysis(entity_map_csv_dir=self.entity_map_csv_dir)

    def map_actor_code_to_text(self, rel_code):
        map_dict = {1: 'MAKE STATEMENT',
                    2: 'APPEAL',
                    3: 'EXPRESS INTENT TO COOPERATE',
                    4: 'CONSULT',
                    5: 'ENGAGE IN DIPLOMATIC COOPERATION',
                    6: 'ENGAGE IN MATERIAL COOPERATION',
                    7: 'PROVIDE AID',
                    8: 'YIELD',
                    9: 'INVESTIGATE',
                    10: 'DEMAND',
                    11: 'DISAPPROVE',
                    12: 'REJECT',
                    13: 'THREATEN',
                    14: 'PROTEST',
                    15: 'EXHIBIT MILITARY POSTURE',
                    16: 'REDUCE RELATIONS',
                    17: 'COERCE',
                    18: 'ASSAULT',
                    19: 'FIGHT',
                    20: 'ENGAGE IN UNCONVENTIONAL MASS VIOLENCE'}
        return map_dict[rel_code]

    def data_analysis(self, entity_map_csv_dir):
        # save a entity_map
        # merge subject and object
        total_entity = pd.concat([self.source_df['Actor1Code'], self.source_df['Actor2Code']])

        entity_map = total_entity.value_counts(normalize=True)
        entity_map = entity_map.reset_index()
        entity_map.columns = ['actor_code', 'ratio']
        num_entity = len(entity_map)
        entity_map['index'] = range(num_entity)

        num_rel = len(set(self.source_df['EventRootCode']))

        entity_map.to_csv(entity_map_csv_dir, index=False, header=True)

        return entity_map, num_entity, num_rel

    def encode_col(self, to_encode, encode_map_df, encode_map_df_col):
        res = encode_map_df[encode_map_df[encode_map_df_col] == to_encode]['index'].to_list()[0]
        return res

    def encode_entity(self):
        """

        Returns: encoded entity number

        """

        # self.entity_map = pd.read_csv(self.entity_map_csv_dir, header=0)

        self.process_df['encode_actor1'] = self.process_df['Actor1Code'].apply(
            lambda x: self.encode_col(x, self.entity_map, 'actor_code'))

        self.process_df['encode_actor2'] = self.process_df['Actor2Code'].apply(
            lambda x: self.encode_col(x, self.entity_map, 'actor_code'))

        return

    def encode_rel(self):
        """

        Returns: encoded relation = EventRootCode - 1

        """

        self.process_df['encode_rel'] = self.process_df['EventRootCode'] - 1

        return

    def encode_time(self, s):
        return time.mktime(datetime.datetime.strptime(str(s), "%Y%m%d%H%M%S").timetuple())

    def analyze_time(self):
        res = pd.DataFrame(columns=['encode_actor', 'avg_dt'])
        for i in range(20):
            entity_df = self.process_df[self.process_df['encode_actor1'] == i]
            entity_df.sort_values(['event_time_unix'], inplace=True)
            dt = entity_df['event_time_unix'].diff(1).dropna()
            res.loc[i, 'avg_dt'] = np.mean(dt[dt > 0])
            res.loc[i, 'encode_actor'] = i

        print(res)
        return

    def clean_timestamp(self):
        self.process_df['event_time_unix'] = self.process_df['event_time'].apply(lambda x: self.encode_time(x))
        min_timestamp = min(self.process_df['event_time_unix'])

        self.process_df['event_time_unix'] -= min_timestamp
        self.process_df['event_time_unix'] /= (60 * 60 * 12)  # half day

        self.process_df.sort_values(['event_time_unix'], inplace=True)
        self.process_df.reset_index(inplace=True)

        return

    def make_event_text(self, actor1_name, actor2_name, rel, event_time, event_headline, event_key_phrases):
        rel_ = self.map_actor_code_to_text(rel)

        if pd.isnull(event_headline):
            event_headline = ''

        event_time = str(event_time)[:8]
        event_time = event_time[:4] + '-' + event_time[4:6] + '-' + event_time[6:8]

        res = 'event headline:' + event_headline + '; event type:' + rel_ + '; event time:' + str(
            event_time) + '; subject name:' + actor1_name + '; object name:' + actor2_name +  '.'

        return res

    def make_seq(self, input_df):
        input_df['event_text'] = input_df.apply(lambda x: self.make_event_text(x['Actor1Name'],
                                                                               x['Actor2Name'],
                                                                               x['EventRootCode'],
                                                                               x['event_time'],
                                                                               x['news_title'],
                                                                               x['news_short_key_word']),
                                                axis=1)

        seq_tuples = zip(input_df['encode_actor1'],
                         input_df['encode_actor2'],
                         input_df['encode_rel'],
                         input_df['event_time_unix'],
                         input_df['event_text'])
        seq_entity = set(input_df['encode_actor1']).union(set(input_df['encode_actor2']))

        return list(seq_tuples), seq_entity

    def make_dataset(self, train_ratio=0.7, valid_ratio=0.1):
        self.encode_rel()
        self.encode_entity()
        self.clean_timestamp()

        current_seq, current_entity = self.make_seq(self.process_df)

        self.save_to_pkl(current_seq, self.num_entity, self.num_rel, '../gdelt.pkl')
        # total_len = len(current_seq)
        # train_seq = current_seq[: int(total_len * train_ratio)]
        # valid_seq = current_seq[int(total_len * train_ratio): int(total_len * (train_ratio + valid_ratio))]
        # test_seq = current_seq[int(total_len * (train_ratio + valid_ratio)):]
        #
        # self.save_to_pkl(train_seq, self.num_entity, self.num_rel, '../train.pkl')
        # self.save_to_pkl(valid_seq, self.num_entity, self.num_rel, '../dev.pkl')
        # self.save_to_pkl(test_seq, self.num_entity, self.num_rel, '../test.pkl')

        return

    def make_csv_data(self):
        self.encode_rel()
        self.encode_entity()
        self.clean_timestamp()

        process_df = self.process_df[['encode_actor1', 'encode_actor2', 'encode_rel', 'event_time_unix']]

        total_len = len(process_df)
        train_ratio = 0.7
        valid_ratio = 0.1
        train_df = process_df.loc[: int(total_len * train_ratio), :]
        test_df = process_df.loc[int(total_len * (train_ratio + valid_ratio)):, :]

        train_df.to_csv('gdelt_train.csv', header=True)
        test_df.to_csv('gdelt_test.csv', header=True)

        return

    def save_to_pkl(self, batch_data, num_entity, num_rel, save_dir):
        res = dict({'num_entity': num_entity,
                    'num_rel': num_rel,
                    'data': batch_data})
        with open(save_dir, 'wb') as handle:
            pickle.dump(res, handle)

        return

if __name__ == '__main__':
    csv_dir = '../../preprocess/gdelt_v0314_v2.csv'
    processor = InputProcessor(source_csv_dir=csv_dir)
    # processor.data_analysis('entity_map.csv')

    processor.make_dataset()


from builtins import str
import logging

from django.conf import settings
from MySQLdb.cursors import DictCursor

from bq_data_access.v2.feature_id_utils import FeatureProviderFactory
from bq_data_access.v2.user_data import UserDataQueryHandler
from bq_data_access.v2.data_access import submit_tcga_job, get_submitted_job_results

from cohorts.metadata_helpers import get_sql_connection
from google_helpers.bigquery.service import get_bigquery_service

def user_feature_handler(feature_id, cohort_id_array):
    include_tcga = False
    user_studies = ()
    for cohort_id in cohort_id_array:
        try:
            db = get_sql_connection()
            cursor = db.cursor(DictCursor)

            cursor.execute("SELECT project_id FROM cohorts_samples WHERE cohort_id = %s GROUP BY project_id", (cohort_id,))
            for row in cursor.fetchall():
                if row['project_id'] is None:
                    include_tcga = True
                else:
                    user_studies += (row['project_id'],)

        except Exception as e:
            if db: db.close()
            if cursor: cursor.close()
            raise e

    user_feature_id = None
    if feature_id.startswith('USER:'):
        # Try and convert it with a shared ID to a TCGA queryable id
        user_feature_id = feature_id
        feature_id = UserDataQueryHandler.convert_feature_id(feature_id)
        if feature_id is None:
            # Querying user specific data, don't include TCGA
            include_tcga = False

    return {
        'converted_feature_id': feature_id,
        'include_tcga': include_tcga,
        'user_studies': user_studies,
        'user_feature_id': user_feature_id
    }

def submit_jobs_with_user_data(params_array):
    bigquery_service = get_bigquery_service()
    provider_array = []

    cohort_settings = settings.GET_BQ_COHORT_SETTINGS()

    # Submit jobs
    for parameter_object in params_array:
        feature_id = parameter_object.feature_id
        cohort_id_array = parameter_object.cohort_id_array

        user_data = user_feature_handler(feature_id, cohort_id_array)

        if user_data['include_tcga']:
            job_item = submit_tcga_job(parameter_object, bigquery_service, cohort_settings)
            provider_array.append(job_item)

        if len(user_data['user_studies']) > 0:
            converted_feature_id = user_data['converted_feature_id']
            user_feature_id = user_data['user_feature_id']
            logging.debug("user_feature_id: {0}".format(user_feature_id))
            provider = UserDataQueryHandler(converted_feature_id, user_feature_id=user_feature_id)

            # The UserDataQueryHandler instance might not generate a BigQuery query and job at all given the combination
            # of cohort(s) and feature identifiers. The provider is not added to the array, and therefore to the
            # polling loop below, if it would not submit a BigQuery job.
            if provider.is_queryable(cohort_id_array):
                job_reference = provider.get_data_job_reference(cohort_id_array, cohort_settings.dataset_id, cohort_settings.table_id)

                logging.info("Submitted USER {job_id}: {fid} - {cohorts}".format(job_id=job_reference['jobId'], fid=feature_id,
                                                                                cohorts=str(cohort_id_array)))
                provider_array.append({
                    'feature_id': feature_id,
                    'provider': provider,
                    'ready': False,
                    'job_reference': job_reference['job_reference'],
                    'tables_used': job_reference['tables_queried']
                })
            else:
                logging.debug("No UserFeatureDefs for '{0}'".format(converted_feature_id))

    return provider_array

# This code was part of the V1 data_access code, pulled out of V2 data_access.py, and only used in
# the V1 Pairwise analysis.

def get_feature_vector(feature_id, cohort_id_array):
    include_tcga = False
    user_studies = ()
    for cohort_id in cohort_id_array:
        try:
            db = get_sql_connection()
            cursor = db.cursor(DictCursor)

            cursor.execute("SELECT project_id FROM cohorts_samples WHERE cohort_id = %s GROUP BY project_id", (cohort_id,))
            for row in cursor.fetchall():
                if row['project_id'] is None:
                    include_tcga = True
                else:
                    user_studies += (row['project_id'],)

        except Exception as e:
            if db: db.close()
            if cursor: cursor.close()
            raise e

    #  ex: feature_id 'CLIN:Disease_Code'
    user_feature_id = None
    if feature_id.startswith('USER:'):
        # Try and convert it with a shared ID to a TCGA queryable id
        user_feature_id = feature_id
        feature_id = UserDataQueryHandler.convert_user_feature_id(feature_id)
        if feature_id is None:
            # Querying user specific data, don't include TCGA
            include_tcga = False

    items = []
    type = None
    result = []
    cohort_settings = settings.GET_BQ_COHORT_SETTINGS()
    if include_tcga:
        provider = FeatureProviderFactory.from_feature_id(feature_id)
        result = provider.get_data(cohort_id_array, cohort_settings.dataset_id, cohort_settings.table_id)

        # ex: result[0]
        # {'aliquot_id': None, 'case_id': u'TCGA-BH-A0B1', 'sample_id': u'TCGA-BH-A0B1-10A', 'value': u'BRCA'}
        for data_point in result:
            data_item = {key: data_point[key] for key in ['case_id', 'sample_id', 'aliquot_id']}
            value = provider.process_data_point(data_point)
            # TODO refactor missing value logic
            if value is None:
                value = 'NA'
            data_item['value'] = value
            items.append(data_item)

        type = provider.get_value_type()

    if len(user_studies) > 0:
        # Query User Data
        user_provider = UserDataQueryHandler(feature_id, user_feature_id=user_feature_id)
        user_result = user_provider.get_data(cohort_id_array, cohort_settings.dataset_id, cohort_settings.table_id)
        result.extend(user_result)

        for data_point in user_result:
            data_item = {key: data_point[key] for key in ['case_id', 'sample_id', 'aliquot_id']}
            value = provider.process_data_point(data_point)
            # TODO refactor missing value logic
            if value is None:
                value = 'NA'
            data_item['value'] = value
            items.append(data_item)

        if not type:
            type = user_provider.get_value_type()

    return type, items

def get_feature_vectors_with_user_data(params_array, poll_retry_limit=20, skip_formatting_for_plot=False):
    provider_array = submit_jobs_with_user_data(params_array)

    project_id = settings.BIGQUERY_PROJECT_ID
    result = get_submitted_job_results(provider_array, project_id, poll_retry_limit, skip_formatting_for_plot)

    return result


from flask import request, jsonify
from flask_restful import Resource
from data import db_session
from data.products import Products

class ProductsListResource(Resource):
    def get(self):
        sess = db_session.create_session()
        try:
            types = request.args.get('types', type=str)
            if types:
                types = types.lower().split(',')
            else:
                types = ''
            price = request.args.get('price', type=str)
            if price:
                price = range(*map(int, request.args.get('price', type=str).split('-')))
            else:
                price = range(0, 10 ** 6)
            order = request.args.get('order')
            if not order:
                order = '0'
            sorting_orders = {
                '0': lambda x: x.price,
                '1': lambda x: -x.price,
                '2': lambda x: x.name
            }
            products = filter(lambda x: x.price in price and all([i in [j.name.lower() for j in x.categories] for i in types]),
                              sess.query(Products).all())
            products = list(sorted(products, key=sorting_orders[order]))
            if not products:
                return jsonify({'error': 'no products found'})
        except KeyError:
            return jsonify({'error': 'Bad request'})
        return jsonify(
            {
                'products':
                    [item.to_dict(only=('id', 'name', 'stringed_categories', 'image', 'price', 'about', 'specs')) for item in
                     products]
            }
        )

class ProductResource(Resource):
    def get(self, product_id):
        sess = db_session.create_session()
        product = sess.query(Products).get(product_id)
        if not product:
            return jsonify({'error': 'no product with such name'})
        return jsonify(
            {
                'product': product.to_dict(only=('id', 'name', 'stringed_categories', 'image', 'price', 'about', 'specs'))
            }
        )


import numpy as np
from sklearn.cluster import KMeans
import cv2

class PaletteGenerator():
    def __init__(self, img):
        self.img = cv2.imread(img)
        img_rgb = cv2.cvtColor(self.img, cv2.COLOR_BGR2RGB)
        img_resized = cv2.resize(img_rgb, (300,300), interpolation = cv2.INTER_AREA)
        self.img_array = img_resized.reshape(-1,3)

    def palette(self):
        self.cluster =  KMeans(n_clusters=5)
        x = self.cluster.fit(self.img_array)
        p = np.zeros(shape=(50,300, 3), dtype=np.uint8)
        step = 300 / x.cluster_centers_.shape[0]
        lst = enumerate(x.cluster_centers_)
        for i, center in lst:
            p[:, int(i * step):int((i + 1) * step), :] = center
        return p


import clip
from datasets.text_label import obj_text_label
import torch

device='cuda'
clip_model = 'ViT-B/32'
clip_model, preprocess = clip.load(clip_model, device=device)
with torch.no_grad():
    obj_text_inputs = torch.cat([clip.tokenize(obj_text[1]) for obj_text in obj_text_label])
    obj_text_embedding = clip_model.encode_text(obj_text_inputs.to(device)).float()
    print('stop')