import numpy as np
import torch as th
from ml import io, logging
from .utils import GiB, get_calibration_files

try:
    import torch2trt as t2t
    import tensorrt as trt
except ImportError as e:
    raise ImportError(e, f"torch2trt required: `pip install --install-option='--plugins' git+https://github.com/NVIDIA-AI-IOT/torch2trt.git@b0cc8e77a0fbd61e96b971a66bbc11326f77c6b5`")

EXPLICIT_BATCH = 1 << (int)(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
# load all custom plugins
trt.init_libnvinfer_plugins(None,'')

class TRTPredictor(t2t.TRTModule):
    def __init__(self, engine=None, input_names=None, output_names=None):
        super(TRTPredictor, self).__init__(engine, input_names, output_names)

    def predict(self, *inputs, out=None, sync=False):
        with th.no_grad():
            output = self(*inputs, out=out)
            if sync:
                th.cuda.synchronize()
            return output

    def forward(self, *inputs, out=None):
        batch_size = inputs[0].shape[0]
        bindings = [None] * (len(self.input_names) + len(self.output_names))

        for i, input_name in enumerate(self.input_names):
            # XXX Conclude dynamic input shape
            idx = self.engine.get_binding_index(input_name)
            binding_shape = tuple(self.context.get_binding_shape(idx))
            arg_shape = tuple(inputs[i].shape)
            if binding_shape != arg_shape:
                # logging.info(f"Reallocate {input_name}.shape{binding_shape} -> {arg_shape}")
                self.context.set_binding_shape(idx, trt.Dims(arg_shape))
            bindings[idx] = inputs[i].contiguous().data_ptr()

        # create output tensors
        outputs = [None] * len(self.output_names)
        if out is None:
            for i, output_name in enumerate(self.output_names):
                idx = self.engine.get_binding_index(output_name)
                dtype = t2t.torch_dtype_from_trt(self.engine.get_binding_dtype(idx))
                shape = tuple(self.context.get_binding_shape(idx))
                #assert shape[0] == batch_size
                device = t2t.torch_device_from_trt(self.engine.get_location(idx))
                output = th.empty(size=shape, dtype=dtype, device=device)
                outputs[i] = output
                bindings[idx] = output.data_ptr()
        else:
            for i, output_name in enumerate(self.output_names):
                idx = self.engine.get_binding_index(output_name)
                outputs[i] = out[i]
                bindings[idx] = out[i].data_ptr()

        self.context.execute_async_v2(
            bindings=bindings,
            stream_handle=th.cuda.current_stream().cuda_stream
        )

        outputs = tuple(outputs)
        if len(outputs) == 1:
            outputs = outputs[0]
        return outputs

def torch2trt(module,
              inputs,
              input_names=None,
              output_names=None,
              max_batch_size=1,
              max_workspace_size=GiB(1),
              strict_type_constraints=False,
              fp16_mode=False,
              int8_mode=False,
              int8_calib_batch_size=16,
              int8_calib_algorithm=t2t.DEFAULT_CALIBRATION_ALGORITHM,
              keep_network=True,
              log_level=trt.Logger.INFO,
              use_onnx=True,
              **kwargs):
    """Revise to support dynamic batch size through ONNX by default
    Args:
        module(nn.Module):
        inputs(List[Tensor]): list of tensors
    Kwargs:
        input_names:
        output_names:
        dynamic_axes(Dict[str, Dict[int, str]]): {input_key: {index: name}}
        min_shapes(Dict[int, Tuple(int, int, int)]): minimum shape for each dynamic input
        opt_shapes(Dict[int, Tuple(int, int, int)]): optimal shape for each dynamic input
        max_shapes(Dict[int, Tuple(int, int, int)]): maximum shape for each dynamic input

        max_batch_size(int): max batch size as the dynamic axis 0
        int8_calib_cache_file(str):
        int8_calib_data_path(str):
        int8_calib_max_data(int):
        int8_calib_batch_size(int): batch size to load for calibration
        int8_calib_preprocess_func(input):
        opset_version(int):
    """

    # copy inputs to avoid modifications to source data
    inputs = [tensor.clone()[0:1] for tensor in inputs]  # only run single entry

    logger = trt.Logger(log_level)
    builder = trt.Builder(logger)
    if isinstance(inputs, list):
        inputs = tuple(inputs)
    if not isinstance(inputs, tuple):
        inputs = (inputs,)

    # run once to get num outputs
    outputs = module(*inputs)
    if not isinstance(outputs, tuple) and not isinstance(outputs, list):
        outputs = (outputs,)

    def reduce(value, outputs):
        nonlocal count
        if th.is_tensor(outputs):
            value += 1
        else:
            for output in outputs:
                value = reduce(value, output)
        return value
    if input_names is None:
        # list of tensors expected
        input_names = t2t.default_input_names(len(inputs))
    if output_names is None:
        # in case of nested tensors
        count = reduce(0, outputs)
        output_names = t2t.default_output_names(count)
        # logging.info(f"len(outputs)={len(outputs)}, count={count}")
    logging.info(f"input_names={input_names}")
    logging.info(f"output_names={output_names}")

    dynamic_axes = kwargs.pop('dynamic_axes', None)
    if dynamic_axes is None and max_batch_size > 1:
        dynamic_axes = {input_name: {0: 'batch_size'} for input_name in input_names}

    if use_onnx:
        f = io.BytesIO()
        th.onnx.export(module, inputs, f,
                       input_names=input_names,
                       output_names=output_names,
                       dynamic_axes=dynamic_axes,
                       opset_version=kwargs.pop('opset_version', 13))
        f.seek(0)
        onnx_bytes = f.read()
        network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
        parser = trt.OnnxParser(network, logger)
        parser.parse(onnx_bytes)
    else:
        # FIXME No dynamic batch size by default
        network = builder.create_network()
        with t2t.ConversionContext(network) as ctx:
            ctx.add_inputs(inputs, input_names)
            outputs = module(*inputs)
            if not isinstance(outputs, tuple) and not isinstance(outputs, list):
                outputs = (outputs,)
            ctx.mark_outputs(outputs, output_names)

    builder.max_batch_size = max_batch_size
    """ Removed in tensorrt > 8.0"""
    #builder.max_workspace_size = max_workspace_size
    #builder.fp16_mode = fp16_mode
    #builder.int8_mode = int8_mode
    #builder.strict_type_constraints = strict_type_constraints
    """"""""""""""""""""""""""""""""
    if dynamic_axes is None:
        for i in range(network.num_inputs):
            logging.info(f"network.get_input({i}).shape={network.get_input(i).shape}")
        for i in range(network.num_outputs):
            logging.info(f"network.get_output({i}).shape={network.get_output(i).shape}")
        engine = builder.build_cuda_engine(network)
    else:
        cfg = builder.create_builder_config()
        if fp16_mode:
            cfg.flags |= 1 << int(trt.BuilderFlag.FP16)
            if strict_type_constraints:
                cfg.flags |= 1 << int(trt.BuilderFlag.STRICT_TYPES)

        if int8_mode:
            from .calibrator import Calibrator
            cfg.set_flag(trt.BuilderFlag.INT8)
            calib_cache = kwargs.pop('int8_calib_cache', None)            # cache of calibration dataset
            calib_data = kwargs.pop('int8_calib_data', None)              # path to calibration data
            calib_max = kwargs.pop('int8_calib_max', 512)                 # max amount of calibration data to use
            calib_preprocess_func = kwargs.pop('int8_calib_preprocess_func', None)  # preprocessing of calibration data
            calib_files = calib_data and get_calibration_files(calib_data, calib_max) or []

            # TODO: test calibrator with dynamic shapes other than batch size dimension
            cfg.int8_calibrator = Calibrator(
                batch_size=int8_calib_batch_size,
                inputs=[tuple(tensor.shape[1:]) for tensor in inputs],
                cache=calib_cache,
                calibration_files=calib_files,
                max_calib_data=calib_max,
                preprocess_func=calib_preprocess_func,
                algorithm=int8_calib_algorithm
            )

        # XXX: set max_workspace in config for dynamic input
        cfg.max_workspace_size = max_workspace_size
        min_shapes = kwargs.pop('min_shapes', None)
        max_shapes = kwargs.pop('max_shapes', None)
        opt_shapes = kwargs.pop('opt_shapes', None)
        logging.info(f"dynamic_axes={dynamic_axes}")

        profiles = {}
        for i in range(network.num_inputs):
            shape = network.get_input(i).shape
            dynamic = any([s < 1 for s in shape])
            if dynamic:
                logging.info(f"dynamic network.get_input({i}).shape={shape}")
                profile = builder.create_optimization_profile()
                min = min_shapes and (1, *min_shapes[i]) or (1, *shape[1:])
                max = max_shapes and (max_batch_size, *max_shapes[i]) or (max_batch_size, *shape[1:])
                opt = opt_shapes and (max_batch_size, *opt_shapes[i]) or max
                profile.set_shape(input_names[i], min=trt.Dims(min), opt=trt.Dims(opt), max=trt.Dims(max))
                idx = cfg.add_optimization_profile(profile)
                profiles[idx] = profile
                logging.info(f"set dynamic {input_names[i]}.shape to min={min}, opt={opt}, max={max} in profile[{idx}]")
            else:
                logging.info(f"network.get_input({i}).shape={shape}")
        for i in range(network.num_outputs):
            shape = network.get_output(i).shape
            logging.info(f"network.get_output({i}).shape={shape}")
        logging.info(f"building TensorRT engine with fp16={fp16_mode}, int8={int8_mode}, strict={strict_type_constraints}")
        if int8_mode:
            assert network.num_inputs == 1, "Only one dynamic tensor input is supported for int8 calibration"
            cfg.set_calibration_profile(profiles[0])
        engine = builder.build_engine(network, cfg)
    module_trt = TRTPredictor(engine, input_names, output_names)
    if keep_network:
        module_trt.network = network
    return module_trt

def build(path):
    """Build an inference engine from a serialized model.
    Args:
        path: model or path to a saved onnx/trt checkpoint
    """
    logging.info("Deserializing the TensorRT engine from {}".format(path))
    with open(path, "rb") as f, trt.Logger() as logger, trt.Runtime(logger) as runtime:
        return runtime.deserialize_cuda_engine(f.read())


#!/usr/bin/env python
from __future__ import print_function
from dbconnect import *
from datamodel import DataModel
from properties import Properties
from StringIO import StringIO, StringIO
from trainingset import TrainingSet
from time import time
import dirichletintegrate
import fastgentleboostingmulticlass
import multiclasssql
import polyafit
import logging
import numpy as np
import os
import sys
import wx

USAGE = '''
ABOUT:
This script will train a classifier for a given properties file and training
set and output a results table. You can also use it to write directly to a
database table.

USAGE:
python scoreall.py <propertiesfile> <trainingset>
'''

def score(properties, ts, nRules, filter_name=None, group='Image',
          show_results=False, results_table=None, overwrite=False):
    '''
    Trains a Classifier on a training set and scores the experiment
    returns the table of scores as a numpy array.

    properties    -- Properties instance
    ts            -- TrainingSet instance
    nRules        -- number of rules to use
    filter_name   -- name of a filter to use from the properties file
    group         -- name of a group to use from the properties file
    show_results  -- whether or not to show the results in TableViewer
    results_table -- table name to save results to or None.
    '''

    p = properties
    db = DBConnect.getInstance()
    dm = DataModel.getInstance()

    if group == None:
        group = 'Image'

    if results_table:
        if db.table_exists(results_table) and not overwrite:
            print('Table "%s" already exists. Delete this table before running scoreall.'%(results_table))
            return None

    print('')
    print('properties:    ', properties)
    print('training set:  ', ts)
    print('# rules:       ', nRules)
    print('filter:        ', filter_name)
    print('grouping by:   ', group)
    print('show results:  ', show_results)
    print('results table: ', results_table)
    print('overwrite:     ', overwrite)
    print('')

    nClasses = len(ts.labels)
    nKeyCols = len(image_key_columns())

    assert 200 > nRules > 0, '# of rules must be between 1 and 200.  Value was %s'%(nRules,)
    assert filter_name in p._filters.keys()+[None], 'Filter %s not found in properties file.  Valid filters are: %s'%(filter_name, ','.join(p._filters.keys()),)
    assert group in p._groups.keys()+['Image'], 'Group %s not found in properties file.  Valid groups are: %s'%(group, ','.join(p._groups.keys()),)

    output = StringIO()
    logging.info('Training classifier with %s rules...'%nRules)
    t0 = time()
    weaklearners = fastgentleboostingmulticlass.train(ts.colnames,
                                                      nRules, ts.label_matrix,
                                                      ts.values, output)
    logging.info('Training done in %f seconds'%(time()-t0))

    logging.info('Computing per-image class counts...')
    t0 = time()
    def update(frac):
        logging.info('%d%% '%(frac*100.,))
    keysAndCounts = multiclasssql.PerImageCounts(weaklearners, filter_name=(filter_name or None), cb=update)
    keysAndCounts.sort()
    logging.info('Counts found in %f seconds'%(time()-t0))

    if not keysAndCounts:
        logging.error('No images are in filter "%s". Please check the filter definition in your properties file.'%(filter_name))
        raise Exception('No images are in filter "%s". Please check the filter definition in your properties file.'%(filter_name))

    # AGGREGATE PER_IMAGE COUNTS TO GROUPS IF NOT GROUPING BY IMAGE
    if group != 'Image':
        logging.info('Grouping %s counts by %s...' % (p.object_name[0], group))
        t0 = time()
        imData = {}
        for row in keysAndCounts:
            key = tuple(row[:nKeyCols])
            imData[key] = np.array([float(v) for v in row[nKeyCols:]])

        groupedKeysAndCounts = np.array([list(k)+vals.tolist() for k, vals in dm.SumToGroup(imData, group).items()], dtype=object)
        nKeyCols = len(dm.GetGroupColumnNames(group))
        logging.info('Grouping done in %f seconds'%(time()-t0))
    else:
        groupedKeysAndCounts = np.array(keysAndCounts, dtype=object)

    # FIT THE BETA BINOMIAL
    logging.info('Fitting beta binomial distribution to data...')
    counts = groupedKeysAndCounts[:,-nClasses:]
    alpha, converged = polyafit.fit_betabinom_minka_alternating(counts)
    logging.info('   alpha = %s   converged = %s'%(alpha, converged))
    logging.info('   alpha/Sum(alpha) = %s'%([a/sum(alpha) for a in alpha]))

    # CONSTRUCT ARRAY OF TABLE DATA
    logging.info('Computing enrichment scores for each group...')
    t0 = time()
    tableData = []
    for i, row in enumerate(groupedKeysAndCounts):
        # Start this row with the group key:
        tableRow = list(row[:nKeyCols])

        if group != 'Image':
            tableRow += [len(dm.GetImagesInGroup(group, tuple(row[:nKeyCols])))]
        # Append the counts:
        countsRow = [int(v) for v in row[nKeyCols:nKeyCols+nClasses]]
        tableRow += [sum(countsRow)]
        tableRow += countsRow
        if p.area_scoring_column is not None:
            # Append the areas
            countsRow = [int(v) for v in row[-nClasses:]]
            tableRow += [sum(countsRow)]
            tableRow += countsRow

        # Append the scores:
        #   compute enrichment probabilities of each class for this image OR group
        scores = np.array( dirichletintegrate.score(alpha, np.array(countsRow)) )
        #   clamp to [0,1] to
        scores[scores>1.] = 1.
        scores[scores<0.] = 0.
        tableRow += scores.tolist()
        # Append the logit scores:
        #   Special case: only calculate logit of "positives" for 2-classes
        if nClasses==2:
            tableRow += [np.log10(scores[0])-(np.log10(1-scores[0]))]   # compute logit of each probability
        else:
            tableRow += [np.log10(score)-(np.log10(1-score)) for score in scores]   # compute logit of each probability
        tableData.append(tableRow)
    tableData = np.array(tableData, dtype=object)
    logging.info('Enrichments computed in %f seconds'%(time()-t0))

    # CREATE COLUMN LABELS LIST
    # if grouping isn't per-image, then get the group key column names.
    if group != 'Image':
        colnames = dm.GetGroupColumnNames(group)
    else:
        colnames = list(image_key_columns())

    # record the column indices for the keys
    key_col_indices = [i for i in range(len(colnames))]

    if group != 'Image':
        colnames += ['Number_of_Images']
    colnames += ['Total_%s_Count'%(p.object_name[0].capitalize())]
    for i in xrange(nClasses):
        colnames += ['%s_%s_Count'%(ts.labels[i].capitalize(), p.object_name[0].capitalize())]
    if p.area_scoring_column is not None:
        colnames += ['Total_%s_Area'%(p.object_name[0].capitalize())]
        for i in xrange(nClasses):
            colnames += ['%s_%s_Area'%(ts.labels[i].capitalize(), p.object_name[0].capitalize())]
    for i in xrange(nClasses):
        colnames += ['pEnriched_%s'%(ts.labels[i])]
    if nClasses==2:
        colnames += ['Enriched_Score_%s'%(ts.labels[0])]
    else:
        for i in xrange(nClasses):
            colnames += ['Enriched_Score_%s'%(ts.labels[i])]

    title = results_table or "Enrichments_per_%s"%(group,)
    if filter_name:
        title += "_filtered_by_%s"%(filter_name,)
    title += ' (%s)'%(os.path.split(p._filename)[1])

    if results_table:
        print('Creating table %s'%(results_table))
        success = db.CreateTableFromData(tableData, colnames, results_table, temporary=False)
        if not success:
            print('Failed to create results table :(')

    if show_results:
        import tableviewer
        tableview = tableviewer.TableViewer(None, title=title)
        if results_table and overwrite:
            tableview.load_db_table(results_table)
        else:
            tableview.table_from_array(tableData, colnames, group, key_col_indices)
        tableview.set_fitted_col_widths()
        tableview.Show()
    return tableData

#
# run
#
if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)

    if len(sys.argv) < 3:
        print(USAGE)
        sys.exit()

    props_file = sys.argv[1]
    ts_file    = sys.argv[2]

    app = wx.PySimpleApp()

    nRules = int(raw_input('# of rules: '))

    filter_name = raw_input('Filter name (return for none): ')
    if filter_name == '':
        filter_name = None

    group = raw_input('Group name (return for none): ')
    if group=='':
        group = 'Image'

    results_table = raw_input('Results table name (return for none): ')

    logging.info('Loading properties file...')
    p = Properties.getInstance()
    p.LoadFile(props_file)
    logging.info('Loading training set...')
    ts = TrainingSet(p)
    ts.Load(ts_file)

    score(p, ts, nRules, filter_name, group, show_results=True,
          results_table=results_table, overwrite=False)

    app.MainLoop()

    #
    # Kill the Java VM
    #
    try:
        import javabridge
        javabridge.kill_vm()
    except:
        import traceback
        traceback.print_exc()
        print("Caught exception while killing VM")


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

import pydevd_pycharm
pydevd_pycharm.settrace('207.216.103.218', port=30266, stdoutToServer=True, stderrToServer=True)

import datetime
import json
import time
import numpy as np
from Helpers.data_loader import get_feature_dict, load_gene_expression_data, printProgressBar
import sys

# val = float(0.000000001 / 100000000)
# test = np.asarray([0.000000001 / 100000000, 0.000000001 / 100000000], dtype='float16')
#
# print(test)
# quit(0)

start_time = time.time()
gene_count_data_limit = 978
# target_cell_names = ['VCAP', 'A549', 'A375', 'PC3', 'MCF7', 'HT29']
target_cell_names = ['HT29']
save_xy_path = "TrainData/"
LINCS_data_path = "/home/integra/Data/LINCS/LDS-1191/"  # set this path to your LINCS gctx file
if LINCS_data_path == "":
    print("You need to set the LINCS data path")
    sys.exit()
data_path = "Data/"
dosage = 10

def find_nth(haystack, needle, n):
    start = haystack.find(needle)
    while start >= 0 and n > 1:
        start = haystack.find(needle, start+len(needle))
        n -= 1
    return start

def get_gene_id_dict():
    lm_genes = json.load(open('Data/landmark_genes.json'))
    dict = {}
    for lm_gene in lm_genes:
        # dict[lm_gene['gene_symbol']] = lm_gene['entrez_id']
        dict[lm_gene['entrez_id']] = lm_gene['gene_symbol']
    return dict

# get the dictionaries
print(datetime.datetime.now(), "Loading drug and gene features")
drug_features_dict = get_feature_dict('Data/phase1_compounds_morgan_2048.csv')
gene_features_dict = get_feature_dict('Data/go_fingerprints.csv')
cell_name_to_id_dict = get_feature_dict('Data/Phase1_Cell_Line_Metadata.txt', '\t', 2)
experiments_dose_dict = get_feature_dict(LINCS_data_path + 'Metadata/GSE92742_Broad_LINCS_sig_info.txt', '\t', 0)
gene_id_dict = get_gene_id_dict()
covid_genes = get_feature_dict('Data/covid_genes.csv')

lm_gene_entrez_ids = []
for gene in gene_id_dict:
    lm_gene_entrez_ids.append(gene)

print("Loading gene expressions from gctx")
level_5_gctoo = load_gene_expression_data(LINCS_data_path + "Data/GSE92742_Broad_LINCS_Level5_COMPZ.MODZ_n473647x12328.gctx",
                                          lm_gene_entrez_ids)
length = len(level_5_gctoo.col_metadata_df.index)
# length = 10000

for target_cell_name in target_cell_names:
    target_cell_id = cell_name_to_id_dict[target_cell_name][0]

    cell_X = {}  # stores rows used in X
    cell_Y = {}  # stores the highest perturbation for that experiment
    cell_Y_gene_ids = []
    cell_drugs_counts = 0
    repeat_X = {}

    print("Loading experiments")
    # For every experiment
    for i in range(0, length):
        printProgressBar(i, length, prefix='Load experiments progress')
        col_name = level_5_gctoo.col_metadata_df.index[i]
        column = level_5_gctoo.data_df[col_name]

        # parse the time
        start = col_name.rfind("_")
        end = find_nth(col_name, ":", 1)
        exposure_time = col_name[start + 1:end]
        if exposure_time != "24H":  # column counts: 6h 102282, 24h 118597, 48h 1453, 144h 18487, 3h 612
            continue

        # get drug features
        col_name_key = col_name
        if col_name_key not in experiments_dose_dict:
            continue
        experiment_data = experiments_dose_dict[col_name_key]
        drug_id = experiment_data[0]
        if drug_id not in drug_features_dict:
            continue
        drug_features = drug_features_dict[drug_id]

        # parse the dosage unit and value
        dose_unit = experiment_data[5]
        if dose_unit != 'µM':
            # remove any dosages that are not 'µM'. Want to standardize the dosages.
            # column counts: -666 17071, % 2833, uL 238987, uM 205066, ng 1439, ng / uL 2633, ng / mL 5625
            continue
        dose_amt = float(experiment_data[4])
        if dose_amt < dosage - 0.1 or dose_amt > dosage + 0.1:  # 10µM +/- 0.1
            continue

        # parse the cell name
        start = find_nth(col_name, "_", 1)
        end = find_nth(col_name, "_", 2)
        cell_name = col_name[start + 1:end]
        if cell_name != target_cell_name:
            continue

        if cell_name not in cell_name_to_id_dict:
            continue
        cell_id = cell_name_to_id_dict[cell_name][0]

        # go through the covid genes to get the denominator
        pert_sum = 0
        covid_count = 0
        for gene_id in lm_gene_entrez_ids:
            gene_symbol = gene_id_dict[gene_id]
            if gene_symbol not in covid_genes:
                continue
            covid_count += 1
            pert = column[gene_id].astype('float16')
            negative = float(covid_genes[gene_symbol][1]) < 0
            if negative:
                pert_sum -= pert
            else:
                pert_sum += pert
        denominator = (pert_sum/covid_count)/10
        if denominator == 0:
            continue
        for gene_id in lm_gene_entrez_ids:
            gene_symbol = gene_id_dict[gene_id]

            if gene_symbol not in gene_features_dict:
                continue

            pert = column[gene_id].astype('float16')
            # repeat key is used to find the largest perturbation for similar experiments and filter out the rest
            repeat_key = drug_id + "_" + gene_id

            if repeat_key not in cell_X:
                cell_X[repeat_key] = drug_features + gene_features_dict[gene_symbol]
                cell_Y[repeat_key] = []
                cell_Y_gene_ids.append(gene_id)
                cell_drugs_counts += 1
            cell_Y[repeat_key].append((pert/10)/denominator)

    elapsed_time = time.time() - start_time
    print(datetime.datetime.now(), "Time to load data:", elapsed_time)

    print("Sample Size:", len(cell_Y), "Drugs tested:", cell_drugs_counts / gene_count_data_limit)

    # at this point all the perturbation values are stored in cell_Y
    # save the data to be loaded and used multiple times if needed
    model_file_prefix = save_xy_path + target_cell_name
    # save_file = model_file_prefix + "_X"
    # npX = np.asarray(list(cell_X.values()), dtype='float16').astype(dtype='int8')
    # np.savez_compressed(save_file, npX)
    # print("saved", save_file)

    save_file = model_file_prefix + "_Y"
    for key in cell_Y.keys(): # convert from list to average
        vals = cell_Y[key]
        cell_Y[key] = sum(vals) / len(vals)

    keys = list(cell_Y.keys())
    np.savez_compressed('drug_id_gene_id', keys)
    # npY = np.asarray(list(cell_Y.values()), dtype='float16')
    # np.savez_compressed (save_file, npY)
    # print("saved", save_file)


import cv2
import numpy as np
from collections import deque
import pyautogui
def nothing(x):
    print(x)

buffer = 20

#Points deque structure storing 'buffer' no. of object coordinates
pts = deque(maxlen = buffer)
(dX, dY) = (0, 0)
counter = 0
direction = ''
#so i will be able to use different color for the stick
'''
cv2.namedWindow("Tracking")
#making trackbar (horizontal slider)
cv2.createTrackbar('LH',"Tracking",0,360,nothing)
cv2.createTrackbar('LS',"Tracking",0,360,nothing)
cv2.createTrackbar('LV',"Tracking",0,360,nothing)
cv2.createTrackbar('UH',"Tracking",255,255,nothing)
cv2.createTrackbar('US',"Tracking",255,255,nothing)
cv2.createTrackbar('UV',"Tracking",255,255,nothing)
'''
#making a window
cv2.namedWindow("Image")
#opening the webcam
cap=cv2.VideoCapture(0)
#reading two frame for object detection
ret,frame1 = cap.read()
ret,frame2 = cap.read()
frame1 = cv2.GaussianBlur(frame1, (5,5), 0)
frame2 = cv2.GaussianBlur(frame2, (5,5), 0)
while(1):
    #the open cv does open in BGR converting it to hsv
    imghsv1=cv2.cvtColor(frame1,cv2.COLOR_BGR2HSV)
    imghsv2=cv2.cvtColor(frame2,cv2.COLOR_BGR2HSV)
    '''
    lb=cv2.getTrackbarPos('LH',"Tracking")
    lg=cv2.getTrackbarPos('LS',"Tracking")
    lr=cv2.getTrackbarPos('LV',"Tracking")

    ub=cv2.getTrackbarPos('UH',"Tracking")
    ug=cv2.getTrackbarPos('US',"Tracking")
    ur=cv2.getTrackbarPos('UV',"Tracking")
    l_b=np.array([lb,lg,lr])
    u_b=np.array([ub,ug,ur])
    '''
    #these numbers are based on exprience
    l_b=np.array([72,127,0])
    u_b=np.array([109,255,255])
    #giving the range (to find the right color on the image)
    mask1=cv2.inRange(imghsv1,l_b,u_b)
    mask2=cv2.inRange(imghsv2,l_b,u_b)
    #making a mask(deleting all other colors)
    res1=cv2.bitwise_and(frame1,frame1,mask=mask1)
    res2=cv2.bitwise_and(frame2,frame2,mask=mask2)
    #less noise
    diff=cv2.absdiff(res1,res2)
    Gray=cv2.cvtColor(diff,cv2.COLOR_BGR2GRAY)
    blured=cv2.GaussianBlur(Gray,(5,5),0)
    ret,thresh=cv2.threshold(blured,20,255,cv2.THRESH_BINARY)
    dilated=cv2.dilate(thresh,None,iterations=5)
    contours , hierarchy=cv2.findContours(dilated,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    x1=0
    y1=0
    if(len(contours)) > 0:
        c = max(contours, key = cv2.contourArea)
            #Find the center of the circle, and its radius of the largest detected contour.
        ((x,y), radius) = cv2.minEnclosingCircle(c)

        #Calculate the centroid of the ball, as we need to draw a circle around it.
        M = cv2.moments(c)
        center = (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))

        #Proceed only if a ball of considerable size is detected
        if radius > 10:
            #Draw circles around the object as well as its centre
            cv2.circle(frame1, (int(x), int(y)), int(radius), (0,255,255), 2)
            cv2.circle(frame1, center, 5, (255,0,0), -1)
            pts.appendleft(center)
            #Append the detected object in the frame to pts deque structure
    for i in np.arange(1, len(pts)):
        #If no points are detected, move on.
        if(pts[i-1] == None or pts[i] == None):
            continue

        #If atleast 10 frames have direction change, proceed
        if counter >= 15 and i == 1 and pts[-15] is not None:
            #Calculate the distance between the current frame and 10th frame before
            dX = pts[-15][0] - pts[i][0]
            dY = pts[-15][1] - pts[i][1]
            (dirX, dirY) = ('', '')

            #If distance is greater than 100 pixels, considerable direction change has occured.
            if np.abs(dX) > 100:
                if (np.sign(dX) == 1):
                    dirX = 'Right'
                    print("Right")
                    pyautogui.press('right')
                else :
                    dirX ='Left'
                    print("Left")
                    pyautogui.press('left')

            if np.abs(dY) > 100:
                if (np.sign(dY) == 1):
                    dirY = 'Up'
                    print("Up")
                    pyautogui.press('up')
                else:
                    dirY ='Down'
                    print("Down")
                    pyautogui.press('down')

            #Set direction variable to the detected direction
            direction = dirX if dirX != '' else dirY

        #Draw a trailing red line to depict motion of the object.
        thickness = int(np.sqrt(buffer / float(i + 1)) * 2.5)
        cv2.line(frame1, pts[i - 1], pts[i], (0, 0, 255), thickness)
        #frame1=cv2.rectangle(frame1,(x,y),(x+w,y+h),(0,255,0),2)
        cv2.putText(frame1, direction, (20,40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 3)
        #cv2.putText(frame1,"Status : {}".format("movment"),(10,20),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),3)

    counter += 1

    #showing all the Images
    cv2.imshow("Image",frame1)
    cv2.imshow("Image2",res1)
    cv2.imshow("Image3",mask1)
    frame1=frame2
    _,frame2 = cap.read()
    frame2 = cv2.GaussianBlur(frame2, (5,5), 0)
    if cv2.waitKey(1)==27:
        break
cap.release()
cv2.destroyAllWindows()


import gc
import os

import numpy as np
import pandas as pd
import tables

class Encoder(object):

    def __init__(self, out):
        self.name = 'Encoder'
        self.N_matrix = None
        self.F = None
        self.F_inv = None
        self.encoder_chunks = {}
        self.hdf5_iter = 0
        self.npy_iter = 0
        self.pytable_filters = tables.Filters(complevel=9, complib='zlib')
        self.out = out
        self.metadata = None
        self.study_name = None
        self.phen_info_dic = {}
        self.phen_info_dic['id'] = None
        try:
            print('Creating directories...')
            os.mkdir(os.path.join(self.out, 'encode_genotype'))
            os.mkdir(os.path.join(self.out, 'encode_phenotype'))
            os.mkdir(os.path.join(self.out, 'encode_individuals'))

        except:
            print((
                'Directories "encode_genotype","encode_phenotype","encode_individuals" are already exist in {}...'.format(
                    self.out)))

    def matrix(self, N, save=False):

        self.F = np.random.randint(1, 10, N * N).reshape(N, N)
        self.F_inv = np.linalg.inv(self.F)
        self.F = np.array(self.F, dtype=np.float)
        if save:
            print(('Saving decoders Matrices to...{}'.format(self.out)))
            np.save(os.path.join(self.out, 'F.npy'), self.F)
            np.save(os.path.join(self.out, 'F_inv.npy'), self.F_inv)

    def encode(self, data, data_type=None):

        if isinstance(self.F, type(None)):
            raise ValueError('Encode matrix is not define')

        x, y = data.shape
        # TODO (mid) add to encode_chunk info about data size and/or names indexes
        # TODO (mid) check the protocol if exist... all study on single stage

        a, b = self.F.shape

        if isinstance(data_type, type(None)):
            raise ValueError('data_type is None')
        elif data_type == "genotype":
            print('Decoding genotype')
            return np.dot(data, self.F)
        elif data_type == 'phenotype':
            print('Decoding phenotype')
            return np.dot(self.F_inv, data)

    def save_npy(self, data, save_path=None, info=None, index=None):
        # only for phenotype
        if isinstance(save_path, type(None)) or not os.path.isdir(save_path):
            raise ValueError('There is no such path or directory {}'.format(save_path))
        np.save(os.path.join(save_path, str(self.npy_iter) + '_' + self.study_name + '.npy'), data)
        if self.phen_info_dic['id'] is None:
            self.phen_info_dic['id'] = np.array(info._data.id)[index]
        self.phen_info_dic[str(self.npy_iter) + '_' + self.study_name + '.npy'] = info._data.names[
                                                                                info._data.start:info._data.finish]
        self.npy_iter += 1

    def save_csv(self, data, save_path=None, info=None, index=None):
        if isinstance(save_path, type(None)) or not os.path.isdir(save_path):
            raise ValueError('There is no such path or directory {}'.format(save_path))
        df = pd.DataFrame(data)
        df.columns = info._data.names[info._data.start:info._data.finish]
        df.insert(0, 'id', np.array(info._data.id)[index])
        df.to_csv(os.path.join(save_path, str(self.npy_iter) + '_' + self.study_name + '.csv'), sep='\t', index=False)
        self.npy_iter += 1

    def save_hdf5(self, data, save_path=None, info=None, index=None):
        # only for genetics data
        print(('Saving data to ... {}'.format(
            os.path.join(save_path, str(self.hdf5_iter) + '_' + self.study_name + '_encoded.h5'))))

        if not os.path.isfile(os.path.join(self.out, 'encode_individuals', self.study_name + '.h5')):
            chunk = pd.DataFrame.from_dict({"individual": info._data.id[index]})
            chunk.to_hdf(os.path.join(self.out, 'encode_individuals', self.study_name + '.h5'), key='individuals',
                         format='table', min_itemsize=25, complib='zlib', complevel=9)

        if isinstance(save_path, type(None)) or not os.path.isdir(save_path):
            raise ValueError('There is no such path or directory {}'.format(save_path))

        self.h5_gen_file = tables.open_file(
            os.path.join(save_path, str(self.hdf5_iter) + '_' + self.study_name + '.h5'), 'w', title='encode_genotype')
        self.hdf5_iter += 1

        atom = tables.Float64Atom()
        self.genotype = self.h5_gen_file.create_carray(self.h5_gen_file.root, 'genotype', atom,
                                                       (data.shape),
                                                       title='Genotype', filters=self.pytable_filters)
        self.genotype[:] = data
        self.h5_gen_file.close()
        gc.collect()


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

import random
import numpy as np
import math
import ctypes as C
import logging

__all__ = [ 'RateConverter', 'ConvertError' ]

SINC_BEST_QUALITY   = 0
SINC_MEDIUM_QUALITY = 1
SINC_FASTEST        = 2
ZERO_ORDER_HOLD     = 3
LINEAR              = 4

class _SRC_STATE(C.Structure):
  pass

class _SRC_DATA(C.Structure):
  _fields_ = [ ('data_in',           C.POINTER(C.c_float)),
               ('data_out',          C.POINTER(C.c_float)),
               ('input_frames',      C.c_long),
               ('output_frames',     C.c_long),
               ('input_frames_used', C.c_long),
               ('output_frames_gen', C.c_long),
               ('end_of_input',      C.c_int),
               ('src_ratio',         C.c_double),
             ]

try:
  _srclib = C.CDLL('libsamplerate.dylib')
except OSError:
  try:
    _srclib = C.CDLL('libsamplerate.so')
  except OSError:
    try:
      _srclib = C.CDLL('libsamplerate.dll')
    except OSError:
      raise OSError("Unable to load libsamplerate library")

src_new = _srclib.src_new
src_new.argtypes = [C.c_int, C.c_int, C.POINTER(C.c_int)]
src_new.restype = C.POINTER(_SRC_STATE)

src_strerror = _srclib.src_strerror
src_strerror.argtypes = [C.c_int]
src_strerror.restype = C.c_char_p

src_set_ratio = _srclib.src_set_ratio
src_set_ratio.argtypes = [C.POINTER(_SRC_STATE), C.c_double]

src_process = _srclib.src_process
src_process.argtypes = [C.POINTER(_SRC_STATE), C.POINTER(_SRC_DATA)]
src_process.restype = C.c_int

src_delete = _srclib.src_delete
src_delete.argtypes = [C.POINTER(_SRC_STATE)]

MAX_OUTPUT_FRAMES = 50000       #: For each yielded data block

class ConvertError(Exception):
#============================#
  pass

class RateConverter(object):
#===========================

  def __init__(self, rate, channels=1, maxoutput=MAX_OUTPUT_FRAMES, method=SINC_MEDIUM_QUALITY):
  #---------------------------------------------------------------------------------------------
    error = C.c_int()
    self._converter = src_new(method, channels, C.byref(error))
    if not self._converter: raise ConvertError(src_strerror(error))
    self.rate = float(rate)
    self._channels = channels
    self._maxoutframes = maxoutput

  def __del__(self):
  #-----------------
    if self._converter:
      src_delete(self._converter)

  def convert(self, data, rate=None, finished=False):
  #--------------------------------------------------
    if data is not None:
      self._inshape = data.shape
      inframes = self._inshape[0]
      assert(self._channels == data.size/inframes)
    else:
      data = np.array([0])
      inframes = 0
    input = data.flatten().astype(np.float32)
    assert(input.flags['CONTIGUOUS'])
    inpos = 0
    outframes = 1 # For first iteration, actual value set below
    while inframes > 0 or finished and outframes > 0:
      data = _SRC_DATA()
      data.data_in = input[inpos:].ctypes.data_as(C.POINTER(C.c_float))
      data.input_frames = inframes
      data.end_of_input = 1 if finished else 0
      if rate is not None: data.src_ratio = self.rate/rate
      else:                data.src_ratio = 1.0
      output = np.zeros(self._channels*self._maxoutframes, dtype=np.float32)
      data.data_out = output.ctypes.data_as(C.POINTER(C.c_float))
      data.output_frames = self._maxoutframes

      error = src_process(self._converter, data)
      if error: raise ConvertError(src_strerror(error))

      inframes -= data.input_frames_used
      inpos += self._channels*data.input_frames_used
      outframes = data.output_frames_gen
      outshape = (outframes,) + self._inshape[1:]
      if outframes > 0:
        yield output[:self._channels*outframes].reshape(outshape)

if __name__ == "__main__":
#========================#

  times = np.linspace(0.0, 2.0*np.pi, 100)
  signal = np.sin(times)

#  print signal

  resampler = RateConverter(2, maxoutput=50)
  #for o in resampler.convert(signal, 1, finished=True):
  g = resampler.convert(signal, 1, finished=True)
  for o in g:
    print len(o)

  del resampler


import numpy as np
import torch
import torch.utils.data as data
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
import os, random, glob, cv2

class DALY2DEMO(data.Dataset):
    def __init__(self, data_dir='data', transform=None):
        super(DALY2DEMO, self).__init__()
        # load split file
        self.data_dir = data_dir
        img_path = os.path.join(data_dir, 'demo_video/test_frames')
        self.indexlist = glob.glob(img_path + '/*/*.jpeg')
        self.transform = transform

    def load_img(self, index):
        info = self.indexlist[index]
        vid, im_name = info.split('/')[-2:]

        img_path = info
        img = Image.open(img_path).convert('RGB')
        w, h = img.size

        annot_path = os.path.join(self.data_dir, 'demo_video/face_bbox', vid, im_name.strip('.jpeg')+'.npz')
        annot = np.load(annot_path)

        face_bbox = torch.DoubleTensor(50,5).zero_()
        if len(annot['gt']) > 0:
            #  print('len:', len(annot['gt']))
            for i in range(len(annot['gt'])):
                #  print('i:' , i)
                _bbox = annot['gt'][i]
                # enlarge the bbox 2.2x
                x1 = _bbox[0]
                y1 = _bbox[1]
                x2 = _bbox[2]
                y2 = _bbox[3]
                _bbox[0] = max(0, 1.3*x1 - 0.3*x2)
                _bbox[1] = max(0, 1.3*y1 - 0.3*y2)
                _bbox[2] = min(w-1, 1.3*x2 - 0.3*x1)
                _bbox[3] = min(h-1, 1.3*y2 - 0.3*y1)
                face_bbox[i] = torch.DoubleTensor(_bbox)
            face_count = len(annot['gt'])
        else:
            face_count = 0

        return img, face_bbox, face_count

    def __getitem__(self, index):
        _img, face_bbox, face_count = self.load_img(index)

        if self.transform:
            img = self.transform(_img)
        #  print(img.size(), face_bbox.size(), face_count)
        return img, face_bbox, face_count, index

    def __len__(self):
        return len(self.indexlist)

def main():
    import torch
    from torchvision import transforms, utils
    from torch.utils.data import DataLoader
    normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    g_data = DALY2DEMO(data_dir='/home/SSD5/jason-data/Privacy/dataset/DALY',
            transform=transforms.Compose([
                        transforms.Resize((600, 800)),
                        transforms.ToTensor(), normalize]))
    print(len(g_data))
    dataloader = DataLoader(g_data, batch_size=4, shuffle=False, num_workers=1)
    for i, data in enumerate(dataloader):
        im, bbox, flag,idx = data
        print(im.size(), bbox.size(), flag.size())
        utils.save_image(im, str(i) + '.jpg', normalize=True)

if __name__ == "__main__":
    main()


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

from micropsi_core.nodenet.node import Node, Gate, Slot
from micropsi_core.nodenet.theano_engine.theano_link import TheanoLink
from micropsi_core.nodenet.theano_engine.theano_stepoperators import *
from micropsi_core.nodenet.theano_engine.theano_definitions import *
import numpy as np

class TheanoNode(Node):
    """
        theano node proxy class
    """

    def __init__(self, nodenet, partition, parent_uid, uid, type, parameters={}, **_):

        self._numerictype = type
        self._id = node_from_id(uid)
        self._uid = uid
        self._parent_id = nodespace_from_id(parent_uid)
        self._nodenet = nodenet
        self._partition = partition
        self._state = {}

        self.__gatecache = {}
        self.__slotcache = {}

        self.parameters = None

        strtype = get_string_node_type(type, nodenet.native_modules)

        Node.__init__(self, strtype, nodenet.get_nodetype(strtype))

        if strtype in nodenet.native_modules or strtype == "Comment":
            self.slot_activation_snapshot = {}
            self._state = {}

            if parameters is not None:
                self.parameters = parameters.copy()
            else:
                self.parameters = {}

    @property
    def uid(self):
        return self._uid

    @property
    def pid(self):
        return self._partition.pid

    @property
    def index(self):
        return self._id

    @index.setter
    def index(self, index):
        raise NotImplementedError("index can not be set in theano_engine")

    @property
    def position(self):
        return self._nodenet.positions.get(self.uid, [10, 10, 0])

    @position.setter
    def position(self, position):
        if position is None and self.uid in self._nodenet.positions:
            del self._nodenet.positions[self.uid]
        else:
            position = list(position)
            position = (position + [0] * 3)[:3]
            self._nodenet.positions[self.uid] = position
        self._partition.node_changed(self.uid)

    @property
    def name(self):
        return self._nodenet.names.get(self.uid, self.uid)

    @name.setter
    def name(self, name):
        if name is None or name == "" or name == self.uid:
            if self.uid in self._nodenet.names:
                del self._nodenet.names[self.uid]
        else:
            self._nodenet.names[self.uid] = name

    @property
    def parent_nodespace(self):
        return nodespace_to_id(self._parent_id, self._partition.pid)

    @property
    def activation(self):
        return float(self._partition.a.get_value(borrow=True)[self._partition.allocated_node_offsets[self._id] + GEN])

    @property
    def activations(self):
        return {"default": self.activation}

    @activation.setter
    def activation(self, activation):
        a_array = self._partition.a.get_value(borrow=True)
        a_array[self._partition.allocated_node_offsets[self._id] + GEN] = activation
        self._partition.a.set_value(a_array, borrow=True)

    def get_gate(self, type):
        if type not in self.__gatecache:
            self.__gatecache[type] = TheanoGate(type, self, self._nodenet, self._partition)
        return self.__gatecache[type]

    def set_gatefunction_name(self, gate_type, gatefunction_name):
        self._nodenet.set_node_gatefunction_name(self.uid, gate_type, gatefunction_name)

    def get_gatefunction_name(self, gate_type):
        g_function_selector = self._partition.g_function_selector.get_value(borrow=True)
        return get_string_gatefunction_type(g_function_selector[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type(gate_type, self.nodetype)])

    def get_gatefunction_names(self):
        result = {}
        g_function_selector = self._partition.g_function_selector.get_value(borrow=True)
        for numericalgate in range(0, get_gates_per_type(self._numerictype, self._nodenet.native_modules)):
            result[get_string_gate_type(numericalgate, self.nodetype)] = \
                get_string_gatefunction_type(g_function_selector[self._partition.allocated_node_offsets[self._id] + numericalgate])
        return result

    def set_gate_parameter(self, gate_type, parameter, value):
        self._nodenet.set_node_gate_parameter(self.uid, gate_type, parameter, value)

    def get_gate_parameters(self):
        return self.clone_non_default_gate_parameters()

    def clone_non_default_gate_parameters(self, gate_type=None):
        g_threshold_array = self._partition.g_threshold.get_value(borrow=True)
        g_amplification_array = self._partition.g_amplification.get_value(borrow=True)
        g_min_array = self._partition.g_min.get_value(borrow=True)
        g_max_array = self._partition.g_max.get_value(borrow=True)
        g_theta = self._partition.g_theta.get_value(borrow=True)

        gatemap = {}
        gate_types = self.nodetype.gate_defaults.keys()
        if gate_type is not None:
            if gate_type in gate_types:
                gate_types = [gate_type]
            else:
                return None

        for gate_type in gate_types:
            numericalgate = get_numerical_gate_type(gate_type, self.nodetype)
            gate_parameters = {}

            threshold = g_threshold_array[self._partition.allocated_node_offsets[self._id] + numericalgate].item()
            if 'threshold' not in self.nodetype.gate_defaults[gate_type] or threshold != self.nodetype.gate_defaults[gate_type]['threshold']:
                gate_parameters['threshold'] = threshold

            amplification = g_amplification_array[self._partition.allocated_node_offsets[self._id] + numericalgate].item()
            if 'amplification' not in self.nodetype.gate_defaults[gate_type] or amplification != self.nodetype.gate_defaults[gate_type]['amplification']:
                gate_parameters['amplification'] = amplification

            minimum = g_min_array[self._partition.allocated_node_offsets[self._id] + numericalgate].item()
            if 'minimum' not in self.nodetype.gate_defaults[gate_type] or minimum != self.nodetype.gate_defaults[gate_type]['minimum']:
                gate_parameters['minimum'] = minimum

            maximum = g_max_array[self._partition.allocated_node_offsets[self._id] + numericalgate].item()
            if 'maximum' not in self.nodetype.gate_defaults[gate_type] or maximum != self.nodetype.gate_defaults[gate_type]['maximum']:
                gate_parameters['maximum'] = maximum

            theta = g_theta[self._partition.allocated_node_offsets[self._id] + numericalgate].item()
            if 'theta' not in self.nodetype.gate_defaults[gate_type] or theta != self.nodetype.gate_defaults[gate_type]['theta']:
                gate_parameters['theta'] = theta

            if not len(gate_parameters) == 0:
                gatemap[gate_type] = gate_parameters

        return gatemap

    def take_slot_activation_snapshot(self):
        a_array = self._partition.a.get_value(borrow=True)
        self.slot_activation_snapshot.clear()
        for slottype in self.nodetype.slottypes:
            self.slot_activation_snapshot[slottype] =  \
                a_array[self._partition.allocated_node_offsets[self._id] + get_numerical_slot_type(slottype, self.nodetype)]

    def get_slot(self, type):
        if type not in self.__slotcache:
            self.__slotcache[type] = TheanoSlot(type, self, self._nodenet, self._partition)
        return self.__slotcache[type]

    def unlink_completely(self):
        self._partition.unlink_node_completely(self._id)
        if self.uid in self._nodenet.proxycache:
            del self._nodenet.proxycache[self.uid]

    def unlink(self, gate_name=None, target_node_uid=None, slot_name=None):
        for gate_name_candidate in self.nodetype.gatetypes:
            if gate_name is None or gate_name == gate_name_candidate:
                for link_candidate in self.get_gate(gate_name_candidate).get_links():
                    if target_node_uid is None or target_node_uid == link_candidate.target_node.uid:
                        if slot_name is None or slot_name == link_candidate.target_slot.type:
                            self._nodenet.delete_link(self.uid, gate_name_candidate, link_candidate.target_node.uid, link_candidate.target_slot.type)

    def get_associated_node_uids(self):
        numeric_ids_in_same_partition = self._partition.get_associated_node_ids(self._id)
        ids = [node_to_id(id, self._partition.pid) for id in numeric_ids_in_same_partition]

        # find this node in links coming in from other partitions to this node's partition
        for partition_from_spid, inlinks in self._partition.inlinks.items():
            for numeric_slot in range(0, get_slots_per_type(self._numerictype, self._nodenet.native_modules)):
                element = self._partition.allocated_node_offsets[self._id] + numeric_slot
                from_elements = inlinks[0].get_value(borrow=True)
                to_elements = inlinks[1].get_value(borrow=True)
                weights = inlinks[2].get_value(borrow=True)
                if element in to_elements:
                    from_partition = self._nodenet.partitions[partition_from_spid]
                    element_index = np.where(to_elements == element)[0][0]
                    slotrow = weights[element_index]
                    links_indices = np.nonzero(slotrow)[0]
                    for link_index in links_indices:
                        source_id = from_partition.allocated_elements_to_nodes[from_elements[link_index]]
                        ids.append(node_to_id(source_id, from_partition.pid))

        # find this node in links going out to other partitions
        for partition_to_spid, to_partition in self._nodenet.partitions.items():
            if self._partition.spid in to_partition.inlinks:
                for numeric_gate in range(0, get_gates_per_type(self._numerictype, self._nodenet.native_modules)):
                    element = self._partition.allocated_node_offsets[self._id] + numeric_gate
                    inlinks = to_partition.inlinks[self._partition.spid]
                    from_elements = inlinks[0].get_value(borrow=True)
                    to_elements = inlinks[1].get_value(borrow=True)
                    weights = inlinks[2].get_value(borrow=True)
                    if element in from_elements:
                        element_index = np.where(from_elements == element)[0][0]
                        gatecolumn = weights[:, element_index]
                        links_indices = np.nonzero(gatecolumn)[0]
                        for link_index in links_indices:
                            target_id = to_partition.allocated_elements_to_nodes[to_elements[link_index]]
                            ids.append(node_to_id(target_id, to_partition.pid))

        return ids

    def get_parameter(self, parameter):
        if self.type in self._nodenet.native_modules:
            return self.parameters.get(parameter, self.nodetype.parameter_defaults.get(parameter, None))
        else:
            return self.clone_parameters().get(parameter, None)

    def set_parameter(self, parameter, value):
        if value == '' or value is None:
            if parameter in self.nodetype.parameter_defaults:
                value = self.nodetype.parameter_defaults[parameter]
            else:
                value = None
        if self.type == "Sensor" and parameter == "datasource":
            if value is not None and value != "":
                datasources = self._nodenet.get_datasources()
                sensor_element = self._partition.allocated_node_offsets[self._id] + GEN
                old_datasource_index = np.where(self._partition.sensor_indices == sensor_element)[0]

                self._partition.sensor_indices[old_datasource_index] = 0
                if value not in datasources:
                    self.logger.warning("Datasource %s not known, will not be assigned." % value)
                    return

                datasource_index = datasources.index(value)

                if self._partition.sensor_indices[datasource_index] != sensor_element and \
                        self._partition.sensor_indices[datasource_index] > 0:

                    other_sensor_element = self._partition.sensor_indices[datasource_index]
                    other_sensor_id = node_to_id(self._partition.allocated_elements_to_nodes[other_sensor_element], self._partition.pid)

                    self.logger.warning("Datasource %s had already been assigned to sensor %s, which will now be unassigned." % (value, other_sensor_id))

                self._nodenet.sensormap[value] = self.uid
                self._partition.sensor_indices[datasource_index] = sensor_element
        elif self.type == "Actor" and parameter == "datatarget":
            if value is not None and value != "":
                datatargets = self._nodenet.get_datatargets()
                actuator_element = self._partition.allocated_node_offsets[self._id] + GEN
                old_datatarget_index = np.where(self._partition.actuator_indices == actuator_element)[0]
                self._partition.actuator_indices[old_datatarget_index] = 0
                if value not in datatargets:
                    self.logger.warning("Datatarget %s not known, will not be assigned." % value)
                    return

                datatarget_index = datatargets.index(value)

                if self._partition.actuator_indices[datatarget_index] != actuator_element and \
                        self._partition.actuator_indices[datatarget_index] > 0:

                    other_actuator_element = self._partition.actuator_indices[datatarget_index]
                    other_actuator_id = node_to_id(self._partition.allocated_elements_to_nodes[other_actuator_element], self._partition.pid)

                    self.logger.warning("Datatarget %s had already been assigned to actuator %s, which will now be unassigned." % (value, other_actuator_id))

                self._nodenet.actuatormap[value] = self.uid
                self._partition.actuator_indices[datatarget_index] = actuator_element
        elif self.type == "Activator" and parameter == "type":
            if value != "sampling":
                self._nodenet.set_nodespace_gatetype_activator(self.parent_nodespace, value, self.uid)
            else:
                self._nodenet.set_nodespace_sampling_activator(self.parent_nodespace, self.uid)
        elif self.type == "Pipe" and parameter == "expectation":
            g_expect_array = self._partition.g_expect.get_value(borrow=True)
            g_expect_array[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type("gen")] = float(value)
            g_expect_array[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type("sur")] = float(value)
            g_expect_array[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type("por")] = float(value)
            self._partition.g_expect.set_value(g_expect_array, borrow=True)
        elif self.type == "Pipe" and parameter == "wait":
            g_wait_array = self._partition.g_wait.get_value(borrow=True)
            g_wait_array[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type("sur")] = int(value)
            g_wait_array[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type("por")] = int(value)
            self._partition.g_wait.set_value(g_wait_array, borrow=True)
        elif self.type == "Comment" and parameter == "comment":
            self.parameters[parameter] = value
        elif self.type in self._nodenet.native_modules:
            self.parameters[parameter] = value

    def clear_parameter(self, parameter):
        if self.type in self._nodenet.native_modules and parameter in self.parameters:
            del self.parameters[parameter]

    def clone_parameters(self):
        parameters = {}
        if self.type == "Sensor":
            sensor_element = self._partition.allocated_node_offsets[self._id] + GEN
            datasource_index = np.where(self._partition.sensor_indices == sensor_element)[0]
            if len(datasource_index) == 0:
                parameters['datasource'] = None
            else:
                parameters['datasource'] = self._nodenet.get_datasources()[datasource_index[0]]
        elif self.type == "Actor":
            actuator_element = self._partition.allocated_node_offsets[self._id] + GEN
            datatarget_index = np.where(self._partition.actuator_indices == actuator_element)[0]
            if len(datatarget_index) == 0:
                parameters['datatarget'] = None
            else:
                parameters['datatarget'] = self._nodenet.get_datatargets()[datatarget_index[0]]
        elif self.type == "Activator":
            activator_type = None
            if self._id in self._partition.allocated_nodespaces_por_activators:
                activator_type = "por"
            elif self._id in self._partition.allocated_nodespaces_ret_activators:
                activator_type = "ret"
            elif self._id in self._partition.allocated_nodespaces_sub_activators:
                activator_type = "sub"
            elif self._id in self._partition.allocated_nodespaces_sur_activators:
                activator_type = "sur"
            elif self._id in self._partition.allocated_nodespaces_cat_activators:
                activator_type = "cat"
            elif self._id in self._partition.allocated_nodespaces_exp_activators:
                activator_type = "exp"
            elif self._id in self._partition.allocated_nodespaces_sampling_activators:
                activator_type = "sampling"
            parameters['type'] = activator_type
        elif self.type == "Pipe":
            g_expect_array = self._partition.g_expect.get_value(borrow=True)
            value = g_expect_array[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type("sur")].item()
            parameters['expectation'] = value
            g_wait_array = self._partition.g_wait.get_value(borrow=True)
            parameters['wait'] = g_wait_array[self._partition.allocated_node_offsets[self._id] + get_numerical_gate_type("sur")].item()
        elif self.type == "Comment":
            parameters['comment'] = self.parameters['comment']
        elif self.type in self._nodenet.native_modules:
            # handle the defined ones, the ones with defaults and value ranges
            for parameter in self.nodetype.parameters:
                value = None
                if parameter in self.parameters:
                    value = self.parameters[parameter]
                elif parameter in self.nodetype.parameter_defaults:
                    value = self.nodetype.parameter_defaults[parameter]
                parameters[parameter] = value
            # see if something else has been set and return, if so
            for parameter in self.parameters:
                if parameter not in parameters:
                    parameters[parameter] = self.parameters[parameter]

        return parameters

    def get_state(self, state):
        return self._state.get(state)

    def set_state(self, state, value):
        if isinstance(value, np.floating):
            value = float(value)
        self._state[state] = value

    def clone_state(self):
        if self._numerictype > MAX_STD_NODETYPE:
            return self._state.copy()
        else:
            return None

    def clone_sheaves(self):
        return {"default": dict(uid="default", name="default", activation=self.activation)}  # todo: implement sheaves

    def node_function(self):
        try:
            self.nodetype.nodefunction(netapi=self._nodenet.netapi, node=self, sheaf="default", **self.clone_parameters())
        except Exception:
            self._nodenet.is_active = False
            if self.nodetype is not None and self.nodetype.nodefunction is None:
                self.logger.warn("No nodefunction found for nodetype %s. Node function definition is: %s" % (self.nodetype.name, self.nodetype.nodefunction_definition))
            else:
                raise

class TheanoGate(Gate):
    """
        theano gate proxy clas
    """

    @property
    def type(self):
        return self.__type

    @property
    def node(self):
        return self.__node

    @property
    def empty(self):
        w_matrix = self.__partition.w.get_value(borrow=True)
        gatecolumn = w_matrix[:, self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype]
        return len(np.nonzero(gatecolumn)[0]) == 0

    @property
    def activation(self):
        return float(self.__partition.a.get_value(borrow=True)[self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype])

    @activation.setter
    def activation(self, value):
        a_array = self.__partition.a.get_value(borrow=True)
        a_array[self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype] = value
        self.__partition.a.set_value(a_array, borrow=True)

    @property
    def activations(self):
        return {'default': self.activation}  # todo: implement sheaves

    def __init__(self, type, node, nodenet, partition):
        self.__type = type
        self.__node = node
        self.__nodenet = nodenet
        self.__partition = partition
        self.__numerictype = get_numerical_gate_type(type, node.nodetype)
        self.__linkcache = None

    def get_links(self):
        if self.__linkcache is None:
            self.__linkcache = []
            w_matrix = self.__partition.w.get_value(borrow=True)
            gatecolumn = w_matrix[:, self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype]
            links_indices = np.nonzero(gatecolumn)[0]
            for index in links_indices:
                target_id = self.__partition.allocated_elements_to_nodes[index]
                target_type = self.__partition.allocated_nodes[target_id]
                target_nodetype = self.__nodenet.get_nodetype(get_string_node_type(target_type, self.__nodenet.native_modules))
                target_slot_numerical = index - self.__partition.allocated_node_offsets[target_id]
                target_slot_type = get_string_slot_type(target_slot_numerical, target_nodetype)
                link = TheanoLink(self.__nodenet, self.__node.uid, self.__type, node_to_id(target_id, self.__partition.pid), target_slot_type)
                self.__linkcache.append(link)

            element = self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype
            # does any of the inlinks in any partition orginate from me?
            for partition_to_spid, to_partition in self.__nodenet.partitions.items():
                if self.__partition.spid in to_partition.inlinks:
                    inlinks = to_partition.inlinks[self.__partition.spid]
                    from_elements = inlinks[0].get_value(borrow=True)
                    to_elements = inlinks[1].get_value(borrow=True)
                    weights = inlinks[2].get_value(borrow=True)
                    if element in from_elements:
                        element_index = np.where(from_elements == element)[0][0]
                        gatecolumn = weights[:, element_index]
                        links_indices = np.nonzero(gatecolumn)[0]
                        for link_index in links_indices:
                            target_id = to_partition.allocated_elements_to_nodes[to_elements[link_index]]
                            target_type = to_partition.allocated_nodes[target_id]
                            target_slot_numerical = to_elements[link_index] - to_partition.allocated_node_offsets[target_id]
                            target_nodetype = self.__nodenet.get_nodetype(get_string_node_type(target_type, self.__nodenet.native_modules))
                            target_slot_type = get_string_slot_type(target_slot_numerical, target_nodetype)
                            link = TheanoLink(self.__nodenet, self.__node.uid, self.__type, node_to_id(target_id, to_partition.pid), target_slot_type)
                            self.__linkcache.append(link)

        return self.__linkcache

    def invalidate_caches(self):
        self.__linkcache = None

    def get_parameter(self, parameter_name):
        gate_parameters = self.__node.nodetype.gate_defaults[self.type]
        gate_parameters.update(self.__node.clone_non_default_gate_parameters(self.type))
        return gate_parameters[parameter_name]

    def clone_sheaves(self):
        return {"default": dict(uid="default", name="default", activation=self.activation)}  # todo: implement sheaves

    def gate_function(self, input_activation, sheaf="default"):
        # in the theano implementation, this will only be called for native module gates, and simply write
        # the value back to the activation vector for the theano math to take over
        self.activation = input_activation

    def open_sheaf(self, input_activation, sheaf="default"):
        pass            # todo: implement sheaves

class TheanoSlot(Slot):
    """
        theano slot proxy class
    """

    @property
    def type(self):
        return self.__type

    @property
    def node(self):
        return self.__node

    @property
    def empty(self):
        w_matrix = self.__partition.w.get_value(borrow=True)
        slotrow = w_matrix[self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype]
        if self.__partition.sparse:
            return len(np.nonzero(slotrow)[1]) == 0
        else:
            return len(np.nonzero(slotrow)[0]) == 0

    @property
    def activation(self):
        return self.__node.slot_activation_snapshot[self.__type]

    @property
    def activations(self):
        return {
            "default": self.activation
        }

    def __init__(self, type, node, nodenet, partition):
        self.__type = type
        self.__node = node
        self.__nodenet = nodenet
        self.__partition = partition
        self.__numerictype = get_numerical_slot_type(type, node.nodetype)
        self.__linkcache = None

    def get_activation(self, sheaf="default"):
        return self.activation

    def get_links(self):
        if self.__linkcache is None:
            self.__linkcache = []
            w_matrix = self.__partition.w.get_value(borrow=True)
            slotrow = w_matrix[self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype]
            if self.__partition.sparse:
                links_indices = np.nonzero(slotrow)[1]
            else:
                links_indices = np.nonzero(slotrow)[0]
            for index in links_indices:
                source_id = self.__partition.allocated_elements_to_nodes[index]
                source_type = self.__partition.allocated_nodes[source_id]
                source_gate_numerical = index - self.__partition.allocated_node_offsets[source_id]
                source_nodetype = self.__nodenet.get_nodetype(get_string_node_type(source_type, self.__nodenet.native_modules))
                source_gate_type = get_string_gate_type(source_gate_numerical, source_nodetype)
                link = TheanoLink(self.__nodenet, node_to_id(source_id, self.__partition.pid), source_gate_type, self.__node.uid, self.__type)
                self.__linkcache.append(link)

            element = self.__partition.allocated_node_offsets[node_from_id(self.__node.uid)] + self.__numerictype
            for partition_from_spid, inlinks in self.__partition.inlinks.items():
                from_elements = inlinks[0].get_value(borrow=True)
                to_elements = inlinks[1].get_value(borrow=True)
                weights = inlinks[2].get_value(borrow=True)
                if element in to_elements:
                    from_partition = self.__nodenet.partitions[partition_from_spid]
                    element_index = np.where(to_elements == element)[0][0]
                    slotrow = weights[element_index]
                    links_indices = np.nonzero(slotrow)[0]
                    for link_index in links_indices:
                        source_id = from_partition.allocated_elements_to_nodes[from_elements[link_index]]
                        source_type = from_partition.allocated_nodes[source_id]
                        source_gate_numerical = from_elements[link_index] - from_partition.allocated_node_offsets[source_id]
                        source_nodetype = self.__nodenet.get_nodetype(get_string_node_type(source_type, self.__nodenet.native_modules))
                        source_gate_type = get_string_gate_type(source_gate_numerical, source_nodetype)
                        link = TheanoLink(self.__nodenet, node_to_id(source_id, from_partition.pid), source_gate_type, self.__node.uid, self.__type)
                        self.__linkcache.append(link)

        return self.__linkcache

    def invalidate_caches(self):
        self.__linkcache = None


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
SmartPark - Client Application
Short description...

Code by: Magnus Øye, Dated: 14.12-2018
Contact: magnus.oye@gmail.com
Website: https://github.com/magnusoy/
"""

# Importing packages
from keras.preprocessing import image
from keras.models import model_from_json
import numpy as np
import glob

def loadModel():
    """Load trained model."""
    json_file = open('../model/model.json', 'r')
    loaded_model_json = json_file.read()
    json_file.close()
    loaded_model = model_from_json(loaded_model_json)
    # load weights into new model
    loaded_model.load_weights("../model/model.h5")
    print("Loaded model from disk")
    return loaded_model

def getParkingStatus(model):
    """Predict if the parkingslot is occupied or empty."""
    predictions = []
    path = '../instance/images/'
    for i, filename in enumerate(glob.glob(path + '*.png')):
        test_image = image.load_img(filename, target_size=(64, 64))
        test_image = image.img_to_array(test_image)
        test_image = np.expand_dims(test_image, axis=0)
        result = model.predict(test_image)
        if result[0][0] == 1:
            prediction = True  # Occupied
        else:
            prediction = False  # Empty
        predictions.append(prediction)
    return predictions


from ctypes import c_float, cast, POINTER

import OpenGL.GL as gl
import numpy as np

import openvr
from openvr.gl_renderer import OpenVrFramebuffer as OpenVRFramebuffer
from openvr.gl_renderer import matrixForOpenVrMatrix as matrixForOpenVRMatrix
from openvr.tracked_devices_actor import TrackedDevicesActor

import gltfutils.gltfutils as gltfu

c_float_p = POINTER(c_float)

class OpenVRRenderer(object):
    def __init__(self, multisample=0, znear=0.1, zfar=1000, poll_tracked_device_frequency=None):
        self.vr_system = openvr.init(openvr.VRApplication_Scene)
        w, h = self.vr_system.getRecommendedRenderTargetSize()
        self.vr_framebuffers = (OpenVRFramebuffer(w, h, multisample=multisample),
                                OpenVRFramebuffer(w, h, multisample=multisample))
        self._multisample = multisample
        self.vr_compositor = openvr.VRCompositor()
        if self.vr_compositor is None:
            raise Exception('unable to create compositor')
        self.vr_framebuffers[0].init_gl()
        self.vr_framebuffers[1].init_gl()
        self._poses = (openvr.TrackedDevicePose_t * openvr.k_unMaxTrackedDeviceCount)()
        self.projection_matrices = (np.asarray(matrixForOpenVRMatrix(self.vr_system.getProjectionMatrix(openvr.Eye_Left,
                                                                                znear, zfar))),
                                    np.asarray(matrixForOpenVRMatrix(self.vr_system.getProjectionMatrix(openvr.Eye_Right,
                                                                                znear, zfar))))
        self.eye_transforms = (np.asarray(matrixForOpenVRMatrix(self.vr_system.getEyeToHeadTransform(openvr.Eye_Left)).I),
                               np.asarray(matrixForOpenVRMatrix(self.vr_system.getEyeToHeadTransform(openvr.Eye_Right)).I))
        self.view = np.eye(4, dtype=np.float32)
        self.view_matrices  = (np.empty((4,4), dtype=np.float32),
                               np.empty((4,4), dtype=np.float32))
        self.controllers = TrackedDevicesActor(self._poses)
        #self.controllers.show_controllers_only = False
        self.controllers.init_gl()
        self.vr_event = openvr.VREvent_t()
        self._poll_tracked_device_count()
        self._poll_tracked_device_frequency = poll_tracked_device_frequency
        self._frames_rendered = 0
        self._pulse_t0 = 0.0
    def render(self, gltf, nodes, window_size=(800, 600)):
        self.vr_compositor.waitGetPoses(self._poses, openvr.k_unMaxTrackedDeviceCount, None, 0)
        hmd_pose = self._poses[openvr.k_unTrackedDeviceIndex_Hmd]
        if not hmd_pose.bPoseIsValid:
            return
        hmd_34 = np.ctypeslib.as_array(cast(hmd_pose.mDeviceToAbsoluteTracking.m, c_float_p),
                                       shape=(3,4))
        self.view[:3,:] = hmd_34
        view = np.linalg.inv(self.view.T)
        poses = [hmd_34]
        for i in self._controller_indices:
            controller_pose = self._poses[i]
            if controller_pose.bPoseIsValid:
                pose_34 = np.ctypeslib.as_array(cast(controller_pose.mDeviceToAbsoluteTracking.m, c_float_p),
                                                shape=(3,4))
                poses.append(pose_34)
        view.dot(self.eye_transforms[0], out=self.view_matrices[0])
        view.dot(self.eye_transforms[1], out=self.view_matrices[1])
        for eye in (0, 1):
            gl.glViewport(0, 0, self.vr_framebuffers[eye].width, self.vr_framebuffers[eye].height)
            gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.vr_framebuffers[eye].fb)
            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
            gltfu.set_material_state.current_material = None
            gltfu.set_technique_state.current_technique = None
            for node in nodes:
                gltfu.draw_node(node, gltf,
                                projection_matrix=self.projection_matrices[eye],
                                view_matrix=self.view_matrices[eye])
            self.controllers.display_gl(self.view_matrices[eye], self.projection_matrices[eye])
        # self.vr_compositor.submit(openvr.Eye_Left, self.vr_framebuffers[0].texture)
        # self.vr_compositor.submit(openvr.Eye_Right, self.vr_framebuffers[1].texture)
        self.vr_framebuffers[0].submit(openvr.Eye_Left)
        self.vr_framebuffers[1].submit(openvr.Eye_Right)
        # mirror left eye framebuffer to screen:
        gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.vr_framebuffers[0].fb)
        gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, 0)
        gl.glBlitFramebuffer(0, 0, self.vr_framebuffers[0].width, self.vr_framebuffers[0].height,
                             0, 0, window_size[0], window_size[1],
                             gl.GL_COLOR_BUFFER_BIT, gl.GL_NEAREST)
        gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
        self._frames_rendered += 1
        if self._poll_tracked_device_frequency and self._frames_rendered % self._poll_tracked_device_frequency == 0:
            self._poll_tracked_device_count()
    def process_input(self, button_press_callbacks=None, t_pulse=0.0, strength=0.65*2750):
        for i in self._controller_indices:
            got_state, state = self.vr_system.getControllerState(i, 1)
            if got_state and state.rAxis[1].x > 0.05:
                #if self._t - self._pulse_t0 > t_pulse:
                self.vr_system.triggerHapticPulse(self._controller_indices[0], 0, int(strength * state.rAxis[1].x))
                #self._pulse_t0 = self._t
        if self.vr_system.pollNextEvent(self.vr_event):
            if button_press_callbacks and self.vr_event.eventType == openvr.VREvent_ButtonPress:
                button = self.vr_event.data.controller.button
                if button in button_press_callbacks:
                    button_press_callbacks[button]()
            elif self.vr_event.eventType == openvr.VREvent_ButtonUnpress:
                pass
    def shutdown(self):
        self.controllers.dispose_gl()
        openvr.shutdown()
    def _poll_tracked_device_count(self):
        self._controller_indices = []
        for i in range(openvr.k_unMaxTrackedDeviceCount):
            if self.vr_system.getTrackedDeviceClass(i) == openvr.TrackedDeviceClass_Controller:
                self._controller_indices.append(i)