import json
import os

import geopandas as gpd
import matplotlib.pyplot as plt
import pandas as pd

import osmnx as ox

# configure script
cities = ["olomouc", "belfast", "hong_kong"]
edge_buffer_dists = [10, 50]
indicators_filepath = "./indicators.csv"
figure_filepath = "./fig/street-comparison-{city}.png"

if not os.path.exists("./fig/"):
    os.makedirs("./fig/")

def load_data(osm_graphml_path, osm_buffer_gpkg_path, official_streets_gpkg_path):
    """
    Load the street network edges and study boundary.

    Parameters
    ----------
    osm_graphml_path : str
        path to the OSM graphml file
    osm_buffer_gpkg_path : str
        path to the buffered study area geopackage
    official_streets_gpkg_path : str
        path to the official streets shapefile

    Returns
    -------
    gdf_osm_streets_clipped, gdf_official_streets_clipped, study_area : tuple
        the osm streets (clipped to the study area), the official streets
        (clipped to the study area), and the study area polygon
    """

    # load the study area boundary as a shapely (multi)polygon
    gdf_study_area = gpd.read_file(osm_buffer_gpkg_path, layer="urban_study_region")
    study_area = gdf_study_area["geometry"].iloc[0]
    print(ox.ts(), "loaded study area boundary")

    # load the official streets shapefile
    gdf_official_streets = gpd.read_file(official_streets_gpkg_path)
    print(ox.ts(), "loaded official streets shapefile")

    # load the graph, make it undirected, then get edges GeoDataFrame
    gdf_osm_streets = ox.graph_to_gdfs(ox.get_undirected(ox.load_graphml(osm_graphml_path)), nodes=False)
    print(ox.ts(), "loaded osm edges and made undirected streets")

    # Project the data to a common crs
    crs = gdf_study_area.crs
    if gdf_osm_streets.crs != crs:
        gdf_osm_streets = gdf_osm_streets.to_crs(crs)
        print(ox.ts(), "projected osm streets")
    if gdf_official_streets.crs != crs:
        gdf_official_streets = gdf_official_streets.to_crs(crs)
        print(ox.ts(), "projected official streets")

    # spatially clip the streets to the study area boundary
    import warnings

    warnings.filterwarnings("ignore", "GeoSeries.notna", UserWarning)  # temp warning suppression
    gdf_osm_streets_clipped = gpd.clip(gdf_osm_streets, study_area)
    gdf_official_streets_clipped = gpd.clip(gdf_official_streets, study_area)
    print(ox.ts(), "clipped osm/official streets to study area boundary")

    # double-check everything has same CRS, then return
    assert gdf_osm_streets_clipped.crs == gdf_official_streets_clipped.crs == gdf_study_area.crs
    return gdf_osm_streets_clipped, gdf_official_streets_clipped, study_area

def plot_data(gdf_osm, gdf_official, study_area, filepath, figsize=(10, 10), bgcolor="#333333", projected=True):
    """
    Plot the OSM vs official streets and save to disk.

    Parameters
    ----------
    gdf_osm : geopandas.GeoDataFrame
        the osm streets
    gdf_official : geopandas.GeoDataFrame
        the official streets
    study_area : shapely.Polygon or shapely.MultiPolygon
        the study area boundary
    filepath : str
        path to save figure as file
    figsize : tuple
        size of plotting figure
    bgcolor : str
        background color of plot
    projected : bool
        True if gdfs are projected rather than lat-lng

    Returns
    -------
    fig, ax : tuple
    """

    fig, ax = plt.subplots(figsize=figsize, facecolor=bgcolor)
    ax.set_facecolor(bgcolor)

    # turn study_area polygon into gdf with correct CRS
    gdf_boundary = gpd.GeoDataFrame(geometry=[study_area], crs=gdf_osm.crs)

    # plot study area, then official streets, then osm streets as layers
    _ = gdf_boundary.plot(ax=ax, facecolor="k", label="Study Area")
    _ = gdf_official.plot(ax=ax, color="r", lw=1, label="Official Data")
    _ = gdf_osm.plot(ax=ax, color="y", lw=1, label="OSM Data")

    ax.axis("off")
    if projected:
        # only make x/y equal-aspect if data are projected
        ax.set_aspect("equal")

    # create legend
    ax.legend()

    # save to disk
    fig.savefig(filepath, dpi=300, bbox_inches="tight", facecolor=fig.get_facecolor())
    print(ox.ts(), f'figure saved to disk at "{filepath}"')

    plt.close()
    return fig, ax

def total_edge_length_count(gdf_streets):
    """
    Calculate the total length and count of streets in gdf.

    Parameters
    ----------
    gdf_streets : geopandas.GeoDataFrame
        the osm or official streets

    Returns
    -------
    streets_total_length, streets_count : tuple
    """
    streets_total_length = gdf_streets.length.sum()
    streets_count = len(gdf_streets)
    return streets_total_length, streets_count

def calculate_overlap(a, b, dist):
    """
    Calculate the % overlap of a and b's lines and buffered lines' areas
    given different buffering distances.

    Parameters
    ----------
    a : geopandas.GeoDataFrame
        the osm streets
    b : geopandas.GeoDataFrame
        the osm streets
    dist : int
        buffer distance in meters

    Returns
    -------
    a_area_pct, b_area_pct, a_length_pct, b_length_pct : tuple
    """

    # buffer each by the current distance
    a_buff = a.buffer(dist)
    b_buff = b.buffer(dist)

    # take the unary union of each's buffered geometry
    a_buff_unary = a_buff.unary_union
    b_buff_unary = b_buff.unary_union

    # find the portion of each's buffered geometry that intersects with the other's buffered geometry
    a_buff_overlap = a_buff_unary.intersection(b_buff_unary)
    b_buff_overlap = b_buff_unary.intersection(a_buff_unary)

    # what % of each's buffered area does that intersecting portion comprise?
    a_area_pct = a_buff_overlap.area / a_buff_unary.area
    b_area_pct = b_buff_overlap.area / b_buff_unary.area

    # take the unary union of each's original unbuffered lines
    a_unary = a.unary_union
    b_unary = b.unary_union

    # find each's lines that intersect the intersecting buffered portion
    a_overlap = a_unary.intersection(a_buff_overlap)
    b_overlap = b_unary.intersection(b_buff_overlap)

    # what % of each's lines length does that intersecting portion comprise?
    a_length_pct = a_overlap.length / a_unary.length
    b_length_pct = b_overlap.length / b_unary.length

    return a_area_pct, b_area_pct, a_length_pct, b_length_pct

# RUN THE SCRIPT
indicators = {}
for city in cities:

    print(ox.ts(), f"begin processing {city}")
    indicators[city] = {}

    # load this city's configs
    with open(f"../configuration/{city}.json") as f:
        config = json.load(f)

    # load street gdfs from osm graph and official shapefile, then clip to study area boundary polygon
    gdf_osm_streets, gdf_official_streets, study_area = load_data(
        config["osm_graphml_path"], config["osm_buffer_gpkg_path"], config["official_streets_gpkg_path"]
    )

    # plot map of study area + osm and official streets, save to disk
    fp = figure_filepath.format(city=city)
    fig, ax = plot_data(gdf_osm_streets, gdf_official_streets, study_area, fp)

    # calculate total street length and edge count in each dataset, then add to indicators
    osm_total_length, osm_edge_count = total_edge_length_count(gdf_osm_streets)
    official_total_length, official_edge_count = total_edge_length_count(gdf_official_streets)
    indicators[city]["osm_total_length"] = osm_total_length
    indicators[city]["osm_edge_count"] = osm_edge_count
    indicators[city]["official_total_length"] = official_total_length
    indicators[city]["official_edge_count"] = official_edge_count
    print(ox.ts(), "calculated edge lengths and counts")

    # calculate the % overlaps of areas and lengths between osm and official streets with different buffer distances
    for dist in edge_buffer_dists:
        osm_area_pct, official_area_pct, osm_length_pct, official_length_pct = calculate_overlap(
            gdf_osm_streets, gdf_official_streets, dist
        )
        indicators[city][f"osm_area_pct_{dist}"] = osm_area_pct
        indicators[city][f"official_area_pct_{dist}"] = official_area_pct
        indicators[city][f"osm_length_pct_{dist}"] = osm_length_pct
        indicators[city][f"official_length_pct_{dist}"] = official_length_pct
        print(ox.ts(), f"calculated area/length of overlaps for buffer {dist}")

# turn indicators into a dataframe and save to disk
df_ind = pd.DataFrame(indicators).T
df_ind.to_csv(indicators_filepath, index=True, encoding="utf-8")
print(ox.ts(), f'all done, saved indicators to disk at "{indicators_filepath}"')


import numpy as np
import os
import constants
from numpy.linalg import inv
from dataloader import indoor_scenes
import torch
from collections import OrderedDict, Counter
from tqdm import tqdm

def project_image_to_world(x, y, depth, cam2world, depth_intrinsic):
    I = torch.zeros(4, depth.shape[0]).type(torch.cuda.FloatTensor)
    I[0, :] = x * depth
    I[1, :] = y * depth
    I[2, :] = depth
    I[3, :] = 1.0
    world_coordinates = torch.mm(torch.from_numpy(cam2world).type(torch.cuda.FloatTensor), torch.mm(
        torch.from_numpy(inv(depth_intrinsic)).type(torch.cuda.FloatTensor), I))

    del I, x, y, depth
    torch.cuda.empty_cache()

    return world_coordinates

def project_images_to_world(depths, cam2worlds, depth_intrinsic, superpixels, frames):
    x = np.linspace(0, constants.DEPTH_WIDTH - 1, constants.DEPTH_WIDTH)
    y = np.linspace(0, constants.DEPTH_HEIGHT - 1, constants.DEPTH_HEIGHT)
    x_mesh, y_mesh = np.meshgrid(x, y)

    world_coordinates = torch.zeros(4, len(depths) * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT).type(torch.cuda.FloatTensor)
    frame_origins = torch.zeros(len(depths) * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT).type(torch.cuda.IntTensor)
    superpixel_origins = torch.zeros(len(depths) * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT).type(torch.cuda.IntTensor)

    for im_idx in range(len(depths)):
        world_coordinates[:, im_idx * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT: (im_idx + 1) * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT] = project_image_to_world(torch.from_numpy(x_mesh).type(torch.cuda.FloatTensor).flatten(),
                                                                                torch.from_numpy(y_mesh).type(torch.cuda.FloatTensor).flatten(), torch.from_numpy(depths[im_idx][:]).type(torch.cuda.FloatTensor).flatten(), cam2worlds[im_idx], depth_intrinsic)
        frame_origins[im_idx * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT: (im_idx + 1) * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT] = torch.ones(
            constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT).type(torch.cuda.IntTensor) * frames[im_idx]

        superpixel_origins[im_idx * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT: (im_idx + 1) * constants.DEPTH_WIDTH * constants.DEPTH_HEIGHT] = torch.from_numpy(superpixels[im_idx].astype(np.int).flatten()).type(torch.cuda.IntTensor)

    # visualize_point_cloud(world_coordinates)

    return world_coordinates, frame_origins, superpixel_origins

def project_world_to_image(depth, superpixel_map, cam2world, depth_intrinsic, world_coordinates, frame_origins, superpixel_origins):
    world_coordinates_copy = world_coordinates.transpose(0, 1)[:, :3]
    projected_points = torch.mm(torch.mm(torch.from_numpy(depth_intrinsic).type(torch.cuda.FloatTensor),
                                         torch.from_numpy(inv(cam2world)).type(torch.cuda.FloatTensor)), world_coordinates)
    projected_points = projected_points.transpose(0, 1)[:, :3]
    projected_points[:, 0] /= projected_points[:, 2]
    projected_points[:, 1] /= projected_points[:, 2]
    projected_points[:, 2] /= projected_points[:, 2]
    selection_mask = ~torch.isnan(projected_points[:, 2])

    projected_points = torch.round(projected_points[selection_mask])
    frame_origins = frame_origins[selection_mask]
    superpixel_origins = superpixel_origins[selection_mask]
    world_coordinates_copy = world_coordinates_copy[selection_mask]

    # remove out of frame bounds
    selection_mask = (projected_points[:, 0] >= 0) & (projected_points[:, 0] < constants.DEPTH_WIDTH) & (
        projected_points[:, 1] >= 0) & (projected_points[:, 1] < constants.DEPTH_HEIGHT)

    projected_points = projected_points[selection_mask][:, :2]
    frame_origins = frame_origins[selection_mask]
    superpixel_origins = superpixel_origins[selection_mask]
    world_coordinates_copy = world_coordinates_copy[selection_mask]

    depth = torch.from_numpy(depth).type(torch.cuda.FloatTensor)
    depth = depth[projected_points[:, 1].type(torch.cuda.LongTensor), projected_points[:, 0].type(torch.cuda.LongTensor)].flatten()
    backprojected_points = project_image_to_world(projected_points[:, 0], projected_points[
        :, 1], depth, cam2world, depth_intrinsic).transpose(0, 1)[:, :3]

    selection_mask = (torch.norm(world_coordinates_copy - backprojected_points, dim=1) < constants.WORLD_DISTANCE_THRESHOLD)

    projected_points = projected_points[selection_mask]

    if projected_points.shape[0] == 0:
        return None

    frame_origins = frame_origins[selection_mask]
    superpixel_origins = superpixel_origins[selection_mask]
    superpixel_targets = superpixel_map[projected_points[:, 1].type(torch.cuda.LongTensor).cpu().numpy(), projected_points[:, 0].type(torch.cuda.LongTensor).cpu().numpy()].flatten()
    t1, t2 = np.unique(superpixel_map, return_counts=True)
    target_superpixel_sizes = dict(zip(t1, t2))

    frame_spx = torch.zeros((frame_origins.shape[0], 3)).type(torch.cuda.IntTensor)
    frame_spx[:, 0] = torch.from_numpy(superpixel_targets.astype(np.int)).type(torch.cuda.IntTensor)
    frame_spx[:, 1] = frame_origins
    frame_spx[:, 2] = superpixel_origins

    uniques, counts = torch.unique(frame_spx, dim=0, return_counts=True)
    frame_spx_counts = {}
    for idx, u in enumerate(uniques.tolist()):
        frame_spx_counts[tuple(u)] = float(counts[idx].cpu().item())

    coverage_dict = {}
    for i in frame_spx_counts:
        coverage = frame_spx_counts[i] / target_superpixel_sizes[i[0]]
        coverage_dict[(i[0], i[1], i[2])] = coverage

    return coverage_dict  # , projected_points

def find_superpixel_coverage(dataset_name, lmdb_handle, superpixel_dir, base_size, images):
    dataset = indoor_scenes.IndoorScenesWithAllInfo(dataset_name, lmdb_handle, superpixel_dir, base_size, images)
    scene_id_to_index = dataset.scene_id_to_index

    image_paths = []

    for scene_id in tqdm(scene_id_to_index, desc='Scene[Coverage]'):
        all_frame_coverages = OrderedDict()
        depths = []
        poses = []
        superpixels = []
        intrinsic = None

        for frame_id in scene_id_to_index[scene_id]:
            sample = dataset[frame_id]
            depths.append(sample['depth'])
            poses.append(sample['pose'])
            superpixels.append(sample['superpixel'])
            intrinsic = sample['intrinsic']

        world_coordinates, frame_origins, superpixel_origins = project_images_to_world(depths, poses, intrinsic, superpixels, scene_id_to_index[scene_id])

        for frame_id in tqdm(scene_id_to_index[scene_id], desc='Scene[Project]'):
            sample = dataset[frame_id]
            frame_coverages = project_world_to_image(sample['depth'], sample['superpixel'], sample['pose'], sample['intrinsic'], world_coordinates, frame_origins, superpixel_origins)
            if not frame_coverages is None:
                all_frame_coverages[frame_id] = frame_coverages
                image_paths.append(images[frame_id])
        #from pprint import pprint
        #pprint(all_frame_coverages)
        np.save(os.path.join(constants.SSD_DATASET_ROOT, dataset_name, "raw", "selections", "coverage_"+superpixel_dir, f'{scene_id}.npy'), all_frame_coverages)

        del world_coordinates, frame_origins, superpixel_origins
        del depths, poses, superpixels, all_frame_coverages
        torch.cuda.empty_cache()

    with open(os.path.join(constants.SSD_DATASET_ROOT, dataset_name, "raw", "selections", "coverage_"+superpixel_dir, "coverage_paths.txt"), "w") as fptr:
        for p in image_paths:
            fptr.write(p.decode() + "\n")

def test_coverage_scannet_sample():
    import constants
    import os
    from dataloader import dataset_base
    from dataloader.indoor_scenes import IndoorScenes
    lmdb_handle = dataset_base.LMDBHandle(os.path.join(constants.HDD_DATASET_ROOT, "scannet-sample", "dataset.lmdb"), False)
    train_set = IndoorScenes('scannet-sample', lmdb_handle, (240, 320), 'train')
    find_superpixel_coverage('scannet-sample', lmdb_handle, (240, 320), train_set.image_path_subset)

if __name__=='__main__':
    test_coverage_scannet_sample()


import copy
import pickle
from modules.hpe.utils.misc import postprocess_yolo_output, homography, get_augmentations, is_within_fov, \
    reconstruct_absolute
import einops
import numpy as np
from utils.tensorrt_runner import Runner
from utils.params import MetrabsTRTConfig, RealSenseIntrinsics, MainConfig
from tqdm import tqdm
import cv2
from utils.matplotlib_visualizer import MPLPosePrinter

class HumanPoseEstimator:
    def __init__(self, model_config, cam_config, just_box=None):

        if just_box is None:
            self.just_box = model_config.just_box
        else:
            self.just_box = just_box

        self.yolo_thresh = model_config.yolo_thresh
        self.nms_thresh = model_config.nms_thresh
        self.num_aug = model_config.num_aug
        self.n_test = 1 if self.num_aug < 1 else self.num_aug

        # Intrinsics and K matrix of RealSense
        self.K = np.zeros((3, 3), np.float32)
        self.K[0][0] = cam_config.fx
        self.K[0][2] = cam_config.ppx
        self.K[1][1] = cam_config.fy
        self.K[1][2] = cam_config.ppy
        self.K[2][2] = 1

        # Load conversions
        self.skeleton = model_config.skeleton
        self.expand_joints = np.load(model_config.expand_joints_path)
        with open(model_config.skeleton_types_path, "rb") as input_file:
            self.skeleton_types = pickle.load(input_file)

        # Load modules
        self.yolo = Runner(model_config.yolo_engine_path)  # model_config.yolo_engine_path
        if not self.just_box:
            self.image_transformation = Runner(model_config.image_transformation_path)
            self.bbone = Runner(model_config.bbone_engine_path)
            self.heads = Runner(model_config.heads_engine_path)

    def estimate(self, frame):

        # Preprocess for yolo
        square_img = cv2.resize(frame, (256, 256), fx=1.0, fy=1.0, interpolation=cv2.INTER_AREA)
        yolo_in = copy.deepcopy(square_img)
        yolo_in = cv2.cvtColor(yolo_in, cv2.COLOR_BGR2RGB)
        yolo_in = np.transpose(yolo_in, (2, 0, 1)).astype(np.float32)
        yolo_in = np.expand_dims(yolo_in, axis=0)
        yolo_in = yolo_in / 255.0

        # Yolo
        outputs = self.yolo(yolo_in)
        boxes, confidences = outputs[0].reshape(1, 4032, 1, 4), outputs[1].reshape(1, 4032, 80)
        bboxes_batch = postprocess_yolo_output(boxes, confidences, self.yolo_thresh, self.nms_thresh)

        # Get only the bounding box with the human with highest probability
        box = bboxes_batch[0]  # Remove batch dimension
        humans = []
        for e in box:  # For each object in the image
            if e[5] == 0:  # If it is a human
                humans.append(e)
        if len(humans) > 0:
            humans.sort(key=lambda x: x[4], reverse=True)  # Sort with decreasing probability
            human = humans[0]
        else:
            return None

        # Preprocess for BackBone
        x1 = int(human[0] * frame.shape[1]) if int(human[0] * frame.shape[1]) > 0 else 0
        y1 = int(human[1] * frame.shape[0]) if int(human[1] * frame.shape[0]) > 0 else 0
        x2 = int(human[2] * frame.shape[1]) if int(human[2] * frame.shape[1]) > 0 else 0
        y2 = int(human[3] * frame.shape[0]) if int(human[3] * frame.shape[0]) > 0 else 0

        # If we are doing rgb inference, we need just the box
        if self.just_box:
            return {"bbox": (x1, y1, x2, y2)}

        new_K, homo_inv = homography(x1, x2, y1, y2, self.K, 256)

        # Test time augmentation (What is Gamma Decoding?)
        if self.num_aug > 0:
            aug_should_flip, aug_rotflipmat, aug_gammas, aug_scales = get_augmentations(self.num_aug)
            new_K = np.tile(new_K, (self.num_aug, 1, 1))
            for k in range(self.num_aug):
                new_K[k, :2, :2] *= aug_scales[k]
            homo_inv = aug_rotflipmat @ np.tile(homo_inv[0], (self.num_aug, 1, 1))

        # Apply homography
        H = self.K @ np.linalg.inv(new_K @ homo_inv)
        bbone_in = self.image_transformation(frame.astype(int), H.astype(np.float32))

        bbone_in = bbone_in[0].reshape(self.n_test, 256, 256, 3)  # [..., ::-1]
        bbone_in_ = (bbone_in / 255.0).astype(np.float32)

        # BackBone
        outputs = self.bbone(bbone_in_)

        # Heads
        logits = self.heads(outputs[0])

        # Get logits 3d  TODO DO THE SAME WITH 2D
        logits = logits[0].reshape(1, 8, 8, 288)
        _, logits2d, logits3d = np.split(logits, [0, 32], axis=3)
        current_format = 'b h w (d j)'
        logits3d = einops.rearrange(logits3d, f'{current_format} -> b h w d j', j=32)  # 5, 8, 8, 9, 32

        # 3D Softmax
        heatmap_axes = (2, 1, 3)
        max_along_axis = logits3d.max(axis=heatmap_axes, keepdims=True)
        exponential = np.exp(logits3d - max_along_axis)
        denominator = np.sum(exponential, axis=heatmap_axes, keepdims=True)
        res = exponential / denominator

        # 3D Decode Heatmap
        result = []
        for ax in heatmap_axes:
            other_heatmap_axes = tuple(other_ax for other_ax in heatmap_axes if other_ax != ax)
            summed_over_other_heatmap_axes = np.sum(res, axis=other_heatmap_axes, keepdims=True)
            coords = np.linspace(0.0, 1.0, res.shape[ax])
            decoded = np.tensordot(summed_over_other_heatmap_axes, coords, axes=[[ax], [0]])
            result.append(np.squeeze(np.expand_dims(decoded, ax), axis=heatmap_axes))
        pred3d = np.stack(result, axis=-1)

        # 2D Softmax
        heatmap_axes = (2, 1)
        max_along_axis = logits2d.max(axis=heatmap_axes, keepdims=True)
        exponential = np.exp(logits2d - max_along_axis)
        denominator = np.sum(exponential, axis=heatmap_axes, keepdims=True)
        res = exponential / denominator

        # Decode heatmap
        result = []
        for ax in heatmap_axes:
            other_heatmap_axes = tuple(other_ax for other_ax in heatmap_axes if other_ax != ax)
            summed_over_other_heatmap_axes = np.sum(res, axis=other_heatmap_axes, keepdims=True)
            coords = np.linspace(0.0, 1.0, res.shape[ax])
            decoded = np.tensordot(summed_over_other_heatmap_axes, coords, axes=[[ax], [0]])
            result.append(np.squeeze(np.expand_dims(decoded, ax), axis=heatmap_axes))
        pred2d = np.stack(result, axis=-1) * 255

        # Get absolute position (if desired)
        is_predicted_to_be_in_fov = is_within_fov(pred2d)

        # If less than 1/3 of the joints is visible, then the resulting pose will be weird
        if is_predicted_to_be_in_fov.sum() < is_predicted_to_be_in_fov.size/4:
            return None

        # Move the skeleton into estimated absolute position if necessary
        pred3d = reconstruct_absolute(pred2d, pred3d, new_K[None, ...], is_predicted_to_be_in_fov, weak_perspective=False)

        # Go back in original space (without augmentation and homography)
        pred3d = pred3d @ homo_inv

        # Get correct skeleton
        pred3d = (pred3d.swapaxes(1, 2) @ self.expand_joints).swapaxes(1, 2)
        if self.skeleton is not None:
            pred3d = pred3d[:, self.skeleton_types[self.skeleton]['indices']]
            edges = self.skeleton_types[self.skeleton]['edges']
        else:
            edges = None

        pred3d = pred3d[0]  # Remove batch dimension

        return {"pose": pred3d,
                "edges": edges,
                "bbox": (x1, x2, y1, y2)}

if __name__ == "__main__":
    import pycuda.autoinit  # IMPORTANT leave this here! It creates the context for CUDA
    args = MainConfig()
    vis = MPLPosePrinter()

    h = HumanPoseEstimator(MetrabsTRTConfig(), RealSenseIntrinsics())

    # from utils.input import RealSense
    # cap = RealSense(width=args.cam_width, height=args.cam_height)  # RealSense
    cap = cv2.VideoCapture(0)  # Webcam

    for _ in tqdm(range(10000)):
        ret, img = cap.read()
        r = h.estimate(img)

        if r is not None:

            p = r["pose"]
            e = r["edges"]
            b = r["bbox"]

            if p is not None:
                p = p - p[0]
                vis.clear()
                vis.print_pose(p, e)
                vis.sleep(0.001)

            if b is not None:
                x1_, x2_, y1_, y2_ = b
                xm = int((x1_ + x2_) / 2)
                ym = int((y1_ + y2_) / 2)
                l = max(xm - x1_, ym - y1_)
                img = img[(ym - l if ym - l > 0 else 0):(ym + l), (xm - l if xm - l > 0 else 0):(xm + l)]
                img = cv2.resize(img, (224, 224))

        cv2.imshow("", img)
        cv2.waitKey(1)


# -*- coding: utf-8 -*-
import json
import os
import more_itertools
import math
import argparse

from jinja2 import Environment, FileSystemLoader
from livereload import Server

def rebuild(json_file_path):
    with open(json_file_path, 'r', encoding='utf-8') as json_file:
        books = json.load(json_file)
    total_books = len(books)
    books_per_page = 20
    book_pages = list(more_itertools.chunked(books, books_per_page))
    total_pages = math.ceil(total_books / books_per_page)
    for page_num, page_books in enumerate(book_pages, start=1):
        env = Environment(loader=FileSystemLoader('.'))
        template = env.get_template('template.html')
        render_html = template.render(
            books=page_books,
            current_page=page_num,
            total_pages=total_pages,
        )
        page_filename = f'pages/index{page_num}.html'
        with open(page_filename, 'w', encoding='utf-8') as html_file:
            html_file.write(render_html)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--json',
        default='media/book_parse.json',
        help='Путь к файлу book_parse.json'
    )
    args = parser.parse_args()

    if not os.path.exists('pages'):
        os.makedirs('pages')

    rebuild(args.json)
    server = Server()
    server.watch('template.html', lambda: rebuild(args.json))
    server.serve(root='.')


from dataclasses import asdict, dataclass

from apibara import Info
from apibara.model import BlockHeader, StarkNetEvent

from dao import utils
from dao.indexer import storage
from dao.indexer.base_event import BaseEvent
from dao.indexer.deserializer import BlockNumber

@dataclass
class VoteSubmitted(BaseEvent):
    callerAddress: bytes
    proposalId: int
    vote: bool
    onBehalfAddress: bytes

    async def _handle(
        self, info: Info, block: BlockHeader, starknet_event: StarkNetEvent
    ):
        # TODO: store both callerAddress and onBehalfAddress, we'll need to know
        # who voted at some point
        if self.vote:
            update_proposal_vote = {"$push": {"yesVoters": self.onBehalfAddress}}
        else:
            update_proposal_vote = {"$push": {"noVoters": self.onBehalfAddress}}

        await storage.update_proposal(
            proposal_id=self.proposalId,
            update=update_proposal_vote,
            info=info,
        )

        if self.vote:
            update_member_vote = {"$push": {"yesVotes": self.proposalId}}
        else:
            update_member_vote = {"$push": {"noVotes": self.proposalId}}

        await storage.update_member(
            member_address=self.onBehalfAddress,
            update=update_member_vote,
            info=info,
        )

@dataclass
class MemberAdded(BaseEvent):
    memberAddress: bytes
    shares: int
    loot: int
    onboardedAt: BlockNumber

    async def _handle(
        self, info: Info, block: BlockHeader, starknet_event: StarkNetEvent
    ):
        member_dict = {
            **asdict(self),
            "jailedAt": None,
            "exitedAt": None,
        }
        await info.storage.insert_one("members", member_dict)

@dataclass
class MemberUpdated(BaseEvent):
    memberAddress: bytes
    delegateAddress: bytes
    shares: int
    loot: int
    jailed: bool
    lastProposalYesVote: int
    onboardedAt: BlockNumber

    async def _handle(
        self, info: Info, block: BlockHeader, starknet_event: StarkNetEvent
    ):
        update_member_dict = asdict(self)

        block_datetime = utils.get_block_datetime_utc(block)

        # TODO: keep history of jailed and exited
        update_member_dict["jailedAt"] = block_datetime if self.jailed else None
        update_member_dict["exitedAt"] = block_datetime if not self.shares else None

        return await storage.update_member(
            member_address=self.memberAddress,
            update={"$set": update_member_dict},
            info=info,
        )

@dataclass
class RoleGranted(BaseEvent):
    account: bytes
    role: str
    sender: bytes

    async def _handle(
        self, info: Info, block: BlockHeader, starknet_event: StarkNetEvent
    ):
        # TODO: add roles history
        return await storage.update_member(
            member_address=self.account,
            update={"$push": {"roles": self.role}},
            info=info,
        )

@dataclass
class RoleRevoked(BaseEvent):
    account: bytes
    role: str
    sender: bytes

    async def _handle(
        self, info: Info, block: BlockHeader, starknet_event: StarkNetEvent
    ):
        # TODO: add roles history
        return await storage.update_member(
            member_address=self.account,
            update={"$pull": {"roles": self.role}},
            info=info,
        )


from tests.integration.factories.objs import create_project, create_issue
from wt.ids import EntityId

ID1 = EntityId("AAA-1")
ID2 = EntityId("AAA-2")

def get_link_url(entity_id, other_entity_id):
    return "/links/{entity_id}/{other_entity_id}".format(
        entity_id=entity_id.full_id,
        other_entity_id=other_entity_id.full_id,
    )

def test_create_link(authorized_api_request, post_issue, put_project):
    project = create_project()
    put_project(project)
    issue1 = post_issue(project.project_id, create_issue())
    issue2 = post_issue(project.project_id, create_issue())

    response = authorized_api_request(
        "PUT",
        get_link_url(issue1.object_id, issue2.object_id)
    )
    assert response.status_code == 201

def test_create_invalid_link(authorized_api_request):
    response = authorized_api_request(
        "PUT",
        get_link_url(ID1, ID1)
    )
    assert response.status_code == 400

def test_delete_link(authorized_api_request, put_project, post_issue, put_link):
    project = create_project()
    put_project(project)
    issue1 = post_issue(project.project_id, create_issue())
    issue2 = post_issue(project.project_id, create_issue())
    put_link(issue1.object_id, issue2.object_id)

    response = authorized_api_request(
        "DELETE",
        get_link_url(issue1.object_id, issue2.object_id)
    )
    assert response.status_code == 200

def test_delete_missing_link(authorized_api_request):
    response = authorized_api_request(
        "DELETE",
        get_link_url(ID1, ID2)
    )
    assert response.status_code == 400


import os
os.chdir("data/custom_cough/")

import argparse

import pandas as pd

import torch.nn as nn
import torch
from torch.utils.data import Dataset, DataLoader, ConcatDataset

import warnings
warnings.filterwarnings("ignore")

import logging
import datetime
from datetime import datetime, timedelta

from typing import List
from utils.custom_topological_cnn import *
from utils.custom_dataloader import *
from utils.helper_functions import *

from sklearn.model_selection import KFold

import json
with open("config.json") as json_data_file:
    config = json.load(json_data_file)

LABELS = [config["labels"]["TRAIN_LABEL"], config["labels"]["TEST_LABEL"]]
PATHS = [config["paths"]["TRAIN_TOPO"],  config["paths"]["TEST_TOPO"]]

# trainload = CoughData(TRAIN_PATH, TRAIN_LBL)
# testload = CoughData(TEST_PATH, TEST_LBL)
# trainer = DataLoader(trainload, batch_size=BATCH_SIZE)
# tester = DataLoader(testload, batch_size=BATCH_SIZE)

class Gatherer:
    def __init__(self,
                 batch_size: int,
                 torch_dataloader: torch.utils.data.DataLoader = DataLoader,
                 dataset: torch.utils.data.Dataset = CoughData,
                 datapaths: List[str] = PATHS,
                 datalabels: List[str] = LABELS,
                 log_dir: str = "./logs/"):

        self.dtformat = datetime.now().strftime("%Y-%m-%d")
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)

        # Create log directory if it doesn't exist
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)

        report_name = log_dir + "logging_report_" + datetime.now().strftime("%Y-%m-%d_%H_%M_%S") + ".log"
        handler = logging.FileHandler(report_name)
        formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s")
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

        self.batch_size=BATCH_SIZE
        self.dataset = dataset
        self.torch_dataloader = torch_dataloader
        self.datapaths = datapaths
        self.datalabels = datalabels

    def initiatie_data(self):
        self.logger.info("Using batch size: {}".format(self.batch_size))
        self.logger.info("Instantiating training dataset")
        self.trainload = self.dataset(self.datapaths[0],
                                      self.datalabels[0])
        self.logger.info("Finished train. Instantiating testing dataset.")
        self.testload = self.dataset(self.datapaths[1],
                                      self.datalabels[1])

        self.logger.info("Training and testing loaders finished.\
                        Now instantiating training and testing dataloaders")

        self.trainer, self.tester = self.torch_dataloader(self.trainload, batch_size=self.batch_size, shuffle=True), self.torch_dataloader(self.testload, batch_size=self.batch_size, shuffle=False)

        self.logger.info("Finished dataloaders.")

        return self.trainer, self.tester

def reset_weights(m):
  '''
    Try resetting model weights to avoid
    weight leakage.
  '''
  for layer in m.children():
   if hasattr(layer, 'reset_parameters'):
    print(f'Reset trainable parameters of layer = {layer}')
    layer.reset_parameters()

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='---------------- Train a 2D CNN with topological signals with Takens embeddings input. Made for Imagimob case study by Jako Rostami.')
    parser.add_argument('-epochs', '--epochs', type=int, default=1, help='Number of epochs to train. Defaults to 1.')
    parser.add_argument('-batchsize', '--batchsize', type=int, default=16, help='Number of batches to pass. Defaults to 16.')
    parser.add_argument("-montecarlo", "--montecarlo", type=bool, default=False, help="Use Monte Carlo dropout or not. Defaults to False.")
    parser.add_argument("-mc_runs", "--mc_runs", type=int, default=5, help="Number of Monte Carlo runs. Defaults to 5.")
    parser.add_argument("-mc_dropout", "--mc_dropout", type=float, )
    args = parser.parse_args()

    EPOCHS = args.epochs
    BATCH_SIZE = args.batchsize

    # Instantiate the training and testing datafetching
    gg = Gatherer(batch_size=BATCH_SIZE)
    train_loader, test_loader = CoughData(PATHS[0], LABELS[0]), CoughData(PATHS[1], LABELS[1])

    loss_fn = nn.BCEWithLogitsLoss()

    # For fold results
    results = {}

    dataset = ConcatDataset([train_loader, test_loader])

    kfold = KFold(n_splits=5, shuffle=True)
    mc_dropout = True
    for fold, (train_ids, test_ids) in enumerate(kfold.split(dataset)):
        print(f"Fold: {fold}")
        print("---"*10)

        fold_str = "fold_" + str(fold)

        train_subsampler = torch.utils.data.SubsetRandomSampler(train_ids)
        test_subsampler = torch.utils.data.SubsetRandomSampler(test_ids)

        trainloader = DataLoader(dataset, batch_size=BATCH_SIZE, sampler=train_subsampler)
        testloader = DataLoader(dataset, batch_size=BATCH_SIZE, sampler=test_subsampler)

        # Instantiate the Conv model into GPU and initialize weights
        cnn = SingleCNN2DTopo(mc_dropout=mc_dropout).to("cuda")
        cnn.apply(reset_weights)
        initialize_weights(cnn)
        optim = torch.optim.Adam(cnn.parameters(), lr=0.001) # Classical optimizer

        for epoch in range(0, EPOCHS):

            print(f"Epoch {epoch+1}/{EPOCHS}")
            train_loss, train_acc, train_prec, train_rec, train_f1 = training_grounds(cnn, trainloader, loss_fn, optim)
            print(f"Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f}, Train Precision: {train_prec:.4f}, Train Recall: {train_rec:.4f}, Train F1: {train_f1:.4f}")
            results[epoch] = {
            "train_loss": train_loss,
            "train_acc": train_acc,
            "train_precision": train_prec,
            "train_recall": train_rec,
            "train_f1": train_f1
            }

        test_loss, test_acc, test_prec, test_rec, test_f1 = testing_grounds(cnn, testloader, loss_fn, mc_dropout=mc_dropout)
        print(f"Train loss: {train_loss:.4f} | Train acc: {train_acc:.4f} | Test loss: {test_loss:.4f} | Test acc: {test_acc:.4f} | Test precision {test_prec:.4f} | Test recall {test_rec:.4f} | Test F1: {test_f1:.4f}")

        results[fold] = {
            "train_loss": train_loss,
            "train_acc": train_acc,
            "train_precision": train_prec,
            "train_recall": train_rec,
            "train_f1": train_f1,
            "test_loss": test_loss,
            "test_acc": test_acc,
            "test_precision": test_prec,
            "test_recall": test_rec,
            "test_f1": test_f1
        }

    print("---"*10)

print("Training and testing finished. Proceeding to save model.")

date_tracker = datetime.today().strftime("%Y-%m-%d-%H_%M")
model_dir = "C:/Users/jako/data/custom_cough/models"
# model_name = "noisy_2DCNN_TOPO" + "_epochs=" + str(EPOCHS) + \
#             "_test-acc=" + str(round(results["test_acc"][-1], 2)) + \
#             "_batch=" + str(BATCH_SIZE) + \
#             "_" + date_tracker + ".pth"

# results_name = "noisy_2DCNN_TOPO" + "_epochs=" + str(EPOCHS) + \
#             "_test-acc=" + str(round(results["test_acc"][-1], 2)) + \
#             "_batch=" + str(BATCH_SIZE) + \
#             "_" + date_tracker + ".csv"

# pd.DataFrame(results).to_csv(os.path.join("C:/Users/jako/data/custom_cough/models", results_name), index=True) # Index will act as the number of epochs
pd.DataFrame(results).T.to_csv(os.path.join("C:/Users/jako/data/custom_cough/models", "testing_kfold.csv"))

# torch.save(cnn.state_dict(), os.path.join(model_dir, model_name))

print("Model saved as: {}".format("BAJS.pth"))
print("Session finished.")


import sympy
print(sympy.__file__)
from sympy import Abs
import numpy as np
import argparse

from examples.seismic import Model
from examples.seismic import TimeAxis
from examples.seismic import RickerSource
from examples.seismic import Receiver
from devito import Eq, solve
from devito import Operator
from examples.seismic import plot_shotrecord

parser = argparse.ArgumentParser()
parser.add_argument('--velocity_setup', action="store", dest="vsetup", type=str, default=None, required=True,
                    help=" expects a npz file with shape, spacing, origin and velocity model")
parser.add_argument('--src_loc', action="store", dest="src_loc", type=str, default=None, required=True)
parser.add_argument('--rec_loc_setup', action="store", dest="rec_loc_setup", type=str, default=None, required=True,
                    help=" expects a npz file with receiver locations n_point x 3 matrix")
parser.add_argument('--time', action="store", dest="time", type=float, default=1.0)

# Argument loading
res = parser.parse_args()
REC_LOC_SETUP = res.rec_loc_setup
rsetup = np.load(REC_LOC_SETUP)
REC_LOC = rsetup["rec_loc"]
SRC_LOC = res.src_loc
SRC_LOC_X, SRC_LOC_Y, SRC_LOC_Z = [float(a) for a in SRC_LOC.split(',')]
VSETUP = res.vsetup
setup = np.load(VSETUP)
shape = setup["shape"]
spacing = setup["spacing"]
origin = setup["origin"]
v = setup["velocity_model"]
TIME = res.time

model = Model(vp=v, origin=origin, shape=shape, spacing=spacing,
              space_order=2, nbl=10, bcs="damp")

#### SOURCE SETTING ####
t0 = 0.  # Simulation starts a t=0
tn = np.ceil(TIME * 1000)  # Simulation last 1 second (1000 ms)
dt = model.critical_dt  # Time step from model grid spacing
time_range = TimeAxis(start=t0, stop=tn, step=dt)
f0 = 0.010  # Source peak frequency is 10Hz (0.010 kHz)
src = RickerSource(name='src', grid=model.grid, f0=f0,
                   npoint=1, time_range=time_range)

src.coordinates.data[0, :] = np.array([SRC_LOC_X, SRC_LOC_Y, SRC_LOC_Z])
#src.show()

#### RECEIVER SETTING ####
rec = Receiver(name='rec', grid=model.grid, npoint=REC_LOC.shape[0], time_range=time_range)
rec.coordinates.data[:,:] = REC_LOC

# ACOUSTIC MODEL

from devito import TimeFunction
u = TimeFunction(name="u", grid=model.grid, time_order=2, space_order=2)
pde = model.m * u.dt2 - u.laplace + model.damp * u.dt

stencil = Eq(u.forward, solve(pde, u.forward))
src_term = src.inject(field=u.forward, expr=src * dt**2 / model.m)
rec_term = rec.interpolate(expr=u.forward)
op = Operator([stencil] + src_term + rec_term, subs=model.spacing_map)
op(time=time_range.num-1, dt=model.critical_dt)
plot_shotrecord(rec.data, model, t0, tn)


# -*- coding: utf-8 -*-
"""
Created on Tue Jan 26 14:32:59 2016

@author: Jee-Bee for jBae (c) 2016
"""

import sys
if sys.version_info.major <3:
    from __future__ import division
import numpy as np
from src.signal import chirp
from src import Transform, Window, DefaultFigures
import matplotlib.pyplot as plt
# from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
# from matplotlib.ticker import LinearLocator, FormatStrFormatter

"""transfer function test
Signal out is signal for speaker.
Signal in1 is recorded by mic.
Signal in2 is recorded + filter
transfer function is:
    in signal     in1    in2
H = ---------- --> --- or ---
    out signal     out    out
"""

# http://www.mathworks.com/matlabcentral/answers/33500-2nd-order-digital-butterworth-filter
# Filtering acc:
# [B, A] = sig.butter(2, ff[0] / fs, 'bandstop')
# in3 = np.transpose(sig.filtfilt(B, A, np.transpose(in3)))
# [B, A] = sig.butter(4, (ff[1] * 2)/fs, 'bandstop')
# in3 = np.transpose(sig.filtfilt(B, A, np.transpose(in3)))
# [B, A] = sig.butter(2, 19000 * 2 / fs, 'lowpass')
# in3 = np.transpose(sig.filtfilt(B, A, np.transpose(in3)))

#
#N = 64
#t = np.arange(0, 1, 1 / N)
#x = chirp(t, 1, 1, 8, phi=90)
#y = 2 * np.sin(2 * np.pi * 4 * t) + 0.707 * np.sin(2 * np.pi * 6 * t)
#
#X = fft(x)
#Y = fft(y)
#F = np.arange(-N/2+1, N/2+1)
#
#Xr = np.real(X)
#Xi = np.imag(X)
#Yr = np.real(Y)
#Yi = np.imag(Y)
#
## From here test\
## for now just real and imag or complex.
## next step also Magnitude and phase
#
## Most Needed Function is not finished...
#
## X_in = x, X_out = complex
#H0_1_val = Transform.Transfer(x, Y, N)
#
## X_in = tuple, X_out = complex
#H0_2_val = Transform.Transfer((Xr, Xi), Y, N)
#
## X_in = complex, X_out = tuple
#H0_3_val = Transform.Transfer(X, (Yr, Yi), N)
## Fix Odd symmetry check
#
#plt.plot(F,abs(X))
#
#plt.figure()
#plt.plot(t,x,t,y)
#plt.figure()
#plt.plot(F,abs(X))
#plt.plot(F,np.real(X),F,np.imag(X))
#plt.figure()
#plt.plot(F, H0_1_val, F, H0_2_val, F, H0_3_val)
#
#

#
# ----- Part 2 of test
#

# signal generation
# allready exist
N = 2 ** 16
fs = 2 ** 12
overlap = 50  # [%]
t = np.arange(0, N / fs, 1 / fs)
x = chirp(t, 20, N / fs - 1 / fs, 2000, phi=90)
x = np.append(x, np.zeros(fs))  # silece for tail of real live sound

y = sd.playrec(x, samplerate=fs, channels=1)
sd.wait()
y = np.reshape(y, len(y))
# averaging multiple signals
# pass  # for later on

# window + multi fft
Nw = fs
hannw = Window.Window(Nw)
(dummy, hann) = hannw.hanwind()

S_1 = np.sum(hann)
S_2 = np.sum(hann ** 2)
NENBW = Nw * S_2 / (S_1 ** 2)
ENBW = fs * S_2 / (S_1 ** 2)  # = NENBW * fs/N

print(NENBW, ENBW)

overlap = overlap/100 *Nw
idxl = 0
idxu = Nw
Y = []
while idxu <= len(y):
    (F, Yd) = Transform.FFT(y[idxl:idxu] * hann, fs)
    #np.concatenate([a[None,:],b[None,:]])
#    http://stackoverflow.com/questions/21887754/numpy-concatenate-two-arrays-vertically?rq=1
    if Y == []:
        Y = np.append(Y,  Yd)
    else:
        Y = np.vstack((Y, Yd))
    #(F, Y) = np.append(Y, Transform.FFT(y[idxl:idxu] * hann,fs), axis=1)
    idxl += (Nw - overlap)
    idxu += (Nw - overlap)
Y /= S_1
#Y = np.reshape(Y,(Nw,len(Y)/Nw))
twind = np.arange(0,16.5,0.5)
twind, F = np.meshgrid(twind, F)
if np.shape(F) != np.shape(Y):
    Y = Y.T

## Later on fix
## now not volt_rms so PS, PSD, LS and LSD are just V and not V_rms
## PS_rms = 2 * np.abs(y_m)/S1 equation 23
#PS_rms = 2 * np.abs(WIND_nf)/S_1
#
## PSD_rms = PS_rms/ENBW equation 24
#PSD_rms = 2 * np.abs(WIND_nf)**2/(fs*S_2)

# spectogram
#DefaultFigures.Default3D(Y, F, twind)

fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(twind, F, abs(Y), rstride=1, cstride=1, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False)
#ax.set_zlim(-1.01, 1.01)

#ax.zaxis.set_major_locator(LinearLocator(10))
#ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

fig.colorbar(surf, shrink=0.5, aspect=5)

plt.show()

#spectfig = DefaultFigures.default3D(twind, F, Y)
#spectfig.Spect()

#
# ---TEST-----------TEST------------TEST----------TEST----
#

#H4 = OUT / fftshift(IN1)
#plt.figure()
#plt.plot(t, ifft(H4))  # hold all;
#
#SIH3 = fftshift(ifft(H3))
#ISH3 = ifft(fftshift(H3))
#
#plt.figure()
## plt.plot(t, SIH3)# hold all;
#plt.plot(t, ISH3)


class GUIOutMessage:
    # a class for messages from the controller to the GUI

    def __init__(self, Target, Characteristic, Value):
        # the initializer for messages from a bot

        self.m_target = Target # the bot the raised the message
        self.m_characteristic = Characteristic # the characteristic that the message is based on
        self.m_value = Value # the value passed


# import the necessary packages
from PIL import Image
import os
from imutils import paths
import argparse
import cv2
from pathlib import Path
import numpy as np
import sys
from sightengine.client import SightengineClient

def get_image_sharpness2(path):
    im = Image.open(path).convert('L')  # to grayscale
    array = np.asarray(im, dtype=np.int32)

    gy, gx = np.gradient(array)
    gnorm = np.sqrt(gx ** 2 + gy ** 2)
    sharpness = np.average(gnorm)
    return sharpness

def get_image_dimensions(imagePath):

    # Inline import for PIL because it is not a common library
    with Image.open(imagePath) as img:
        # Calculate the width and hight of an image
        width, height = img.size
    # calculat ethe size in bytes
    size_bytes = os.path.getsize(imagePath)
    a =  str(height) + ", " + str(width) + ", " + str((size_bytes)/1000)
    return a
    # return dict(width=width, height=height, size_bytes=size_bytes)

for i in range(0,4290):

    path = r'C:\Users\training.CSSCORP\Desktop\Video\Vid1\1080\frame'+str(i)+'.png'
    print(path)

    # image = cv2.imread(path)
    # image = cv2.resize(image, (1600, 1600))
    # image = cv2.GaussianBlur(image, (3, 3), 0)
    # image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    fm = get_image_sharpness2(path)
    info = get_image_dimensions(path)
    # text = imagePath + " - " + str(fm)
    # print(imagePath + " -  Blurriness:" + str(fm) + " - " + info)

    f = open(r'C:\Users\training.CSSCORP\Desktop\Notes\Week 4\14.06.19\miniproj\sharp_1080_v1.csv','a')
    f.write("  \n" + path + ", " + info + ", " + str(fm))
    f.close()


import os
import time
import argparse
from tqdm import tqdm

# Command-line arguement parsing code.
parser = argparse.ArgumentParser()
parser.add_argument("--basepath", "-b", type=str, help="Enter the location of all the .tar files.")
parser.add_argument("--destination", "-d", type=str, help="Enter the destination to extract all files.")
args = parser.parse_args()

errorFiles = []

# Creating the destination folder if does not exist already.
if not os.path.exists(args.destination):
    os.makedirs(args.destination)

# Loop to go through every .tar file and extract it in the destination folder.
for file in tqdm(os.listdir(args.basepath)):
    if file.endswith('.tar') and 'mai' in file and 'pDSSAT' in file:
        try:
            #print("[INFO]. Extracting {}".format(os.path.join(args.basepath, file)))
            os.system("tar -xf {} --directory={}".format(os.path.join(args.basepath, file), args.destination))
        except:
            print("[ERROR]. Cannot extract {} file properly.".format(os.path.join(args.basepath, file)))
            errorFiles.append(os.path.join(args.basepath, file))
            print("[INFO]. Process will continue shortly.")
            time.sleep(5)

# Checking if encountered error in any .tar file.
if len(errorFiles) == 0:
    print("[FINISH]. Successfully extracted all the files.")
else:
    print("[INCOMPLETE][ERROR]. Some Files are not extracted properly.")
    print("[INCOMPLETE][ERROR]. List : ", errorFiles)


from turtle import Turtle, Screen
from random import randint

colors = ["red", "orange", "yellow", "green", "blue", "purple"]

screen = Screen()
screen.setup(width=500, height=400)
screen.title("Turtle Race")
user_bet = screen.textinput(title="Make your bet", prompt="Which turtle will win the race? Enter a color: ")

turtles = []

y_position = -100
for color in colors:
    new_turtle = Turtle(shape="turtle")
    new_turtle.penup()
    new_turtle.color(color)
    new_turtle.goto(x=-240, y=y_position)
    new_turtle.speed("slow")
    y_position += 40
    turtles.append(new_turtle)

winner = -1
is_race_on = True

while is_race_on:
    for index in range(0, 6):
        speed = randint(1, 10)
        turtles[index].fd(speed)

        pos = turtles[index].position()
        if pos[0] >= 230:
            winner = index
            is_race_on = False

if user_bet == colors[winner]:
    print("You win!!")
else:
    print("You loose!")

print(f"The winner is {colors[winner]}")

screen.exitonclick()


from loguru import logger
from LBFGS import FullBatchLBFGS
from torch.utils.data import TensorDataset, DataLoader
import itertools
import operator
from functools import reduce
import numpy as np
from collections import OrderedDict
from tqdm import tqdm
import torch
import gpytorch

class WeightClipper(object):

    def __init__(self, frequency=5):
        self.frequency = frequency

    def __call__(self, module):
        # filter the variables to get the ones you want
        if hasattr(module, 'weight'):
            w = module.weight.data
            w = w.clamp(-1, 1)

# We will use the simplest form of GP model, exact inference
class gp_torch(gpytorch.models.AbstractVariationalGP):
    # class gp_torch(gpytorch.models.ExactGP):

    def __init__(self, train_x, train_y, likelihood, verbose=False, kernel='Matern_2_5'):
        # super(gp_torch, self).__init__(train_x, train_y, likelihood)

        if len(train_y.shape) == 1:
            num_tasks = 1
        else:
            num_tasks = train_y.shape[1]

        if len(train_x.shape) == 1:
            num_dims = 1
        else:
            num_dims = train_x.shape[-1]

        grid_size = 32
        grid_bounds = [(-1, 1)] * num_dims
        grid_size = int(max(min(1E10**(1/num_dims), 32), 3))
        variational_distribution = gpytorch.variational.CholeskyVariationalDistribution(num_inducing_points=
                                                                                grid_size ** 2)
        variational_strategy = gpytorch.variational.GridInterpolationVariationalStrategy(self,
                                                                                grid_size=grid_size,
                                                                                grid_bounds=grid_bounds,
                                                                                variational_distribution=
                                                                                variational_distribution)
        # super(GPRegressionLayer, self).__init__(variational_strategy)
        super(gp_torch, self).__init__(variational_strategy)
        self.likelihood = likelihood

        if kernel == 'Matern_0_5':
            base_kernel = gpytorch.kernels.MaternKernel(nu=0.5)
        elif kernel == 'Matern_1_5':
            # base_kernel = gpytorch.kernels.MaternKernel(nu=1.5, ard_num_dims=num_dims)
            base_kernel = gpytorch.kernels.MaternKernel(nu=1.5)
        elif kernel == 'Matern_2_5':
            base_kernel = gpytorch.kernels.MaternKernel(nu=2.5)
        elif kernel == 'Linear':
            base_kernel = gpytorch.kernels.LinearKernel(num_dimensions=num_dims)
        elif kernel == 'RBF':
            base_kernel = gpytorch.kernels.RBFKernel()
        elif kernel == 'Cosine':
            base_kernel = gpytorch.kernels.CosineKernel()
        else:
            raise AssertionError('Unknown kernel type: ' + kernel)

        self.mean_module = gpytorch.means.ConstantMean()

        # grid_size = gpytorch.utils.grid.choose_grid_size(train_x)
        # self.covar_module = gpytorch.kernels.GridInterpolationKernel(
        #     gpytorch.kernels.MaternKernel(nu=1.5)
        #     , grid_size=max(grid_size,100), num_dims=train_x.shape[-1],
        # )

        grid_size = gpytorch.utils.grid.choose_grid_size(train_x)
        if grid_size <= 1:
            logger.warning(
                'Too less measurements for grid calculation: need at least: ' + str(2 ** num_dims) + 'but got: '
                + str(train_x.shape[-2]))

        self.covar_module = base_kernel
        # grid_size = gpytorch.utils.grid.choose_grid_size(train_x)
        # self.covar_module = gpytorch.kernels.GridInterpolationKernel(
        #     base_kernel
        #     , grid_size=grid_size, num_dims=num_dims,
        # )

        self.verbose = verbose

    def forward(self, x):
        mean_x = self.mean_module(x)
        covar_x = self.covar_module(x)
        return gpytorch.distributions.MultivariateNormal(mean_x, covar_x)

    def train_gp_model(self, train_x, train_y):
        # Use full-batch L-BFGS optimizer
        # optimizer = FullBatchLBFGS(self.parameters(), lr=1)
        train_dataset = TensorDataset(train_x, train_y)
        train_loader = DataLoader(train_dataset, batch_size=int(max(min(train_y.size(0) / 100, 1E4), 100)), shuffle=True)
        self.train()
        self.likelihood.train()

        # Use the adam optimizer
        optimizer = torch.optim.Adam([
            {'params': self.parameters()},  # Includes GaussianLikelihood parameters
        ], lr=0.1)

        # "Loss" for GPs - the marginal log likelihood
        mll = gpytorch.mlls.VariationalELBO(self.likelihood, self, num_data=train_y.size(0), combine_terms=False).cuda()
        scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[3, 5], gamma=0.1)

        # define closure

        training_iter = 20
        num_epochs = 2
        for i in range(num_epochs):
            scheduler.step()
            for minibatch_i, (x_batch, y_batch) in enumerate(train_loader):
                optimizer.zero_grad()
                with gpytorch.settings.use_toeplitz(False):
                    output = self(x_batch.double())
                    log_lik, kl_div, log_prior = mll(output, y_batch.double())
                    loss = -(log_lik - kl_div + log_prior).sum()

                # The actual optimization step
                loss.backward()
                optimizer.step()

                if self.verbose:
                    logger.info(
                        'Iter %d[%d/%d] - Loss: %.3f'
                        % (i + 1, minibatch_i, len(train_loader), loss.item())
                    )

    def train_gp_model_LBFGS_SGP(self, train_x, train_y):
        # Use full-batch L-BFGS optimizer
        # optimizer = FullBatchLBFGS(self.parameters(), lr=1)
        train_dataset = TensorDataset(train_x, train_y)
        train_loader = DataLoader(train_dataset, batch_size=int(max(min(train_y.size(0) / 100, 1E4), 100)), shuffle=True)
        self.train()
        self.likelihood.train()

        optimizer = FullBatchLBFGS(self.parameters(), lr=1E-1)
        # Access aprameters: self.likelihood.noise_covar.raw_noise
        # self.likelihood.noise_covar.initialize(raw_noise=0.)
        # self.mean_module.initialize(constant=0.)
        # para_to_check = self.covar_module.base_kernel
        # para_to_check.initialize(raw_lengthscale=0.)
        # self.covar_module.initialize(raw_lengthscale=0.)

        # "Loss" for GPs - the marginal log likelihood
        mll = gpytorch.mlls.VariationalELBO(
            self.likelihood, self, num_data=train_y.size(0), combine_terms=False).cuda()
        scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[3, 5], gamma=0.1)

        # define closure

        training_iter = 20
        num_epochs = 2
        for i in range(num_epochs):
            scheduler.step()
            for minibatch_i, (x_batch, y_batch) in enumerate(train_loader):
                # define closure
                def closure():
                    optimizer.zero_grad()
                    # output = self(train_x.double())
                    # loss = -mll(output, train_y.double()).sum()
                    with gpytorch.settings.use_toeplitz(False):
                        output = self(x_batch.double())
                        log_lik, kl_div, log_prior = mll(output, y_batch.double())
                        loss = -(log_lik - kl_div + log_prior).sum()

                    return loss

                loss = closure()
                loss.backward()
                # perform step and update curvature
                # optimizer.zero_grad()
                options = {'closure': closure, 'current_loss': loss, 'max_ls': 1, 'eta': 2}
                loss, g_new, lr, _, F_eval, G_eval, desc_dir, fail = optimizer.step(options)

                if self.verbose:
                    logger.info(
                        'Iter %d[%d/%d] - Loss: %.3f - LR: %.3f - Func Evals: %0.0f - Grad Evals: %0.0f - fail: %0.0f'
                        % (i + 1, minibatch_i, len(train_loader), loss.item(), lr, F_eval, G_eval, fail)
                    )
                    # logger.info(str(g_new))
                # if torch.isnan(para_to_check.raw_lengthscale.data):
                #     # logger.warning('NaN detected')
                #     # self.covar_module.initialize(raw_lengthscale=1E-6)
                #     para_to_check.initialize(raw_lengthscale=1E-6)

    def train_gp_model_LBFGS(self, train_x, train_y):
        # Use full-batch L-BFGS optimizer
        # optimizer = FullBatchLBFGS(self.parameters(), lr=1)
        optimizer = FullBatchLBFGS(self.parameters(), lr=1E-1)
        # Access aprameters: self.likelihood.noise_covar.raw_noise
        self.likelihood.noise_covar.initialize(raw_noise=0.)
        self.mean_module.initialize(constant=0.)
        para_to_check = self.covar_module.base_kernel
        para_to_check.initialize(raw_lengthscale=0.)
        # self.covar_module.initialize(raw_lengthscale=0.)

        # "Loss" for GPs - the marginal log likelihood
        mll = gpytorch.mlls.ExactMarginalLogLikelihood(self.likelihood, self)

        # define closure
        # define closure
        def closure():
            optimizer.zero_grad()
            output = self(train_x.double())
            loss = -mll(output, train_y.double()).sum()
            return loss

        loss = closure()
        loss.backward()

        training_iter = 20
        for i in range(training_iter):

            # perform step and update curvature
            # optimizer.zero_grad()
            options = {'closure': closure, 'current_loss': loss, 'max_ls': 20, 'eta': 2}
            loss, g_new, lr, _, F_eval, G_eval, desc_dir, fail = optimizer.step(options)
            if self.verbose:
                logger.info(
                    'Iter %d/%d - Loss: %.3f - LR: %.3f - Func Evals: %0.0f - Grad Evals: %0.0f - fail: %0.0f' % (
                        i + 1, training_iter, loss.item(), lr, F_eval, G_eval, fail
                    ))
                # logger.info(str(g_new))
            if torch.isnan(para_to_check.raw_lengthscale.data):
                # logger.warning('NaN detected')
                # self.covar_module.initialize(raw_lengthscale=1E-6)
                para_to_check.initialize(raw_lengthscale=1E-6)

    def train_gp_model_2(self, train_x, train_y):
        # Find optimal model hyperparameters
        self.train()
        self.likelihood.train()

        # Use the adam optimizer
        optimizer = torch.optim.Adam([
            {'params': self.parameters()},  # Includes GaussianLikelihood parameters
        ], lr=0.0001)

        # "Loss" for GPs - the marginal log likelihood
        mll = gpytorch.mlls.ExactMarginalLogLikelihood(self.likelihood, self)

        n_iter = 50
        for i in range(n_iter):
            # optimizer.zero_grad()
            output = self(train_x.double())
            # loss = -mll(output, train_y)
            loss = -mll(output, train_y).sum()
            loss.backward()
            print('Iter %d/%d - Loss: %.3f' % (i + 1, n_iter, loss.item()))
            optimizer.step()

    def train_gp_model_test(self, train_x, train_y):
        # Switch to train mode
        self.train()
        self.likelihood.train()

        # random.seed(opt.manualSeed)
        # torch.manual_seed(opt.manualSeed)
        # if torch.cuda.is_available():
        #     torch.cuda.manual_seed_all(opt.manualSeed)

        # Use the adam optimizer
        optimizer = torch.optim.Adam([
            {'params': self.parameters()},  # Includes GaussianLikelihood parameters
        ], lr=0.1)
        # optimizer = torch.optim.SGD(self.parameters(), lr=0.1)

        # "Loss" for GPs - the marginal log likelihood
        mll = gpytorch.mlls.ExactMarginalLogLikelihood(self.likelihood, self)

        n_trials = 50
        for i_trial in tqdm(range(n_trials)):
            new_state_dict = OrderedDict({'covar_module.task_covar_module.raw_var':
                                              (torch.rand((1, len(self.train_targets[0]))) - 0.5) * 2
                                          })
            self.load_state_dict(new_state_dict, strict=False)

            training_iterations = 10
            for i in range(training_iterations):
                # print(list(self.named_parameters()))
                optimizer.zero_grad()
                output = self(train_x)
                # loss = -mll(output, train_y)
                loss = -mll(output, train_y).sum()

                c_min = []
                c_max = []
                for i_para in list(self.parameters()):
                    i_test = i_para.data.cpu().detach().numpy()[0]
                    if type(i_test) == np.ndarray:
                        if type(i_test[0]) == np.ndarray:
                            i_test = i_test[0]
                        c_min.append(min(i_test))
                        c_max.append(max(i_test))
                    else:
                        c_min.append(i_test)
                        c_max.append(i_test)

                check_constants = np.array(c_min + c_max)

                check_constrain_mean = len([value_para for value_para in check_constants if abs(value_para) > 1]) != 0
                if check_constrain_mean:
                    loss = loss / (10 * check_constants.max())
                # loss = (output - train_y) ** 2
                loss.backward()
                # if self.verbose:
                #     logger.info('Iter %d/%d - Loss: %.3f' % (i + 1, training_iterations, loss.item()))
                # print(list(self.parameters()))
                optimizer.step()

            if i_trial == 0:
                para = self.state_dict()
                current_best_loss = loss.item()
            else:
                if current_best_loss > loss.item():
                    para = self.state_dict()
                    current_best_loss = loss.item()
            if self.verbose:
                logger.info('MLL after optimization: %.3f, best so far: %.3f' % (loss.item(), current_best_loss))

        logger.info('MLL after optimization: %.3f, best so far: %.3f' % (loss.item(), current_best_loss))
        self.load_state_dict(para, strict=False)

        # def train():
        #     for i in range(training_iterations):
        #         # Zero backprop gradients
        #         optimizer.zero_grad()
        #         # Get output from model
        #         output = self(train_x)
        #         # Calc loss and backprop derivatives
        #         loss = -mll(output, train_y)
        #         loss.backward()
        #         if self.verbose:
        #             logger.info('Iter %d/%d - Loss: %.3f' % (i + 1, training_iterations, loss.item()))
        #         optimizer.step()
        #         torch.cuda.empty_cache()

        # # See dkl_mnist.ipynb for explanation of this flag
        # with gpytorch.settings.use_toeplitz(True):
        #     train()

        # training_iter = 50
        # for i in tqdm(range(training_iter)):
        #     # Zero gradients from previous iteration
        #     optimizer.zero_grad()
        #     # Output from model
        #     output = self(train_x)
        #     # Calc loss and backprop gradients
        #     loss = -mll(output, train_y)
        #     loss.backward()
        #     if self.verbose:
        #         logger.info('Iter %d/%d - Loss: %.3f' % (
        #             i + 1, training_iter, loss.item(),
        #         ))
        #     optimizer.step()

    def make_prediction(self, test_x):
        # Get into evaluation (predictive posterior) mode
        self.eval()
        self.likelihood.eval()

        # Test points are regularly spaced along [0,1]
        # Make predictions by feeding model through likelihood
        with torch.no_grad(), gpytorch.settings.fast_pred_var():
            observed_pred = self.likelihood(self(test_x))

        mean_array = observed_pred.mean.detach().cpu().numpy().transpose()
        std_array = observed_pred.stddev.detach().cpu().numpy().transpose()

        # with gpytorch.settings.max_preconditioner_size(10), torch.no_grad():
        #     with gpytorch.settings.use_toeplitz(False), gpytorch.settings.max_root_decomposition_size(30), \
        #          gpytorch.settings.fast_pred_var():
        #         observed_pred = self(test_x)
        #         mean_array = observed_pred.mean.numpy()
        #         std_array = observed_pred.stddev.numpy()

        return mean_array, std_array


from search.algorithms import State
from search.map import Map
import getopt
import sys
import heapq

def dijkstra(start, goal, gridded_map):
    # open list is heap
    open_list = []
    node_count = 0

    # closed list is hashmap or dict in python
    closed_list = dict()
    heapq.heappush(open_list, start)
    closed_list[start.state_hash()] = start
    while open_list:
        n = heapq.heappop(open_list)
        node_count+=1
        if n == goal:
            return n.get_g(), node_count
        for n_prime in gridded_map.successors(n):
            if n_prime.state_hash() not in closed_list:
                heapq.heappush(open_list, n_prime)
                closed_list[n_prime.state_hash()] = n_prime
            if n_prime.state_hash() in closed_list and n_prime.get_g() < closed_list[n_prime.state_hash()].get_g():
                heapq.heappush(open_list, n_prime)
                closed_list[n_prime.state_hash()] = n_prime
    return -1, node_count

def bi_bs(start, goal, gridded_map):
    open_forward = []
    open_back = []
    closed_forward = dict()
    closed_back = dict()
    u = float("inf")
    node_count = 0

    heapq.heappush(open_forward, start)
    heapq.heappush(open_back, goal)

    closed_forward[start.state_hash()] = start
    closed_back[goal.state_hash()] = goal

    while open_forward and open_back:
        if u < open_forward[0].get_g() + open_back[0].get_g():
            return u, node_count
        if open_forward[0].get_g() < open_back[0].get_g():
            n = heapq.heappop(open_forward)
            node_count+=1
            for n_prime in gridded_map.successors(n):
                if n_prime.state_hash() in closed_back:
                    u = min(u, n_prime.get_g() + closed_back[n_prime.state_hash()].get_g())
                if n_prime.state_hash() not in closed_forward:
                    heapq.heappush(open_forward, n_prime)
                    closed_forward[n_prime.state_hash()] = n_prime
                if n_prime.state_hash() in closed_forward and n_prime.get_g() < closed_forward[n_prime.state_hash()].get_g():
                    heapq.heappush(open_forward, n_prime)
                    closed_forward[n_prime.state_hash()] = n_prime
        else:
            n = heapq.heappop(open_back)
            node_count+=1
            for n_prime in gridded_map.successors(n):
                if n_prime.state_hash() in closed_forward:
                    u = min(u, n_prime.get_g() + closed_forward[n_prime.state_hash()].get_g())
                if n_prime.state_hash() not in closed_back:
                    heapq.heappush(open_back, n_prime)
                    closed_back[n_prime.state_hash()] = n_prime
                if n_prime.state_hash() in closed_back and n_prime.get_g() < closed_back[n_prime.state_hash()].get_g():
                    heapq.heappush(open_back, n_prime)
                    closed_back[n_prime.state_hash()] = n_prime
    return -1, node_count

def main():
    """
    Function for testing your A* and Dijkstra's implementation. There is no need to edit this file.
    Run it with a -help option to see the options available.
    """
    optlist, _ = getopt.getopt(sys.argv[1:], 'h:m:r:', ['testinstances', 'plots', 'help'])

    plots = False
    for o, a in optlist:
        if o in ("-help"):
            print("Examples of Usage:")
            print("Solve set of test instances: main.py --testinstances")
            print("Solve set of test instances and generate plots: main.py --testinstances --plots")
            exit()
        elif o in ("--plots"):
            plots = True
        elif o in ("--testinstances"):
            test_instances = "test-instances/testinstances.txt"

    gridded_map = Map("dao-map/brc000d.map")

    nodes_expanded_dijkstra = []
    nodes_expanded_bibs = []

    start_states = []
    goal_states = []
    solution_costs = []

    file = open(test_instances, "r")
    for instance_string in file:
        list_instance = instance_string.split(",")
        start_states.append(State(int(list_instance[0]), int(list_instance[1])))
        goal_states.append(State(int(list_instance[2]), int(list_instance[3])))

        solution_costs.append(float(list_instance[4]))
    file.close()

    for i in range(0, len(start_states)):
        start = start_states[i]
        goal = goal_states[i]

        cost, expanded_diskstra = dijkstra(start, goal, gridded_map) # Implement here the call to your Dijkstra's implementation for start, goal, and gridded_map

        nodes_expanded_dijkstra.append(expanded_diskstra)

        if cost != solution_costs[i]:
            print("There is a mismatch in the solution cost found by Dijkstra and what was expected for the problem:")
            print("Start state: ", start)
            print("Goal state: ", goal)
            print("Solution cost encountered: ", cost)
            print("Solution cost expected: ", solution_costs[i])
            print()

        cost, expanded_astar = bi_bs(start, goal, gridded_map) # Implement here the call to your Bi-BS's implementation for start, goal, and gridded_map
        nodes_expanded_bibs.append(expanded_astar)

        if cost != solution_costs[i]:
            print("There is a mismatch in the solution cost found by Bi-HS and what was expected for the problem:")
            print("Start state: ", start)
            print("Goal state: ", goal)
            print("Solution cost encountered: ", cost)
            print("Solution cost expected: ", solution_costs[i])
            print()

    if plots:
        from search.plot_results import PlotResults
        plotter = PlotResults()
        plotter.plot_results(nodes_expanded_bibs, nodes_expanded_dijkstra, "Nodes Expanded (Bi-HS)", "Nodes Expanded (Dijkstra)", "nodes_expanded")

    print('Finished running all experiments.')

if __name__ == "__main__":
    main()


import os
import sys
import pygame as pg

from . import tools

SCREEN_SIZE = (1000, 600)
ORIGINAL_CAPTION = 'Puzzle'

pg.mixer.pre_init(44100, -16, 1, 512)
pg.init()
os.environ['SDL_VIDEO_CENTERED'] = 'TRUE'
SCREEN = pg.display.set_mode(SCREEN_SIZE)
SCREEN_RECT = SCREEN.get_rect()

PICS_AMOUNT = 10
DIFFICULTY_SIZE = (9, 16, 25)
TURN_TIME = [(5000, 10000), (4000, 8000), (3000, 6000)]
TRANSITION_TIME = 700

def graphics_from_directories(directories):
    """
    Calls the tools.load_all_graphics() function for all directories passed.
    """
    base_path = os.path.join("resources", "graphics")
    GFX = {}
    for directory in directories:
        if getattr(sys, 'frozen', False):
            path = os.path.join(os.path.dirname(sys.executable), 'graphics',
                                directory)
        else:
            path = os.path.join(base_path, directory)
        GFX[directory] = tools.load_all_gfx(path)
    return GFX

_SUB_DIRECTORIES = ['gui']
GFX = graphics_from_directories(_SUB_DIRECTORIES)

fonts_path = os.path.join('resources', 'fonts')
FONTS = tools.load_all_fonts(fonts_path)

sfx_path = os.path.join('resources', 'sounds')
SFX = tools.load_all_sfx(sfx_path)

music_path = os.path.join('resources', 'music')
MUSIC = tools.load_all_music(music_path)

tools.music_station.create_playlist(MUSIC.values())

def transparent_surface(width, height, alpha=0):
    surface = pg.Surface((width, height)).convert()
    surface.set_alpha(alpha)
    return surface.convert_alpha()

def make_transition(state, name, alpha_transition=False, duration=None):
    image = pg.Surface(SCREEN_RECT.size).convert()
    state.draw(image)
    state.persist['next_state'] = name
    state.persist['next_image'] = image
    if alpha_transition:
        state.persist['alpha_transition'] = True
    if duration:
        state.persist['duration'] = duration
    state.next = 'TRANSITION'
    state.done = True


import random

movements = ["loaded carry", "squat", "hinge", "superior pull", \
             "horizontal pull", "inferior pull", "superior push", \
             "horizontal push", "inferior push", "gait", "jump", "crawl", \
             "rotation", "trunk extension", "trunk flexion", \
             "lift from ground", "throwing"]

##while True:
##    session = input("Choose: /n 1. 2. 3. c. r.:")
##    if session == "1":
##        print(random.choice(movements))
##    if session == "2":
##        print(random.choices(movements, k=2))
##    if session == "3":
##        print(random.choices(movements, k=3))
##    if session == "c":
##        print(random.choices(movements, k=random.randint(4, 10)))
##    if session == "r":
##        print(random.choices(movements, k=random.randint(1, 10)))

# trying above, but without repeating elements during single use
while True:
    session = input("Choose: /n 1. 2. 3. c. r.:")
    if session == "1":
        print(random.choice(movements))
    if session == "2":
        print(random.sample(movements, 2))
    if session == "3":
        print(random.sample(movements, 3))
    if session == "c":
        print(random.sample(movements, k=random.randint(4, 10)))
    if session == "r":
        print(random.sample(movements, k=random.randint(1, 10)))


import pandas as pd
import os
import datetime
from mailcleaner import MailCleaner
from fileordering import *

### Driver code ###
if __name__ == "__main__":
    #Link to directory of files to clean
    file_path =  input("What's the absolute file path where you are storing the files?: ")
    directory = r'{}'.format(file_path)

    #convert xls files to csv
    print('Converting excel files to csv...')
    for file_name in os.listdir(directory):
        print(file_name)
        if file_name[-3:] != 'xls':
            continue

        convert_to_csv(file_name)

    #Create empty dataframes
    manhattan_df = pd.DataFrame(columns = ['Address 2', 'Name', 'Address', 'City',
    'State', 'Zip Code', 'Building Year'])

    brooklyn_df = pd.DataFrame(columns = ['Address 2', 'Name', 'Address', 'City',
    'State', 'Zip Code', 'Building Year'])

    unknown_df = pd.DataFrame(columns = ['Address 2', 'Name', 'Address', 'City',
    'State', 'Zip Code', 'Building Year'])

    #Loop over the directory and apply a cleaner to each
    for file_name in os.listdir(directory):
        #Skip the non-csv version of the file
        if file_name[-3:] != 'csv':
            continue

        #Read in the dataframe and the cleaner file
        df = pd.read_csv(file_path + '/' + file_name)
        print('\n')
        print('original data for ' + file_name[28:-4] + ' has ' + str(df.shape[0]) + ' rows')

        #Create cleaner
        cleaner = MailCleaner()

        #Clean data
        df = cleaner.filter_names(df)
        df = cleaner.format_names(df)
        df = cleaner.remove_duplicates(df)
        df = cleaner.remove_special_units(df)
        df = cleaner.filter_multi_units(df)
        df = cleaner.delete_cols(df)
        df = cleaner.delete_prev_unit(df=df, file_name=file_name)
        df = cleaner.populate_address(df, file_name)
        df = cleaner.populate_year(df=df, file_name=file_name)

        #Rename columns to match sheets
        df.rename(columns={"Unit/Tax lot": "Address 2", "Owner": "Name"}, inplace=True)

        print('cleaned data for ' + file_name[28:-4] + ' has ' + str(df.shape[0]) + ' rows')

        print('appending data ...')
        #Switch over the address to find proper df (Brooklyn, Manhattan or Unkonwn')
        if 'Brooklyn' in df['City'].values.tolist():
            brooklyn_df = brooklyn_df.append(df)
        elif 'New York' in df['City'].values.tolist():
            manhattan_df = manhattan_df.append(df)
        else:
            unknown_df = unknown_df.append(df)

        print('---------------------------------------------')

    #Get time to create a unique file each time
    time = str(datetime.datetime.now())[0:19].replace(':', '-')

    #Print and Download data based on time stamp
    #Manhattan
    print('Manhattan Data: ')
    print(manhattan_df)
    manhattan_df.to_csv('manhattan_data - ' + time + '.csv')
    print('\n')

    #Brooklyn
    print('Brooklyn Data: ')
    print(brooklyn_df)
    brooklyn_df.to_csv('brooklyn_data - ' + time + '.csv')
    print('\n')

    #Unknown
    print('Uncategorized Data: ')
    print(unknown_df)
    unknown_df.to_csv('unknown_data - ' + time + '.csv')


# -*- coding: utf-8 -*-
"""
Created on Fri Apr  8 12:07:32 2016

@author: Jee-Bee
"""

import numpy as np

def IDFT(X):
    """
    Input:
        X (numpy array) = frequency spectrum (length N)
    Output:
        The function should return a numpy array of length N
        x (numpy array) = The IDFT of the frequency spectrum X
                          (length N)
    """
    N = len(X)
    k = np.arange(N)
    print(k)
    x = []
    for n in range(N):
        s = np.exp(1j * 2 * np.pi * n / N * k)
        x = np.append(x, 1 / N * (X * s).sum())
    return(x)

X = np.array([1, 1, 1, 1])
x = IDFT(X)
print(x)


from html_parser import Parser
from webpage_bp.job import ChromeH

TRACK_ID = "806214832"

class Exxe:
    URL = "https://solex.blulogistics.net/SolexRC/g?Numero="

    def __init__(self, track_id) -> None:
        self.track_id = track_id
        self.parser = Parser(ChromeH(url=self.URL + self.track_id))
        self.info = self.parser.info
        self.tracking = self.parser.tracking

if __name__ == '__main__':
    track = Exxe(TRACK_ID)
    # print(track.track_id)


"""
To-Do

1. Borrowing binge strategy implementation

"""

import random
import time

class Graph(object):
    def __init__(self   , graph_dict = None):
        """Initialise a graph"""
        if graph_dict == None:
            graph_dict = {}
        self.graph_dict = graph_dict

        self.value_dict = {}

    def vertices(self):
        """Getter that returns the
        the vertices of the graph"""
        return list(self.graph_dict.keys())

    def edges(self):
        """Getter that returns the
        edges of the graph"""
        return self.__generate_edges()

    def add_vertex(self, vertex, value):
        """Adds the given vertex
        by adding a new entry to the
        graph_dict: {vertex:[]}"""
        if vertex not in self.graph_dict:
            self.graph_dict[vertex] = []
            self.value_dict[vertex] = value

    def add_edge(self, edge):
        """Adds an edge between
        two vertices"""
        edge = set(edge)
        (vertex1, vertex2) = tuple(edge)
        if vertex1 in self.graph_dict:
            self.graph_dict[vertex1].append(vertex2)
        else:
            self.graph_dict[vertex1] = [vertex2]

    def set_vertex_value(self, vertex, value):
        """Sets the value of a vertex"""
        self.value_dict[vertex] = value

    def __generate_edges(self):
        """Static method to
        generate the edges of the
        graph. An edge is a set with
        one or two vertices"""
        edges = []
        for vertex in self.graph_dict:
            for neighbor in self.graph_dict[vertex]:
                if {neighbor, vertex} not in edges:
                    edges.append({vertex, neighbor})
        return edges

    def transfer(self, vertex1, vertex2):
        """Transfers 1 unit from vertex1
        to vertex2"""
        self.value_dict[vertex1] -= 1
        self.value_dict[vertex2] += 1

    def game_won(self):
        for value in self.value_dict:
            if self.value_dict[value] < 0:
                return False
        return True

    def borrowing_binge(self):
        """ Whilst there are vertices in debt
        (game not won) make a random transfer"""
        move_count = 0
        while not self.game_won():
            borrower = random.choice(list(self.graph_dict.keys()))
            if self.value_dict[borrower] >= 0:
                print("Game not won")
                self.transfer(borrower, random.choice(list(self.graph_dict.keys())))
                move_count += 1
        print("Game won")
        print("Number of moves: " + str(move_count))

    def __str__(self):
        result = "Vertices:"
        for key in self.graph_dict:
            result += str(key) + "(" + str(self.value_dict[key]) + ") "
        result += "\nEdges: "
        for edge in self.__generate_edges():
            result += str(edge) + " "
        return result

def generate_winnable_graph():
    """Static method that generates
    a graph where it is possible to win.
    There is always a surplus of +4 to a
    graph of genus 2"""
    # Generate a random number from 1-26
    vertex_num = random.randrange(2, 27)
    # Dictionary to hold the vertices
    vertices = {}
    # Add vertex_num vertices
    for i in range(vertex_num):
        vertices[chr(i+65)] = []

    # Add edges
    # Connect each edge to the one next to it. The last loops round to the first
    for i in range(vertex_num-1):
        vertices[chr(i+65)] = [chr(i+66)]
    # Loop round so the last edge connects to the first
    vertices[str(chr(vertex_num+64))] = [chr(65)]
    # Update the vertices list of the first edge to include the last edge
    vertices["A"].append(str(chr(vertex_num+64)))

    out_graph = Graph(vertices)

    # Generate values for each vertex
    # Set each vertex value to -1 and compensate to give overall surplus of +4
    # Since the graph has a genus of 2 then the board is always winnable
    for i in range(vertex_num-1):
        out_graph.set_vertex_value(str(chr(i + 65)), -1)
    out_graph.set_vertex_value(str(chr(vertex_num + 64)), vertex_num+3)

    return out_graph

if __name__ == "__main__":

    graph = generate_winnable_graph()

    tic = time.perf_counter()
    graph.borrowing_binge()
    toc = time.perf_counter()

    print(f"Game won in {toc - tic:0.4f} seconds")


from setuptools import setup

from pathlib import Path
this_directory = Path(__file__).parent
long_description = (this_directory / "README.md").read_text()

setup(
    name="dort",
    version="1.4.1",
    description="dort solver/email api wrapper for python",
    url="https://github.com/chaarlottte/dortsolver-python-api",
    author="chaarlottte",
    packages=[
        "dort",
        "dort.captcha",
        "dort.mail"
    ],
    install_requires=[
        "imap-tools",
        "requests"
    ],
    long_description=long_description,
    long_description_content_type="text/markdown"
)


from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
import re

path = 'C:/Projects/OrgBuilder/Codes/DataServices/DataServices/outputs/'
# filename = path+"US Bank.xlsx"
filename = path+"BOA Full Org - 8.29.17 Project Baseline.xlsx"
colname = "Job Title"
# colname = "Job Family Desc"
Ngrams = 3
NumRecurrences = 3
output_path = path
output_tag = "BOA_0305"
dict_path = path + 'Short_Forms_Dictionary_KA.xlsx'

replace_dict = pd.read_excel(dict_path)
df = pd.read_excel(filename, encoding='latin-1')
replace_dict = {s: a for s, a in zip(replace_dict['Short'], replace_dict['Actual'])}

def clean_text(text, use_dict=1):
    """
    function to clean text of symbols, spaces, capitals etc.

    Parameters
    ----------
    text: a string

    Returns
    ----------
    text: modified initial string
    """
    # Cleaning
    REPLACE_BY_SPACE_RE = re.compile('[/(){}\[\]\|@,;&-]')
    BAD_SYMBOLS_RE = re.compile('[^0-9a-z #+_]')

    text = re.sub(r'([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))', r'\1 ', text)

    text = text.lower()  # lowercase text
    text = REPLACE_BY_SPACE_RE.sub(' ',
                                   text)
    # replace REPLACE_BY_SPACE_RE symbols by space in text. substitute the matched string in REPLACE_BY_SPACE_RE with space.
    text = BAD_SYMBOLS_RE.sub('',
                              text)  # remove symbols which are in BAD_SYMBOLS_RE from text. substitute the matched string in BAD_SYMBOLS_RE with nothing.
    # text = text.replace('x', '')

    # if use_dict == 1:
    #     keys = list(self.replace_dict.keys())
    #     values = list(self.replace_dict.values())
    #     for n in range(len(self.replace_dict)):
    #         text = re.sub(r"\b%s\b" % keys[n], values[n], text)

    if use_dict == 1:
        keys = list(replace_dict.keys())
        values = list(replace_dict.values())
        # for n in range(len(self.replace_dict)):
        #     text = re.sub(r"\b%s\b" % keys[n], values[n], text)
        for key_term in set(text.split(" ")).intersection(keys):
            n = keys.index(key_term)
            text = re.sub(r"\b%s\b" % keys[n], values[n], text)

    return text

def clean_column(df, col, use_dict=1):
    """
    Clean the column - remove caps, stop words etc.

    Parameters
    ----------
    df: pandas, Data frame with column to be cleaned
    col: str, Name of the column

    Returns
    --------
    df: pandas, data frame with column cleaned
    """
    df[col] = df[col].str.replace("-", ' ')
    df[col] = df[col].apply(clean_text, use_dict=use_dict)
    df[col] = df[col].str.replace('\d+', '')
    return df

def prepare_col(df, sourcecol, col, replace_spaces=1):
    # Drop duplicates
    df.drop_duplicates(subset=sourcecol, inplace=True)
    df[col] = df[sourcecol].str.strip()

    print('Cleaning column')
    df = clean_column(df, col)

    if replace_spaces:
        print('Replacing spaces')
        df[col] = ['_'.join(x.split()) for x in df[col]]

    return df

print('Clean the column')
df = df[[colname]]
df[colname] = df[colname].astype('str')
df = prepare_col(df, colname, colname + '_Cleaned', replace_spaces=1)
colname = colname + '_Cleaned'

print('Replace Underscores to spaces (if any).')
df[colname] = df[colname].str.replace("_", " ")

N = Ngrams  # N grams
n = NumRecurrences  # least number of recurrences

print('Get word vectorizer')
word_vectorizer = CountVectorizer(ngram_range=(1, N), analyzer='word')
sparse_matrix = word_vectorizer.fit_transform(df[colname])

print('Get frequencies')
frequencies = sum(sparse_matrix).toarray()[0]

print('Get Ngrams df')
ngrams_df = pd.DataFrame(frequencies, index=word_vectorizer.get_feature_names(), columns=['frequency'])

print('Sort on frequency and reset index')
ngrams_df.sort_values('frequency', ascending=False, inplace=True)
ngrams_df.reset_index(inplace=True)
ngrams_df.rename(columns={'index': colname}, inplace=True)

print('Add Ngram column')
ngrams_df['ngram'] = ngrams_df[colname].str.split().str.len()

print('Filter for recurrences >= {}'.format(str(n)))
ngrams_df = ngrams_df[ngrams_df['frequency'] >= n]

Ngram_path = '/Ngrams_1-{}_minCount_{}_{}.csv'.format(N, n, output_tag)
ngrams_df.to_csv(output_path + Ngram_path, index=False)


from utils.dspp_hidden import DSPPHHiddenLayer
from gpytorch.models.deep_gps.dspp import DSPP
from gpytorch.mlls import DeepPredictiveLogLikelihood

from gpytorch.models.deep_gps import DeepGP
from utils.deep_gp_hidden import DeepGPHiddenLayer
from gpytorch.mlls import DeepApproximateMLL, VariationalELBO

def deep_gp_ll(model, x_batch, y_batch):
    return model.likelihood.log_marginal(y_batch, model(x_batch)).cpu()

def dspp_ll(model, x_batch, y_batch):
    base_batch_ll = model.likelihood.log_marginal(y_batch, model(x_batch))
    deep_batch_ll = model.quad_weights.unsqueeze(-1) + base_batch_ll
    batch_log_prob = deep_batch_ll.logsumexp(dim=0)
    return batch_log_prob.cpu()

def create_deep_gp_config(num_inducing):
    return {
        "class_type": DeepGP,
        "hidden_layer_type": DeepGPHiddenLayer,
        "objective": lambda likelihood, model, num_data : DeepApproximateMLL(
            VariationalELBO(likelihood, model, num_data)
        ),
        "hidden_info": {"num_inducing": num_inducing},
        "class_init_info": {},
        "log_likelihood_cal": deep_gp_ll,
    }

def create_dspp_config(num_inducing, num_quad_site):
    return {
        "class_type": DSPP,
        "hidden_layer_type": DSPPHHiddenLayer,
        "objective": lambda likelihood, model, num_data : DeepPredictiveLogLikelihood(
            likelihood, model, num_data, beta=0.5
        ),
        "hidden_info": {"num_inducing": num_inducing, "num_quad_sites": num_quad_site},
        "class_init_info": {"num_quad_sites": num_quad_site},
        "log_likelihood_cal": dspp_ll,
    }


import argparse
import base64
import time
from io import BytesIO
from PIL import Image

from config import auto_timeout, image_depth, trial_laps
from pilot import Autopilot
from client import Client
from sim_recorder import SimRecorder

class AutoClient(Client):

    def __init__(self, address, conf, poll_socket_sleep_time=0.01):
        self.pilot = Autopilot(conf)
        self.mode = conf['mode']
        self.current_image = None
        self.current_telem = None
        self.fresh_data = False
        self.later_lap_sum = 0

        # give time for the autopilot to start up
        # time.sleep(1)

        super().__init__(address, conf=conf, poll_socket_sleep_time=poll_socket_sleep_time)
        if self.mode == 'train':
            self.recorder = SimRecorder(conf)
            self.braking = 0.0
        if self.mode == 'trial':
            self.trial_times = []

        # time.sleep(1)

        # only holds at the starting line if there's a race
        self.driving = self.mode in ['train', 'trial']

        # keep from rolling forward while setting up
        self.send_controls(0.0, 0.0, 1.0)

    def on_telemetry(self, data):
        # print(f"{data['time'] = }")
        self.check_progress(data) # no longer needed since Race Edition v22.05.30
        self.update_telemetry(data)
        if self.mode == 'train':
            data['brake'] = self.braking
            data['lap'] = self.current_lap
            self.recorder.record(data)

    def update_telemetry(self, data):
        # decode image
        if image_depth  == 1:
            self.current_image = Image.open(
                BytesIO(base64.b64decode(data['image']))).getchannel(image_depth)
        else:
            self.current_image = Image.open(
                BytesIO(base64.b64decode(data['image'])))
            # add specified telemetry from json
        if 'first_lap' in self.pilot.telemetry_columns:
            data['first_lap'] = self.current_lap == 1
        self.current_telem = [data[x] for x in self.pilot.telemetry_columns]
        self.fresh_data = True
        if self.mode == 'train':
            self.check_reset(data['time'], data['hit'])

    def check_reset(self, time_stamp, hit):
        if self.lap_start:
            lap_elapsed = time.time() - self.lap_start
            timed_out = lap_elapsed > auto_timeout
            # print(lap_elapsed)
            crashed = hit != 'none'
            if timed_out: # or crashed:
                print(f"{'Auto timeout' * timed_out}{'Crashed!' * crashed}")
                self.reset_car = True
                # self.stop()

    def update(self):
        steering, throttle, brake = 0.0, 0.0, 0.0
        if not self.current_image:
            print("Waiting for first image")
            self.driving = False
            # return
        if self.fresh_data:
            inputs = self.current_image, self.current_telem
            steering, throttle, brake = self.pilot.infer(inputs)
            if brake < 0.01:
                brake = 0
            self.fresh_data = False
        if not self.driving:
            steering, throttle, brake = 0.0, 0.0, 1.0
        if self.mode == 'train':
            self.braking = brake
        self.send_controls(steering, throttle, brake)

    def on_full_lap(self, lap_time):
        dirty_lap = self.hit_count > 0
        print(f"Lap {self.current_lap}: {lap_time:.2f}", end="")
        if self.hit_count > 0:
            print('*', end="")
        if self.current_lap > 1:
            self.later_lap_sum += lap_time
            later_lap_avg = self.later_lap_sum / (self.current_lap - 1)
            print(f" | avg: {later_lap_avg:.3f}", end="")
        self.print_fastest_lap(lap_time)
        if self.mode == 'trial' and self.current_lap <= trial_laps:
            self.trial_times.append(lap_time)
            if self.current_lap > 1:
                self.print_trial_times(dirty_lap)

    def print_trial_times(self, dirty_lap):
        print('trial times: ', end='')
        for n, t in enumerate(self.trial_times):
            print(f'{t:.2f}' + ('*' * dirty_lap) + (',' * (n < (len(self.trial_times) - 1))), end=' ')
        trial_avg = sum(self.trial_times[1:])/(len(self.trial_times)-1)
        print(f'| {trial_avg:.3f}')

# Create client and connect it with the simulator
def run_client(conf):
    host = conf["host"]
    port = conf["port"]
    client = AutoClient(address=(host, port), conf=conf,)

    # msg = '{ "msg_type" : "exit_scene" }'
    # client.send(msg)
    time.sleep(0.5)

    # time.sleep(2)

    # Load Track
    msg = f'{{"msg_type" : "load_scene", "scene_name" : "{conf["track"]}"}}'
    client.send(msg)
    time.sleep(1.0)
    # doesn't work when this isn't here
    # Configure Car
    client.send_config()
    time.sleep(1.0)

    # Drive car
    run_sim = True
    refresh_sim = False
    while run_sim:
        try:
            client.update()
            if client.aborted:
                # print("Client aborted, stopping driving.")
                run_sim = False
            if client.reset_car:
                # print("Refreshing sim")
                refresh_sim = True
                run_sim = False
            if not client.driving:
                _ = input('press Enter to start driving')
                client.driving = True
        except KeyboardInterrupt:
            run_sim = False
        time.sleep(0.1)

    # msg = '{ "msg_type" : "exit_scene" }'
    # client.send(msg)
    # time.sleep(0.5)

    # Close down client
    # print("waiting for msg loop to stop")
    client.stop()
    return refresh_sim

if __name__ == "__main__":

    track_list = [
        'generated_road',
        'warehouse',
        'sparkfun_avc',
        'generated_track',
        'roboracingleague_1',
        'waveshare',
        'mini_monaco',
        'warren',
        'circuit_launch',
        'mountain_track'
        ]

    parser = argparse.ArgumentParser(description="donkeysim_client")
    parser.add_argument("--host",
                        type=str,
                        # default="127.0.0.1",
                        default="donkey-sim.roboticist.dev",
                        help="host to use for tcp",)
    parser.add_argument("--port",
                        type=int,
                        default=9091,
                        help="port to use for tcp",)
    parser.add_argument("--track",
                        type=str,
                        default="sparkfun_avc",
                        help="name of donkey sim environment",
                        choices=track_list,)
    parser.add_argument("--model_number",
                        type=int,
                        default=None,
                        help='model number for pilot')
    parser.add_argument("--mode",
                        type=str,
                        default="trial",
                        help="autonomous driving mode",
                        choices=['race', 'train', 'trial']
                        )

    args = parser.parse_args()
    conf = {
        "host": args.host,
        "port": args.port,
        "mode": args.mode,
        "track": args.track,
        "model_number": args.model_number,
        "record_format": 'CSV', # None, 'CSV', 'tub' (Donkey Car), 'ASL' (openvslam)
        "image_format": 'PNG', # 'JPG', 'PNG', 'TGA'
        "image_depth": 1, # 1:'grayscale', 3:'rgb'
        "telem_type": 'donkey_extended_brake' # 'donkey_basic', 'donkey_extended_brake', 'gym'
    }
    while True:
        refresh = run_client(conf)
        if not refresh:
            break
        # time.sleep(3)
    print("client stopped")