import time, sys
import matplotlib.pyplot as plt
from astropy.io import fits
from astropy.cosmology import WMAP9
import pandas as pd
from scipy.stats import truncnorm
import numpy as np

from sedpy.observate import load_filters
from prospect import prospect_args
from prospect.fitting import fit_model
from prospect.io import write_results as writer
import sedpy

# --------------
# Load LEGA-C data
# --------------

objid = 172669
df_legac = pd.read_csv("/home/cwoodrum/Documents/LEGA-C/LEGAC_internalDR3.cat", sep=r"\s+", comment="#")
df_uvista = pd.read_csv("/home/cwoodrum/Documents/LEGA-C/UVISTA_final_v4.1.cat", sep=r"\s+", comment="#")

work_df_legac = df_legac[df_legac['id'] == objid]
work_df_uvista = df_uvista[df_uvista['id'] == objid]

obj_red = float(work_df_legac['z_spec'].values)
path_spec = "/home/cwoodrum/Documents/LEGA-C/spec1d/legac_M" + str(int(work_df_legac['mask'].values)) + "_v3.6_spec1d_" + str(objid) + ".fits"
path_err = "/home/cwoodrum/Documents/LEGA-C/spec1d/legac_M" + str(int(work_df_legac['mask'].values)) + "_v3.6_wht1d_" + str(objid) + ".fits"

def zp25_mags(flux_filter):
    return -2.5*np.log10(flux_filter) + 25

def mags_to_maggies(mags):
    return 10**(-0.4*mags)

def aper_to_tot(flux_aperture, flux_Ks_total, flux_Ks):
    flux_total = flux_aperture * (flux_Ks_total/flux_Ks)
    return flux_total

def phot_ready(df, phot_name, ephot_name):
    band = df[phot_name].values
    eband = df[ephot_name].values
    band_tot = aper_to_tot(band, df['Ks_tot'].values, df['Ks'].values)
    eband_tot = aper_to_tot(eband, df['eKs_tot'].values, df['eKs'].values)
    band_mags = zp25_mags(band_tot)
    eband_mags = zp25_mags(eband_tot)
    band_maggies = mags_to_maggies(band_mags)
    eband_maggies = mags_to_maggies(eband_mags)
    return band_maggies, eband_maggies

def phot_maggies(df):
    filternames = ["UVISTA_Ks", "UVISTA_H", "UVISTA_J", "UVISTA_Y",
                   "IB427.SuprimeCam", "IB464.SuprimeCam", "IB484.SuprimeCam", "IB505.SuprimeCam",
                   "IB527.SuprimeCam", "IB574.SuprimeCam", "IB624.SuprimeCam", "IB679.SuprimeCam",
                   "IB709.SuprimeCam", "IB738.SuprimeCam", "IB767.SuprimeCam", "IB827.SuprimeCam",
                   "spitzer_irac_ch1", "spitzer_irac_ch2",
                   "spitzer_irac_ch3", "spitzer_irac_ch4", #"spitzer_mips_24",
                   "galex_FUV", "galex_NUV",
                   "u_megaprime_sagem",
                   "B_subaru", "V_subaru", "g_subaru", "r_subaru", "i_subaru", "z_subaru"]

    Ks_maggies, eKs_maggies = phot_ready(df, 'Ks', 'eKs')
    H_maggies, eH_maggies = phot_ready(df, 'H', 'eH')
    J_maggies, eJ_maggies = phot_ready(df, 'J', 'eJ')
    Y_maggies, eY_maggies = phot_ready(df, 'Y', 'eY')

    IB427_maggies, eIB427_maggies = phot_ready(df, 'IB427', 'eIB427')
    IB464_maggies, eIB464_maggies = phot_ready(df, 'IB464', 'eIB464')
    IA484_maggies, eIA484_maggies = phot_ready(df, 'IA484', 'eIA484')
    IB505_maggies, eIB505_maggies = phot_ready(df, 'IB505', 'eIB505')
    IA527_maggies, eIA527_maggies = phot_ready(df, 'IA527', 'eIA527')
    IB574_maggies, eIB574_maggies = phot_ready(df, 'IB574', 'eIB574')
    IA624_maggies, eIA624_maggies = phot_ready(df, 'IA624', 'eIA624')
    IA679_maggies, eIA679_maggies = phot_ready(df, 'IA679', 'eIA679')
    IB709_maggies, eIB709_maggies = phot_ready(df, 'IB709', 'eIB709')
    IA738_maggies, eIA738_maggies = phot_ready(df, 'IA738', 'eIA738')
    IA767_maggies, eIA767_maggies = phot_ready(df, 'IA767', 'eIA767')
    IB827_maggies, eIB827_maggies = phot_ready(df, 'IB827', 'eIB827')

    ch1_maggies, ech1_maggies = phot_ready(df, 'ch1', 'ech1')
    ch2_maggies, ech2_maggies = phot_ready(df, 'ch2', 'ech2')
    ch3_maggies, ech3_maggies = phot_ready(df, 'ch3', 'ech3')
    ch4_maggies, ech4_maggies = phot_ready(df, 'ch4', 'ech4')
    #mips24_maggies, emips24_maggies = phot_ready(df, 'mips24', 'emips24')

    fuv_maggies, efuv_maggies = phot_ready(df, 'fuv', 'efuv')
    nuv_maggies, enuv_maggies = phot_ready(df, 'nuv', 'enuv')
    u_maggies, eu_maggies = phot_ready(df, 'u', 'eu')

    B_maggies, eB_maggies = phot_ready(df, 'B', 'eB')
    V_maggies, eV_maggies = phot_ready(df, 'V', 'eV')
    gp_maggies, egp_maggies = phot_ready(df, 'gp', 'egp')
    rp_maggies, erp_maggies = phot_ready(df, 'rp', 'erp')
    ip_maggies, eip_maggies = phot_ready(df, 'ip', 'eip')
    zp_maggies, ezp_maggies = phot_ready(df, 'zp', 'ezp')

    maggies = [Ks_maggies, H_maggies, J_maggies, Y_maggies,
                IB427_maggies, IB464_maggies, IA484_maggies, IB505_maggies,
                IA527_maggies, IB574_maggies, IA624_maggies, IA679_maggies,
                IB709_maggies, IA738_maggies, IA767_maggies, IB827_maggies,
               ch1_maggies, ch2_maggies, ch3_maggies, ch4_maggies, #mips24_maggies,
               fuv_maggies, nuv_maggies, u_maggies,
                B_maggies, V_maggies, gp_maggies, rp_maggies, ip_maggies, zp_maggies]

    emaggies = [eKs_maggies, eH_maggies, eJ_maggies, eY_maggies,
                eIB427_maggies, eIB464_maggies, eIA484_maggies, eIB505_maggies,
                eIA527_maggies, eIB574_maggies, eIA624_maggies, eIA679_maggies,
                eIB709_maggies, eIA738_maggies, eIA767_maggies, eIB827_maggies,
                ech1_maggies, ech2_maggies, ech3_maggies, ech4_maggies, #emips24_maggies,
                efuv_maggies, enuv_maggies, eu_maggies,
                eB_maggies, eV_maggies, egp_maggies, erp_maggies, eip_maggies, ezp_maggies]
    maggies = np.concatenate(maggies, axis=0)
    emaggies = np.concatenate(emaggies, axis=0)
    return maggies, emaggies

def prospector_wl_flux(spec_name, err_name):
    fluxes = fits.open(spec_name)[0].data
    invfluxvars = fits.open(err_name)[0].data
    fluxerrs = np.sqrt(1./invfluxvars)

    wav0 = fits.open(spec_name)[0].header["CRVAL1"]
    dwav = 0.6
    wl = np.arange(wav0, wav0+0.6*fluxes.shape[0], dwav)

    flux = np.array(fluxes)*1e-19
    flux_err = np.array(fluxerrs)*1e-19

    mask = (flux_err != 0) & (flux != 0) & (wl > 6500) & (wl < 9000) & np.isfinite(flux_err) & (wl > (1.0 + obj_red) * 3550)

    wl_good = wl[mask]
    flux_good = flux[mask]
    flux_err_good = flux_err[mask]

    lam2_c = (wl_good*wl_good)/2.99792e18
    fl_nu = lam2_c*flux_good
    fl_jansky = fl_nu*1e23
    fl_maggies = fl_jansky/3631
    fl_err = lam2_c*flux_err_good*1e23
    fl_err_maggies = fl_err/3631

    return wl_good, fl_maggies, fl_err_maggies

# --------------
# RUN_PARAMS
# When running as a script with argparsing, these are ignored.  Kept here for backwards compatibility.
# --------------

run_params = {'verbose':True,
              'debug':False,
              #'outfile':'demo_galphot',
              'nofork':True,
              # dynesty Fitter parameters
              'nested_bound': 'multi', # bounding method
              'nested_sample': 'rwalk', # sampling method
              'nested_walks': 50,
              'nested_nlive_init': 200,
              'nested_nlive_batch': 200,
              'nested_bootstrap': 0,
              'nested_dlogz_init': 0.01,
              'nested_weight_kwargs': {"pfrac": 1.0},
              'nested_stop_kwargs': {"post_thresh": 0.1},
              # Obs data parameters
#              'objid':0,
#              'phottable': 'demo_photometry.dat',
              'luminosity_distance': None,  # in Mpc
              }

# --------------
# Model Definition
# --------------

def build_model(fixed_metallicity=None, luminosity_distance=None, **extras):
    """Construct a model.  This method defines a number of parameter
    specification dictionaries and uses them to initialize a
    `models.sedmodel.SedModel` object.
    :param object_redshift:
        If given, given the model redshift to this value.
    :param add_dust: (optional, default: False)
        Switch to add (fixed) parameters relevant for dust emission.
    :param add_neb: (optional, default: False)
        Switch to add (fixed) parameters relevant for nebular emission, and
        turn nebular emission on.
    :param luminosity_distance: (optional)
        If present, add a `"lumdist"` parameter to the model, and set it's
        value (in Mpc) to this.  This allows one to decouple redshift from
        distance, and fit, e.g., absolute magnitudes (by setting
        luminosity_distance to 1e-5 (10pc))
    """
    from prospect.models.templates import TemplateLibrary, adjust_continuity_agebins
    from prospect.models import priors, sedmodel, transforms

    model_params = TemplateLibrary['continuity_sfh']

    ### BASIC PARAMETERS ###

    model_params["imf_type"] = {'N': 1,
                                'isfree': False,
                                'init': 1, #1=Chabrier
                                'prior': None}

    model_params['zred'] = {'N': 1,
                                'isfree': True,
                                'init': obj_red,
                                "prior": priors.TopHat(mini=obj_red-0.05, maxi=obj_red+0.05)}

    model_params['add_igm_absorption'] = {'N': 1,
                                'isfree': False,
                                'init': 1,
                                'units': None,
                                'prior': None}

    model_params['add_agb_dust_model'] = {'N': 1,
                                'isfree': False,
                                'init': 1,
                                'units': None,
                                'prior': None}

    # model_params['pmetals'] = {'N': 1,
    #                             'isfree': False,
    #                             'init': -99,
    #                             'units': '',
    #                             'prior': priors.TopHat(mini=-3, maxi=-1)}

    ### SFH ###

    tuniv = WMAP9.age(obj_red).value
    model_params = adjust_continuity_agebins(model_params, tuniv=tuniv, nbins=7)

    ### DUST ABSORPTION ###

    model_params['dust_type'] = {'N': 1,
                            'isfree': False,
                            'init': 4, #4=Kriek & Conroy
                            'units': 'index',
                            'prior': None}

    model_params['dust1'] = {'N': 1,
                            'isfree': False,
                            'depends_on': transforms.dustratio_to_dust1,
                            'init': 0.,
                            'units': 'optical depth towards young stars'}

    model_params['dust_ratio'] = {'N': 1,
                            'isfree': True,
                            'init': 1.0,
                            'init_disp': 0.8,
                            'disp_floor': 0.8,
                            'units': 'ratio of birth-cloud to diffuse dust',
                            'prior': priors.ClippedNormal(mini=0.0, maxi=2.0, mean=1.0, sigma=0.3)}

    model_params['dust2'] = {'N': 1,
                            'isfree': True,
                            'init': 1.0,
                            'init_disp': 0.25,
                            'disp_floor': 0.15,
                            'units': 'optical depth at 5500AA',
                            'prior': priors.ClippedNormal(mini=0.0, maxi=4.0, mean=0.3, sigma=1)}

    model_params['dust_index'] = {'N': 1,
                            'isfree': True,
                            'init': 0.0,
                            'init_disp': 0.25,
                            'disp_floor': 0.15,
                            'units': 'power-law multiplication of Calzetti',
                            'prior': priors.TopHat(mini=-2.0, maxi=0.5)}

    ### DUST EMISSION ###

    model_params.update(TemplateLibrary["dust_emission"])

    ### NEBULAR EMISSION ###

    model_params['add_neb_emission'] = {'N': 1,
                            'isfree': False,
                            'init': True,
                            'prior': None}

    model_params['add_neb_continuum'] = {'N': 1,
                            'isfree': False,
                            'init': True,
                            'prior': None}

    model_params['gas_logz'] = {'N': 1,
                            'isfree': True,
                            'init': 0.0,
                            'units': r'log Z/Z_\odot',
                            'prior': priors.TopHat(mini=-2.0, maxi=0.5)}

    model_params['gas_logu'] = {'N': 1,
                            'isfree': True,
                            'init': -1.0,
                            'units': '',
                            'prior': priors.TopHat(mini=-4.0, maxi=-1.0)}

    ### CALIBRATION ###
    model_params['polyorder'] = {'N': 1,
                            'init': 10,
                            'isfree': False}

    model_params['spec_norm'] = {'N': 1,
                            'init': 1.0,
                            'isfree': True,
                            'prior': priors.Normal(sigma=0.2, mean=1.0),
                            'units': 'f_true/f_obs'}

    model_params['spec_jitter'] = {"N": 1,
                            "isfree": True,
                            "init": 1.0,
                            "prior": priors.TopHat(mini=0., maxi=4.0)}

    model_params['f_outlier_spec'] = {"N": 1,
                            "isfree": True,
                            "init": 0.01,
                            "prior": priors.TopHat(mini=1e-5, maxi=0.5)}

    model_params['nsigma_outlier_spec'] = {"N": 1,
                            "isfree": False,
                            "init": 5.0}

    ### SMOOTHING ###

    model_params.update(TemplateLibrary["spectral_smoothing"])
    model_params["sigma_smooth"]["prior"] = priors.TopHat(mini=150, maxi=250)

    # Now instantiate the model using this new dictionary of parameter specifications
    model = sedmodel.SedModel(model_params)

    return model

# --------------
# Observational Data
# --------------

def build_obs(objid=objid, phottable=None,
             luminosity_distance=None, **kwargs):

    from prospect.utils.obsutils import fix_obs

    wl, flux, flux_err = prospector_wl_flux(path_spec, path_err)
    maggies, emaggies = phot_maggies(work_df_uvista)

    obs = {}
    filternames = ["UVISTA_Ks", "UVISTA_H", "UVISTA_J", "UVISTA_Y",
                   "IB427.SuprimeCam", "IB464.SuprimeCam", "IB484.SuprimeCam", "IB505.SuprimeCam",
                   "IB527.SuprimeCam", "IB574.SuprimeCam", "IB624.SuprimeCam", "IB679.SuprimeCam",
                   "IB709.SuprimeCam", "IB738.SuprimeCam", "IB767.SuprimeCam", "IB827.SuprimeCam",
                   "spitzer_irac_ch1", "spitzer_irac_ch2", "spitzer_irac_ch3", "spitzer_irac_ch4", #"spitzer_mips_24",
                   "galex_FUV", "galex_NUV",
                   "u_megaprime_sagem",
                   "B_subaru", "V_subaru", "g_subaru", "r_subaru", "i_subaru", "z_subaru",]
    obs['filters'] = load_filters(filternames)
    obs['maggies'] = maggies
    floor_phot = 0.05*obs['maggies']
    obs['maggies_unc'] = np.clip(emaggies, floor_phot, np.inf)
    obs['wavelength'] = wl
    obs['spectrum'] = flux
    floor_spec = 0.01*obs['spectrum']
    obs['unc'] = np.clip(flux_err, floor_spec, np.inf)
    obs['objid'] = objid

    obs = fix_obs(obs)
    return obs

# --------------
# SPS Object
# --------------

def build_sps(compute_vega_mags=False, **extras):
    from prospect.sources import FastStepBasis
    sps = FastStepBasis()
    return sps

# -----------------
# Noise Model
# ------------------

def build_noise(**extras):
    from prospect.likelihood import NoiseModel
    from prospect.likelihood.kernels import Uncorrelated
    jitter = Uncorrelated(parnames = ['spec_jitter'])
    spec_noise = NoiseModel(kernels=[jitter],metric_name='unc',weight_by=['unc'])
    return spec_noise, None

# -----------
# Everything
# ------------

def build_all(**kwargs):

    return (build_obs(**kwargs), build_model(**kwargs),
            build_sps(**kwargs), build_noise(**kwargs))

if __name__=='__main__':

    # - Parser with default arguments -
    parser = prospect_args.get_parser()
    # - Add custom arguments -
#    parser.add_argument('--object_redshift', type=float, default=zred,
#                        help=("Redshift for the model"))
    parser.add_argument('--add_neb', action="store_true",
                        help="If set, add nebular emission in the model (and mock).")
    parser.add_argument('--add_duste', action="store_true",
                        help="If set, add dust emission to the model.")
#    parser.add_argument('--luminosity_distance', type=float, default=1e-5,
#                        help=("Luminosity distance in Mpc. Defaults to 10pc "
#                              "(for case of absolute mags)"))
#    parser.add_argument('--phottable', type=str, default="demo_photometry.dat",
#                        help="Names of table from which to get photometry.")
    parser.add_argument('--objid', type=int, default=objid,
                    help="zero-index row number in the table to fit.")

    args = parser.parse_args()
    run_params = vars(args)
    obs, model, sps, noise = build_all(**run_params)

    #run_params["sps_libraries"] = sps.ssp.libraries
    run_params["param_file"] = __file__

    print(model)

    if args.debug:
        sys.exit()

    #hfile = setup_h5(model=model, obs=obs, **run_params)
    hfile = "{0}_{1}_mcmc.h5".format(args.outfile, int(time.time()))
    output = fit_model(obs, model, sps, noise, **run_params)

    writer.write_hdf5(hfile, run_params, model, obs,
                    output["sampling"][0], output["optimization"][0],
                    tsample=output["sampling"][1],
                    toptimize=output["optimization"][1])

    try:
        hfile.close()
    except(AttributeError):
        pass


#!/usr/bin/env python3
"""Setup file for pip module."""

from setuptools import setup

with open('README.md') as f:
    DESCRIPTION = f.read()

setup(
    name='psd',
    version='1.5.2',
    description='Python tools for data visualization and spectral analysis.',
    long_description=DESCRIPTION,
    long_description_content_type='text/markdown',
    url='https://gitlab.in2p3.fr/j2b.bayle/psd',
    author='Jean-Baptiste Bayle (APC/CNRS/CNES)',
    author_email='bayle@apc.in2p3.fr',
    license='GNU GPLv3',
    packages=['psd'],
    install_requires=[
        'argparse',
        'numpy',
        'scipy',
        'matplotlib',
        'h5py',
    ],
    entry_points={
        'console_scripts': [
            'psd=psd.psd:main',
            'convert=psd.convert:main',
        ],
    },
    zip_safe=False,
)


# Imports
import pygame
import random
import math
import os
# Initialize game engine
pygame.init()
pygame.mixer.init()

# Window
WIDTH = 1000
HEIGHT = 700
SIZE = (WIDTH, HEIGHT)
TITLE = "Space War"
screen = pygame.display.set_mode(SIZE)
pygame.display.set_caption(TITLE)

# stages
START = 0
PLAYING = 1
WIN = 2
LOSE = 3

stage = START

#misc
shots = 0

#font
FONT_SM = pygame.font.Font("assets/fonts/heav.ttf", 24)
FONT_MD = pygame.font.Font("assets/fonts/heav.ttf", 32)
FONT_LG = pygame.font.Font("assets/fonts/heav.ttf", 64)
FONT_XL = pygame.font.Font("assets/fonts/heav.ttf", 96)

# Timer
clock = pygame.time.Clock()
refresh_rate = 60

# Colors
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
GREEN = (100, 255, 100)

#Sounds
song1 = "assets/sounds/Bonus Stage Glowball - Sonic the Hedgehog 3 [OST].wav"
song2 = "assets/sounds/Sonic CD (USA) Music Special Stage-[AudioTrimmer.com].wav"
song3 = "assets/sounds/mini_boss_music.wav"
song4 = "assets/sounds/game_over.wav"
song5 = "assets/sounds/win.wav"
songs = [song1, song2, song3, song4, song5]
current_song = 0

def start_music(song):
    pygame.mixer.music.load(song)
    pygame.mixer.music.play(-1)

enemy_explosion = pygame.mixer.Sound('assets/sounds/enemy_explode.wav')
shot = pygame.mixer.Sound('assets/sounds/bullet.wav')
enemy_shot = pygame.mixer.Sound('assets/sounds/bomb.wav')
damage = pygame.mixer.Sound('assets/sounds/damage.wav')
explosion = pygame.mixer.Sound('assets/sounds/death.wav')

# Images
ship_img = pygame.image.load('assets/images/ship.png')
hit = pygame.image.load('assets/images/damage.png')
laser_img = pygame.image.load('assets/images/laser.png')
bomb_img = pygame.image.load('assets/images/enemy_laser.gif')
mob_img1 = pygame.image.load('assets/images/Buzz_bomber.png')
mob_img2 = pygame.image.load('assets/images/puffer_fish.png')
mini_boss = pygame.image.load('assets/images/boss1.png')
start_screen = pygame.image.load('assets/images/start_screen.gif')
background = pygame.image.load('assets/images/Death_Egg.gif')

# Game classes
class Ship(pygame.sprite.Sprite):
    def __init__(self, x, y, image):
        super().__init__()

        self.image = image
        self.mask = pygame.mask.from_surface(self.image)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        self.speed = 5
        self.shield = 10

    def move_left(self):
        self.rect.x -= self.speed
        if self.rect.x <= 0:
            self.rect.x = 0

    def move_right(self):
        self.rect.x += self.speed
        if self.rect.x >= 920:
            self.rect.x = 920

    def shoot(self):
        laser = Laser(laser_img)
        laser.rect.centerx = self.rect.centerx
        laser.rect.centery = self.rect.top
        lasers.add(laser)

    def update(self, bombs, mobs):
        hit_list = pygame.sprite.spritecollide(self, bombs, True, pygame.sprite.collide_mask)

        for hit in hit_list:
            pygame.mixer.Sound.play(damage)
            player.shield -= 1

        hit_list = pygame.sprite.spritecollide(self, mobs, False, pygame.sprite.collide_mask)
        if len(hit_list) > 0:
            player.shield = 0

        if player.shield == 0:
            pygame.mixer.Sound.play(explosion)
            current_song = 3
            start_music(songs[current_song])
            pygame.mixer.music.play(0)
            self.kill()

class Laser(pygame.sprite.Sprite):

    def __init__(self, image):
        super().__init__()

        self.image = image
        self.rect = self.image.get_rect()

        self.speed = 5

    def update(self):
        self.rect.y -= self.speed
        if self.rect.y < -10:
            self.kill()

class Mob(pygame.sprite.Sprite):
    def __init__(self, x, y, image):
        super().__init__()

        self.image = image
        self.mask = pygame.mask.from_surface(self.image)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        if self.image == mob_img2:
            self.shield = 2

    def drop_bomb(self):
        bomb = Bomb(bomb_img)
        bomb.rect.centerx = self.rect.centerx
        bomb.rect.centery = self.rect.bottom
        bombs.add(bomb)

    def update(self, lasers):
        hit_list = pygame.sprite.spritecollide(self, lasers, True, pygame.sprite.collide_mask)
        if len(hit_list) > 0:
            if self.image == mob_img2:
                self.shield -= 1
                if self.shield == 0:
                    pygame.mixer.Sound.play(enemy_explosion)
                    player.score += 100
                    self.kill()
            else:
                pygame.mixer.Sound.play(enemy_explosion)
                player.score += 100
                self.kill()

        if self.rect.top > 700:
            self.kill()

class Boss(pygame.sprite.Sprite):
    def __init__(self, x, y, image):
        super().__init__()

        self.image = image
        self.mask = pygame.mask.from_surface(self.image)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.shield = 50

    def drop_bomb(self):
        bomb = Bomb(bomb_img)
        bomb.rect.centerx = self.rect.centerx
        bomb.rect.centery = self.rect.bottom
        bombs.add(bomb)

    def update(self, lasers):
        hit_list = pygame.sprite.spritecollide(self, lasers, True, pygame.sprite.collide_mask)
        if len(hit_list) > 0 :
            self.shield -= 1

        if self.shield == 0:
            pygame.mixer.Sound.play(enemy_explosion)
            player.score += 1000
            current_song = 4
            start_music(songs[current_song])
            self.kill()
            pygame.mixer.music.play(0)

        if self.rect.top > 700:
            self.kill()

class Boss_AI:
    def __init__(self, bosses):
        self.bosses = bosses
        self.moving_right = True
        self.speed = 5
        self.bomb_rate = 10

    def move(self):
        reverse = False

        for b in bosses:
            if self.moving_right:
                b.rect.x += self.speed
                if b.rect.right >= WIDTH:
                    reverse = True
            else:
                b.rect.x -= self.speed
                if b.rect.left <=0:
                    reverse = True

        if reverse == True:
            self.moving_right = not self.moving_right
            for b in bosses:
                b.rect.y += 0

    def choose_bomber(self):
        rand = random.randrange(0, self.bomb_rate)
        all_bosses = bosses.sprites()

        if len(all_bosses) > 0 and rand == 0:
            return random.choice(all_bosses)
        else:
            return None

    def update(self):
        self.move()

        bomber = self.choose_bomber()
        if bomber != None:
            pygame.mixer.Sound.play(enemy_shot)
            bomber.drop_bomb()

class Bomb(pygame.sprite.Sprite):

    def __init__(self, image):
        super().__init__()

        self.image = image
        self.rect = self.image.get_rect()

        self.speed = 3

    def update(self):
        self.rect.y += self.speed
        if self.rect.y > HEIGHT:
            self.kill()

# implement some kind of wave system
class Fleet:

    def __init__(self, mobs):
        self.mobs = mobs
        self.moving_right = True
        self.speed = 5
        self.bomb_rate = 60

    def move(self):
        reverse = False

        for m in mobs:
            if self.moving_right:
                m.rect.x += self.speed
                if m.rect.right >= WIDTH:
                    reverse = True
            else:
                m.rect.x -= self.speed
                if m.rect.left <=0:
                    reverse = True

        if reverse == True:
            self.moving_right = not self.moving_right
            for m in mobs:
                m.rect.y += 32

    def choose_bomber(self):
        rand = random.randrange(0, self.bomb_rate)
        all_mobs = mobs.sprites()

        if len(all_mobs) > 0 and rand == 0:
            return random.choice(all_mobs)
        else:
            return None

    def update(self):
        self.move()

        bomber = self.choose_bomber()
        if bomber != None:
            pygame.mixer.Sound.play(enemy_shot)
            bomber.drop_bomb()

def game_setup():
    global x, y, image, lasers, bombs, mobs, player, ship, fleet, stage, bosses, boss_AI

    stage = START

    # Make game objects
    ship = Ship(384, 536, ship_img)

    # Make sprite groups
    player = pygame.sprite.GroupSingle()
    player.add(ship)
    player.score = 0
    player.shield = 10
    player.level = 0
    if stage == PLAYING:
        player.level = 1

    lasers = pygame.sprite.Group()
    bombs = pygame.sprite.Group()

    mobs = pygame.sprite.Group()
    fleet = Fleet(mobs)

# Game helper functions
def show_title_screen():
    screen.blit(start_screen, (0, 0))
    title_text = FONT_XL.render("Space War!", 1, WHITE)
    subtitle = FONT_MD.render("(Press ENTER to start.)", True, WHITE)
    t_rect = title_text.get_rect()
    t_rect.centerx = WIDTH/2
    t_rect.bottom = HEIGHT/2
    s_rect = subtitle.get_rect()
    s_rect.centerx = WIDTH/2
    s_rect.bottom = (HEIGHT/2) + 25
    screen.blit(title_text, t_rect)
    screen.blit(subtitle, s_rect)

def show_stats(player):
    score_text = FONT_MD.render('Score: ' + str(player.score), 1, WHITE)
    screen.blit(score_text, [32, 32])

    health_text = FONT_MD.render('Shield: ' + str(player.shield), 1, WHITE)
    screen.blit(health_text, [32, 64])

    level_text = FONT_MD.render('Level: ' + str(player.level), 1, WHITE)
    screen.blit(level_text, [32, 96])

def level_setup():
    global x, y, image, lasers, bombs, mobs, player, ship, fleet, stage, bosses, boss_AI

    if player.level == 5:
        current_song = 2
        start_music(songs[current_song])

    # wave1
    mob1 = Mob(128, 85, mob_img1)
    mob2 = Mob(256, 85, mob_img1)
    mob3 = Mob(384, 85, mob_img1)
    wave1 = [mob1, mob2, mob3]

    #wave 2
    mob4 = Mob(128, 0, mob_img1)
    mob5 = Mob(256, 0, mob_img1)
    mob6 = Mob(384, 0, mob_img1)
    mob7 = Mob(0, 0, mob_img2)
    mob8 = Mob(512, 0, mob_img2)
    wave2 = [mob4, mob5, mob6, mob7, mob8]

    #wave3
    mob9 = Mob(128, 0, mob_img2)
    mob10 = Mob(256, 0, mob_img2)
    mob11 = Mob(384, 0, mob_img2)
    mob12 = Mob(0, 0, mob_img2)
    mob13 = Mob(512, 0, mob_img2)
    mob14 = Mob(64, 100, mob_img2)
    mob15 = Mob(450, 100, mob_img2)
    wave3 = [mob9, mob10, mob11, mob12, mob13, mob14, mob15]

    #wave4
    mob17 = Mob(128, 0, mob_img1)
    mob18 = Mob(256, 0, mob_img2)
    mob19 = Mob(384, 0, mob_img1)
    mob20 = Mob(0, 0, mob_img2)
    mob21 = Mob(512, 0, mob_img2)
    mob22 = Mob(128, 100, mob_img2)
    mob23 = Mob(256, 100, mob_img1)
    mob24 = Mob(384, 100, mob_img2)
    mob25 = Mob(0, 100, mob_img1)
    mob26 = Mob(512, 100, mob_img1)

    wave4 = [mob17, mob18, mob19, mob20, mob21, mob22, mob23, mob24, mob25, mob26]

    #wave 5/boss
    if player.level == 5:
        boss1 = Boss(500, 0 , mini_boss)
        bosses = pygame.sprite.GroupSingle()
        bosses.add(boss1)
        boss_AI = Boss_AI(bosses)

    mobs = pygame.sprite.Group()
    if player.level ==1:
        mobs.add(wave1)
    if player.level == 2:
        mobs.add(wave2)
    if player.level == 3:
        mobs.add(wave3)
    if player.level == 4:
        mobs.add(wave4)

# Game loop
game_setup()

if stage == START:
    start_music(songs[current_song])
    pygame.mixer.music.play(-1)

if stage == PLAYING:
    current_song = 1
    start_music(songs[current_song])
    pygame.mixer.music.play(-1)

done = False

while not done:
    # Event processing (React to key presses, mouse clicks, etc.)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if stage == START:
                current_song = 0
                start_music(songs[current_song])
                if event.key == pygame.K_KP_ENTER:
                    stage = PLAYING
                    current_song = 1
                    start_music(songs[current_song])
            if event.key == pygame.K_SPACE and stage == PLAYING:
                pygame.mixer.Sound.play(shot)
                shots += 1
                ship.shoot()

    if stage == PLAYING:
        pressed = pygame.key.get_pressed()

        if pressed[pygame.K_LEFT]:
            ship.move_left()
        elif pressed[pygame.K_RIGHT]:
            ship.move_right()

    # Game logic (Check for collisions, update points, etc.)
    if stage == PLAYING:
        player.update(bombs, mobs)
        if player.level == 5:
            player.update(bombs, bosses)

        lasers.update()
        mobs.update(lasers)
        bombs.update()
        fleet.update()

        if player.level == 5:
            bosses.update(lasers)
            boss_AI.update()
        if len(player) == 0:
            stage = LOSE

        elif len(mobs) == 0 and player.level !=5:
            player.score += 100 * shots
            player.level+=1
            level_setup()

        if player.level == 5 and len(bosses) == 0:
            player.score += 100 * shots
            player.level+=1
            stage = WIN

    # Drawing code (Describe the picture. It isn't actually drawn yet.)
    if stage == START:
        show_title_screen()

    elif stage == PLAYING:
        screen.blit(background, (0, 0))
        lasers.draw(screen)
        player.draw(screen)
        bombs.draw(screen)
        mobs.draw(screen)
        show_stats(player)
        if player.level == 5:
            bosses.draw(screen)

    if stage == WIN:
        screen.blit(start_screen, (0,0))
        win_text = FONT_XL.render("YOU WIN!", True, WHITE)
        win_subtext = FONT_MD.render("(Press R to restart.)", True, WHITE)
        w_rect = win_text.get_rect()
        w_rect.centerx = WIDTH/2
        w_rect.bottom =  HEIGHT/2
        r_rect = win_subtext.get_rect()
        r_rect.centerx = WIDTH/2
        r_rect.bottom = (HEIGHT/2) + 20
        screen.blit(win_text, w_rect)
        screen.blit(win_subtext, r_rect)
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                stage = START
                current_song = 0
                start_music(songs[current_song])
                game_setup()

    elif stage == LOSE:
        screen.blit(start_screen, (0, 0))
        lose_text = FONT_XL.render("Game Over", True, WHITE)
        lose_subtext = FONT_MD.render("(Press R to restart.)", True, WHITE)
        l_rect = lose_text.get_rect()
        l_rect.centerx = WIDTH/2
        l_rect.bottom = (HEIGHT/2)
        g_rect = lose_subtext.get_rect()
        g_rect.centerx = WIDTH/2
        g_rect.bottom = (HEIGHT/2) + 20
        screen.blit(lose_text, l_rect)
        screen.blit(lose_subtext, g_rect)
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                stage = START
                current_song = 0
                start_music(songs[current_song])
                game_setup()

    # Update screen (Actually draw the picture in the window.)
    pygame.display.flip()

    # Limit refresh rate of game loop
    clock.tick(refresh_rate)

# Close window and quit
pygame.quit()


# -*- coding: UTF-8 -*-

from selenium import webdriver

import common.plog as Log

log = Log.MyLog.get_log()
logger = log.get_logger()

def browser(browserName = 'firefox'):
    try:
        if browserName == 'chrome':
            driver = webdriver.Chrome()
            logger.info('Chrome浏览器启动')
        elif browserName == 'chrome0':
            option = webdriver.ChromeOptions()
            option.add_argument('headless')
            #option.add_argument('--start-maximized')
            option.add_argument('--window-size=1920,1080')
            driver = webdriver.Chrome(chrome_options=option)
            logger.info('Chrome浏览器以静默模式启动')
        elif browserName == 'firefox':
            driver = webdriver.Firefox()
            logger.info('Firefox浏览器启动')
        elif browserName == 'firefox0':
            option = webdriver.FirefoxOptions()
            option.add_argument('headless')
            option.add_argument('--window-size=1920,1080')
            driver = webdriver.Firefox(firefox_options=option)
            logger.info('Firefox浏览器以静默模式启动')
        else:
            driver = webdriver.Firefox()
            logger.warning('参数错误，默认启动Firefox浏览器')
    except Exception as e:
        logger.error('启动浏览器时出现异常：%s' % e)
    else:
        driver.maximize_window()
        return driver

'''
        if self.browser == 'firefox':
            browser_host = rcf.get_driver('firefox')
            self.driver = Remote(command_executor='http://' + browser_host + '/wd/hub', # 'http://127.0.0.1:4444/wd/hub'
                                 desired_capabilities={'platform':'ANY',
                                                       'browserName': 'firefox', # 'firefox'
                                                       'version':'',
                                                       'javascriptEnabled':True
                                                       }
                                )
        else:
            browser_host = rcf.get_driver('chrome')
            self.driver = Remote(command_executor='http://' + browser_host + '/wd/hub', # 'http://127.0.0.1:4444/wd/hub'
                                 desired_capabilities={'platform':'ANY',
                                                       'browserName': 'chrome', # 'firefox'
                                                       'version':'',
                                                       'javascriptEnabled':True
                                                       }
                                )'''


from PyInquirer import prompt, print_json
import calendar as cal #Used calender for better Readbility of user's outputs (e.g. getting month name instead of a number)
import time
import pandas as pd
import numpy as np

CITY_DATA = { 'chicago': 'chicago.csv',
              'new york city': 'new_york_city.csv',
              'washington': 'washington.csv' }

def get_filters():
    """
    Asks user to specify a city, month, and day to analyze.

    Returns:
        (str) city - name of the city to analyze
        (str) month - name of the month to filter by, or "all" to apply no month filter
        (str) day - name of the day of week to filter by, or "all" to apply no day filter
    """
    #### Using PyInquirer to have a fancy user interaction instead of bothering with typing
    #Getting City data
    questions = [
        {
            'type': 'list',
            'name': 'City',
            'message': 'Please Select one of the following cities:',
            'choices' : ['chicago','new york city','washington'],
        }
    ]

    answers = prompt(questions)
    city = answers.get("City","")

    #Getting Month
    questions = [
        {
            'type': 'list',
            'name': 'Month',
            'message': 'Please Select one of the following Months:',
            'choices' : ['all','january', 'february', 'march', 'april', 'may', 'june'],
        }
    ]

    answers = prompt(questions)
    month = answers.get("Month","")

    #Getting Day
    questions = [
        {
            'type': 'list',
            'name': 'Day',
            'message': 'Please Select one of the following days',
            'choices' : ['all','monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday','sunday'],
        }
    ]

    answers = prompt(questions)
    day = answers.get("Day","")
    ####

    print('Hello! Let\'s explore some US bikeshare data!')
    # get user input for city (chicago, new york city, washington). HINT: Use a while loop to handle invalid inputs
    #city = input("\nPlease Type one of the following Cities [chicago, new york city, washington]: ")
    #city = city.lower()
    while city not in CITY_DATA.keys():
        city = input("The vlaue you've entered isn't of the list given [chicago, new york city, washington]. Please enter the city name again: ").lower()

    # get user input for month (all, january, february, ... , june)
    #List for condition check
    months = ['january', 'february', 'march', 'april', 'may', 'june','all']
    #month = input("Please type the month name or [all]: ").lower().strip(' ')

    while month not in months :
        month = input("Wrong Value!\n please type 1 of the following (all, january, february, ... , june): ").lower()

    # get user input for day of week (all, monday, tuesday, ... sunday)
    days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday','sunday','all']
    #day = input("Please type the day of week name or [all]: ")
    while day not in days :
        day = input("Wrong Value!\n please type 1 of the following: ").lower()

    print('-'*40)
    return city, month, day

def load_data(city, month, day):
    """
    Loads data for the specified city and filters by month and day if applicable.

    Args:
        (str) city - name of the city to analyze
        (str) month - name of the month to filter by, or "all" to apply no month filter
        (str) day - name of the day of week to filter by, or "all" to apply no day filter
    Returns:
        df - Pandas DataFrame containing city data filtered by month and day
    """
    #Referenceing solved Quiz 3 code in UDACITY (My Solution):
    # load data file into a dataframe
    df = pd.read_csv(CITY_DATA[city])

    # convert the Start Time column to datetime
    df['Start Time'] = pd.to_datetime(df['Start Time'])

    # extract month and day_of_week from Start Time to create new columns
    df['month'] = df['Start Time'].dt.month
    df['day_of_week'] = df['Start Time'].dt.weekday_name

    # filter by month if applicable
    if month != 'all':
        # use the index of the months list to get the corresponding int
        months = ['january', 'february', 'march', 'april', 'may', 'june']
        month = months.index(month) + 1 #+1 because iteration starts from 0 not 1.
        # filter by month to create the new dataframe
        df = df[df['month'] == month]

    # filter by day of week if applicable
    if day != 'all':
        # filter by day of week to create the new dataframe
        df = df[df['day_of_week'] == day.title()]

    return df

def time_stats(df):
    """Displays statistics on the most frequent times of travel."""

    print('\nCalculating The Most Frequent Times of Travel...\n')
    start_time = time.time()

    # display the most common month
    most_common_month = cal.month_name[df['month'].mode()[0]] #Used Calender lib to output month name

    # display the most common day of week
    most_common_day = df['day_of_week'].mode()[0]

    # display the most common start hour
    most_common_start_time = df['Start Time'].dt.hour.mode()[0]

    print("Most frequent month is: ", most_common_month)
    print("Most frequent day is: ", most_common_day)
    print("Most frequent Starting Hour is: ", most_common_start_time)

    print("\nThis took %s seconds." % (time.time() - start_time))
    print('-'*40)

def station_stats(df):
    """Displays statistics on the most popular stations and trip."""

    print('\nCalculating The Most Popular Stations and Trip...\n')
    start_time = time.time()

    # display most commonly used start station
    most_common_start_station = df['Start Station'].mode()[0]

    # display most commonly used end station
    most_common_end_station = df['End Station'].mode()[0]

    # display most frequent combination of start station and end station trip and thier Count
    most_common_combo_station = df.groupby(['Start Station','End Station']).count().sort_values(['Start Time'], ascending=False).head(1)
    most_common_combo_station = most_common_combo_station.iloc[:,0:1]
    most_common_combo_station.columns = ['Count']

    print("Most Frequent Starting Station is: ", most_common_start_station)
    print("Most Frequent Ending Station is: ", most_common_end_station)
    print("Most frequent of combination of Starting and Ending Trip is: \n", most_common_combo_station)

    print("\nThis took %s seconds." % (time.time() - start_time))
    print('-'*40)

def trip_duration_stats(df):
    """Displays statistics on the total and average trip duration."""

    print('\nCalculating Trip Duration...\n')
    start_time = time.time()

    # display total travel time
    total_travel_time = df['Trip Duration'].sum()

    # display mean travel time
    mean_travel_time = df['Trip Duration'].describe()[4]
    #or# mean_travel_time = df['Trip Duration'].mean(axis = 0)

    print('Total Travel Time:',total_travel_time,'Seconds')
    print('Mean Travel Time:',mean_travel_time,'Seconds')

    print("\nThis took %s seconds." % (time.time() - start_time))
    print('-'*40)

def user_stats(df):
    """Displays statistics on bikeshare users."""

    print('\nCalculating User Stats...\n')
    start_time = time.time()

    # Display counts of user types
    count_user_type = df['User Type'].nunique()

    # Display counts of gender
    count_Gender = df['Gender'].nunique()

    # Display earliest, most recent, and most common year of birth
    #check if column exists in the DataFrame; one city doesn't have that.
    if 'Birth Year' in df.columns:
        earliest_birth_year = df['Birth Year'].describe()[6]
        most_recent_birth_year = df['Birth Year'].describe()[-1]
        most_common_birth_year = df['Birth Year'].value_counts().idxmax()
    else:
        print("The city you've selected doesn't contain Year of Birth Information")

    print('Count of User Types:',count_user_type,'Type(s)')
    print('Count of Genders:',count_Gender,'Gender(s)')
    print('Earlist birth year:',earliest_birth_year,'\nMost recent birth year',most_recent_birth_year,'\nMost common birth year:',most_common_birth_year)

    print("\nThis took %s seconds." % (time.time() - start_time))
    print('-'*40)

def main():
    while True:
        city, month, day = get_filters()

        df = load_data(city, month, day)

        time_stats(df)
        station_stats(df)
        trip_duration_stats(df)
        user_stats(df)

        #restart = input('\nWould you like to restart? Enter yes or no.\n')
        #Alternative script Restart Selction Yes/No/Display raw data
        questions = [
            {
                'type': 'list',
                'name': 'choice',
                'message': 'Would you like to restart?',
                'choices' : ['Yes','No','display some raw data'],
            }
        ]

        answers = prompt(questions)
        restart = answers.get("choice","")
        if restart.lower() == 'no':
            break
        ###Display 5 raw data at a time (Based on User's Selection & Filters selected)
        elif restart.lower() == 'display some raw data':
            raw_data_flag = True
            start_index = 0
            end_inedx = 5
            while raw_data_flag == True :
                print(df[start_index:end_inedx])

                questions = [
                    {
                        'type': 'list',
                        'name': 'choice',
                        'message': 'Do you want to Display more data?',
                        'choices' : ['Yes','No'],
                    }
                ]
                answers = prompt(questions)
                restart = answers.get("choice","")
                if restart.lower() == 'no':
                    raw_data_flag = False
                start_index += 5
                end_inedx += 5
            print('Happy Data Analysing! Good Bye :)')
            break
        ###

if __name__ == "__main__":
	main()


# Copyright (C) 2020 Simon Biggs

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import pathlib
import re
import subprocess
import textwrap

from pymedphys._imports import black, tomlkit

REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent.parent.parent
PYPROJECT_TOML_PATH = REPO_ROOT.joinpath("pyproject.toml")

LIBRARY_PATH = REPO_ROOT.joinpath("lib", "pymedphys")
DOCS_PATH = LIBRARY_PATH.joinpath("docs")

VERSION_PATH = LIBRARY_PATH.joinpath("_version.py")

DIST_DIR = REPO_ROOT.joinpath("dist")
SETUP_PY = REPO_ROOT.joinpath("setup.py")

REQUIREMENTS_TXT = REPO_ROOT.joinpath("requirements.txt")
REQUIREMENTS_DEV_TXT = REPO_ROOT.joinpath("requirements-dev.txt")
REQUIREMENTS_USER_TXT = REPO_ROOT.joinpath("requirements-deploy.txt")

ROOT_PYLINT = REPO_ROOT.joinpath(".pylintrc")
LIBRARY_PYLINT = LIBRARY_PATH.joinpath(".pylintrc")

ROOT_README = REPO_ROOT.joinpath("README.rst")
DOCS_README = DOCS_PATH.joinpath("README.rst")

ROOT_CHANGELOG = REPO_ROOT.joinpath("CHANGELOG.md")
DOCS_CHANGELOG = DOCS_PATH.joinpath("CHANGELOG.md")

AUTOGEN_MESSAGE = [
    "# DO NOT EDIT THIS FILE!",
    "# This file has been autogenerated by `pymedphys dev propagate`",
]

def propagate_all(_):
    propagate_version()
    propagate_extras()
    propagate_requirements()
    propagate_pylintrc()
    propagate_readme()
    propagate_changelog()

def read_pyproject():
    with open(PYPROJECT_TOML_PATH) as f:
        pyproject_contents = tomlkit.loads(f.read())

    return pyproject_contents

def get_version_string():
    pyproject_contents = read_pyproject()
    version_string = pyproject_contents["tool"]["poetry"]["version"]

    return version_string

def propagate_version():
    version_string = get_version_string()
    version_list = re.split(r"[-\.]", version_string)

    for i, item in enumerate(version_list):
        try:
            version_list[i] = int(item)
        except ValueError:
            pass

    version_contents = textwrap.dedent(
        f"""\
        {AUTOGEN_MESSAGE[0]}
        {AUTOGEN_MESSAGE[1]}

        version_info = {version_list}
        __version__ = "{version_string}"
        """
    )

    version_contents = black.format_str(version_contents, mode=black.FileMode())

    with open(VERSION_PATH, "w") as f:
        f.write(version_contents)

def copy_file_with_autogen_message(
    original_path, target_path, comment_syntax=("# ", "")
):
    with open(original_path) as f:
        original_contents = f.read()

    new_autogen = [
        comment_syntax[0] + original_autogen[2::] + comment_syntax[1]
        for original_autogen in AUTOGEN_MESSAGE
    ]

    contents_with_autogen_warning = "\n".join(new_autogen) + "\n\n" + original_contents

    with open(target_path, "w+") as f:
        f.write(contents_with_autogen_warning)

def propagate_pylintrc():
    copy_file_with_autogen_message(ROOT_PYLINT, LIBRARY_PYLINT)

def propagate_readme():
    copy_file_with_autogen_message(ROOT_README, DOCS_README, ("..\n    ", ""))

def propagate_changelog():
    copy_file_with_autogen_message(ROOT_CHANGELOG, DOCS_CHANGELOG, ("<!-- ", " -->"))

def propagate_requirements():
    subprocess.check_call("poetry update pymedphys", shell=True)

    # The docs are included within ``requirements.txt`` due to netlify
    # reading this file and spinning it up. The few extra dependencies
    # for users who choose to go this route isn't such a bad trade off
    # here.
    subprocess.check_call(
        "poetry export --without-hashes -E docs -E user -f requirements.txt --output requirements.txt",
        shell=True,
    )
    with open(REQUIREMENTS_TXT, "a") as f:
        f.write(".[user,docs]\n")

    subprocess.check_call(
        "poetry export --without-hashes -E dev -f requirements.txt --output requirements-dev.txt",
        shell=True,
    )
    with open(REQUIREMENTS_DEV_TXT, "a") as f:
        f.write(".[dev]\n")

    # TODO: Once the hashes pinning issue in poetry is fixed, remove the
    # --without-hashes. See <https://github.com/python-poetry/poetry/issues/1584>
    # for more details.
    subprocess.check_call(
        "poetry export --without-hashes -E user -E tests -f requirements.txt --output requirements-deploy.txt",
        shell=True,
    )

def propagate_extras():
    pyproject_contents = read_pyproject()

    deps = pyproject_contents["tool"]["poetry"]["dependencies"]

    extras = {}

    for key in deps:
        value = deps[key]
        comment = value.trivia.comment

        if comment.startswith("# groups"):
            split = comment.split("=")
            assert len(split) == 2
            groups = json.loads(split[-1])

            for group in groups:
                try:
                    extras[group].append(key)
                except KeyError:
                    extras[group] = [key]

    if pyproject_contents["tool"]["poetry"]["extras"] != extras:
        pyproject_contents["tool"]["poetry"]["extras"] = extras

        with open(PYPROJECT_TOML_PATH, "w") as f:
            f.write(tomlkit.dumps(pyproject_contents))


import numpy as np
import pandas as pd
import json
from datetime import datetime
from sklearn.covariance import LedoitWolf

class Preprocessing:

    # Initializer / Instance Attributes
    def __init__(self, path):
        if '.csv' in path:
            self.df        = pd.read_csv(path)
            # self.df.date   = [datetime.strptime(str(x)[0:8], '%Y%m%d') for x in self.df.date]
            # self.df.date   = pd.to_datetime(self.df.date).dt.strftime('%Y%m%d')
        else:
            self.df        = pd.read_excel(path)
            self.df.date   = pd.to_datetime(self.df.date).dt.strftime('%Y%m%d')
        self.nlargest      = {}
        self.trading_dates = np.sort(self.df.date.unique())

    def compute_nlargest(self, n):
        # Compute n largest stocks, based on market cap, for each day in the data
        self.nlargest   = self.df.groupby([pd.Grouper(key='date'), 'PERMNO'])['CAP'].sum()
        self.nlargest   = self.nlargest.groupby(level=0).nlargest(n).sort_index().reset_index(level=1, drop=True).to_frame()
        self.nlargest   = self.nlargest.groupby(level=0).apply(lambda nlargest: nlargest.xs(nlargest.name).to_dict()).to_dict()

    def save_nlargest(self):
        # Save dict with n largest stocks per date to json file
        with open('nlargest_caps.json', 'w') as fp:
            json.dump(self.nlargest, fp)

    def get_trading_interval(self, date, lookback_window):
        return self.trading_dates[self.trading_dates <= date][-lookback_window:]

    def get_reduced_data(self, date, lookback_window):
        # Get list that includes a dict with the largest n caps of that day
        nlargeststocks = [*self.nlargest[date].values()]
        nlargeststocks = [*nlargeststocks[0].keys()]
        trading_interval = self.get_trading_interval(date, lookback_window)
        # Return reduced data file (includies only relevant stocks for the relevant interval)
        return self.df[(self.df.PERMNO.isin(nlargeststocks)) & (self.df.date.isin(trading_interval))].reset_index(drop=True)

    def permnos_returns_caps_weights(self, date, lookback_window):
        raw = self.get_reduced_data(date=date, lookback_window=lookback_window)[['date','PERMNO', 'RET']]
        permnos = raw.PERMNO.unique()
        names = self.nlargest[date]['CAP']
        returns, caps, deleted = [], [], []
        for name in permnos:
            if len(np.array(raw.loc[raw.PERMNO == name, 'RET'].values)) == lookback_window:
                returns.append(np.array(raw.loc[raw.PERMNO == name, 'RET'].values))
                caps.append(names[name])
                market_weights = np.array(caps) / sum(caps)
            else:
                deleted.append(name)
        permnos = np.delete(permnos, deleted)
        return permnos, returns, caps, market_weights

    def means_historical(self, date, lookback_window):
        permnos, returns, _, market_weights, returns_ahead = self.permnos_returns_caps_weights(date, lookback_window)
        returns = np.matrix(returns)
        rows, _ = returns.shape
        exp_returns = np.array([])
        for r in range(rows):
            exp_returns = np.append(exp_returns, np.mean(returns[r]))
        covars = LedoitWolf().fit(np.transpose(returns)).covariance_
        #covars = np.cov(returns)
        #correls = np.corrcoef(returns)
        #self.exp_returns = (1 + self.exp_returns) ** 250 - 1  # 3months returns
        #self.covars = self.covars * 250  # 3months covariances
        return permnos, market_weights, exp_returns, covars, returns_ahead

    def return_summary(self, date, lookback_window):
        permnos, market_weights, exp_returns, covars, _ = self.means_historical(date, lookback_window)
        mean_weight_overview = pd.DataFrame({'Return': exp_returns, 'Market Weight': market_weights}, index=permnos).T
        cov_overview = pd.DataFrame(covars, columns=permnos, index=permnos)
        #cor_overview = pd.DataFrame(correls, columns=permnos, index=permnos)
        return mean_weight_overview, cov_overview


import numpy as np
import scipy.io as sio
import gym
from random import choice
from copy import deepcopy
import matplotlib.pyplot as plt

class Environment(object):
    """ each state is a 0-1 matrix,
        where 0 denotes obstacle, 1 denotes space"""

    def __init__(self, args):
        self.args = args
        self.hist_len = args.hist_len  # 4
        self.image_dim = args.image_dim  # 32
        self.state_beta_dim = args.state_dim  # 3
        self.image_padding = args.image_padding
        self.max_train_doms = args.max_train_doms  # 6400
        self.start_valid_dom = args.start_valid_dom  # 6400
        self.start_test_dom = args.start_test_dom  # 7200
        self.step_reward = args.step_reward
        self.collision_reward = args.collision_reward
        self.terminal_reward = args.terminal_reward
        self.move = np.array([[0, -1], [0, 1], [-1, 0], [1, 0]])  # North, South, West, East
        self.last_train_dom = -1
        self.border_start = self.image_padding + 1  # >= 1
        self.border_end = self.image_dim + self.image_padding - 2  # <= dim + pad - 2
        self.padded_state_shape = (self.image_dim + self.image_padding * 2, self.image_dim + self.image_padding * 2)
        self.state_alpha_dim = self.state_beta_dim + self.image_padding * 2
        self.pos_bias = np.array([self.image_padding, self.image_padding])
        self.load_data()

    def load_data(self):
        # ipdb.set_trace()
        data = sio.loadmat('data/gridworld_o_%d.mat' % self.image_dim)
        self.images = data['all_images']
        self.states_xy = data['all_states_xy_by_domain']
        self.max_domains = len(self.images)  # 8000
        self.preset_max_steps = {8: 38, 16: 86, 32: 178, 64: 246}
        if self.args.automax == 1:
            self.max_steps = self.image_dim * self.image_dim / 2
        elif self.args.automax == 2:
            self.max_steps = self.preset_max_steps[self.image_dim]
        else:
            self.max_steps = self.image_dim * self.args.max_steps

    def is_valid_pos(self, xy):
        # not in the border
        # return not (xy[0] >= self.image_dim-1 or xy[1] >= self.image_dim-1)
        return not (xy[0] > self.border_end or xy[0] < self.border_start or xy[1] > self.border_end or xy[
            1] < self.border_start)

    def restart(self, data_flag, init=False):
        # ipdb.set_trace()
        if data_flag == 'train':  # training
            init_dom = 0
            max_dom = self.max_train_doms
            if init:
                self.dom_ind = self.last_train_dom

        elif data_flag == 'valid':  # validation
            init_dom = self.start_valid_dom
            max_dom = self.start_test_dom
            if init:
                self.dom_ind = init_dom - 1

        else:  # testing
            init_dom = self.start_test_dom
            max_dom = self.max_domains
            if init:
                self.dom_ind = init_dom - 1

        invalid_flag = True
        pd = self.image_padding
        while invalid_flag:
            self.dom_ind += 1
            if self.dom_ind >= max_dom or self.dom_ind < init_dom:
                self.dom_ind = init_dom
            self.state = np.zeros(self.padded_state_shape, dtype=np.uint8)
            self.state[pd:-pd, pd:-pd] = self.images[self.dom_ind, 0]  # 32 * 32
            self.paths = self.states_xy[self.dom_ind, 0]
            for i in range(len(self.paths)):
                try:
                    self.a_xy = self.paths[i, 0][0] + self.pos_bias  # initial position of alpha
                    self.b_xy = self.paths[i, 0][-1] + self.pos_bias  # initial position of beta
                    self.min_steps = len(self.paths[i, 0]) / 2  # shortest path
                    # self.max_steps = len(self.paths[i, 0]) * self.args.max_steps
                except:
                    continue
                if self.is_valid_pos(self.a_xy) and self.is_valid_pos(self.b_xy):
                    invalid_flag = False
                    break

        self.terminal = False
        self.episode_reward = []
        self.states_alpha = np.zeros([1, self.hist_len, self.state_alpha_dim, self.state_alpha_dim], dtype=np.float32)
        self.states_beta = np.zeros([1, self.hist_len, self.state_beta_dim, self.state_beta_dim], dtype=np.float32)
        self.states_alpha[0, -1] = self.state[self.a_xy[0] - 1 - pd: self.a_xy[0] + 2 + pd,
                                   self.a_xy[1] - 1 - pd: self.a_xy[1] + 2 + pd]
        self.states_beta[0, -1] = self.state[self.b_xy[0] - 1: self.b_xy[0] + 2, self.b_xy[1] - 1: self.b_xy[1] + 2]

    def act(self, action, steps):
        act_a, act_b = divmod(action, 4)
        new_a_xy = self.a_xy + self.move[act_a]
        new_b_xy = self.b_xy + self.move[act_b]

        if self.is_valid_pos(new_a_xy) and self.state[new_a_xy[0], new_a_xy[1]] != 0:
            # not in the border and not obstacle
            self.a_xy = new_a_xy
            r_a = self.step_reward
        else:
            r_a = self.collision_reward

        if self.is_valid_pos(new_b_xy) and self.state[new_b_xy[0], new_b_xy[1]] != 0:
            self.b_xy = new_b_xy
            r_b = self.step_reward
        else:
            r_b = self.collision_reward

        # compute reward
        reward = r_a + r_b
        manhattan_distance = abs(sum(self.b_xy - self.a_xy))

        if self.args.use_instant_distance:
            r_ab = self.image_dim / (manhattan_distance + 1.0)
            reward += r_ab
        if manhattan_distance <= 1:
            reward += self.terminal_reward
        self.episode_reward.append([r_a, r_b, mahattan_distance])

        # terminal # distance = 0 or 1 means that alpha meets beta
        if manhattan_distance <= 1 or steps >= self.max_steps:
            self.terminal = True
        else:
            self.terminal = False

        # add current state to states history
        pd = self.image_padding
        self.states_alpha[0, : -1] = self.states_alpha[0, 1:]
        self.states_alpha[0, -1] = self.state[self.a_xy[0] - 1 - pd: self.a_xy[0] + 2 + pd,
                                   self.a_xy[1] - 1 - pd: self.a_xy[1] + 2 + pd]

        self.states_beta[0, : -1] = self.states_beta[0, 1:]
        self.states_beta[0, -1] = self.state[self.b_xy[0] - 1: self.b_xy[0] + 2, self.b_xy[1] - 1: self.b_xy[1] + 2]

        return reward

    def getState(self):
        return self.states_alpha, self.states_beta

    def isTerminal(self):
        return self.terminal

class MultiTaskEnvironment(Environment):
    def __init__(self, args, role="alpha"):
        super(MultiTaskEnvironment, self).__init__(args)
        # self.goal_x = (self.border_end - self.border_start) / 2
        # self.goal_y = (self.border_end - self.border_start) / 2
        self.target_xy = np.array([8, 8]).reshape((2))
        self.map = np.zeros(self.padded_state_shape, dtype=np.uint8)
        self.pd = self.image_padding
        self.map[self.pd:-self.pd, self.pd:-self.pd] = self.images[args.map_num, 0]
        self.task_list = []
        for k in range(5):
            self.paths = self.states_xy[args.map_num + k, 0]
            # self.task_list = []
            for i in range(len(self.paths)):
                try:
                    a_xy = self.paths[i, 0][0] + self.pos_bias
                    if self.is_valid_pos(a_xy):
                        self.task_list.append(a_xy)
                    a_xy = self.paths[i, 0][-1] = self.pos_bias
                    if self.is_valid_pos(a_xy):
                        self.task_list.append(a_xy)
                except:
                    continue
        self.task_num = len(self.task_list)
        self.a_task_list = self.task_list[0: int(self.task_num / 2)]
        self.b_task_list = self.task_list[int(self.task_num / 2): self.task_num]
        self.role = role
        # print(self.role)
        self.terminal = False
        self.episode_reward = []
        self.states_agent = np.zeros([1, self.hist_len, self.state_alpha_dim, self.state_alpha_dim], dtype=np.float32)

    def restart(self, data_flag, init=False):
        self.terminal = False
        if self.role == "alpha":
            self.a_xy = choice(self.a_task_list)
        else:
            self.a_xy = choice(self.b_task_list)
        self.episode_reward = []
        self.states_agent[0, -1] = self.map[self.a_xy[0] - 1 - self.pd: self.a_xy[0] + 2 + self.pd,
                                   self.a_xy[1] - 1 - self.pd: self.a_xy[1] + 2 + self.pd]

    def act(self, action, steps):
        new_a_xy = self.a_xy + self.move[action]
        if self.is_valid_pos(new_a_xy) and self.map[new_a_xy[0], new_a_xy[1]] != 0:
            self.a_xy = new_a_xy
            reward = self.step_reward
        else:
            reward = self.collision_reward
        self.episode_reward.append(reward)
        manhatten_distance = abs(sum(self.target_xy - self.a_xy))
        distance = np.linalg.norm(self.target_xy - self.a_xy)
        if self.args.use_instant_distance:
            reward += self.image_dim / (manhatten_distance + 1.0)
        if distance <= 1:
            reward += self.terminal_reward
        self.episode_reward.append(reward)
        if distance <= 1 or steps >= self.max_steps:
            self.terminal = True
        else:
            self.terminal = False
        self.states_agent[0, : -1] = self.states_agent[0, 1:]
        self.states_agent[0, -1] = self.map[self.a_xy[0] - 1 - self.pd: self.a_xy[0] + 2 + self.pd,
                                   self.a_xy[1] - 1 - self.pd: self.a_xy[1] + 2 + self.pd]
        return reward

    def getState(self):
        return deepcopy(self.states_agent)

    def isTerminal(self):
        return deepcopy(self.terminal)

class OptimalRewardTestTask(object):
    def __init__(self):
        super(OptimalRewardTestTask, self).__init__()
        self.move = np.array([[0, -1], [0, 1], [-1, 0], [1, 0]])
        self.map = np.zeros((4, 4))
        self.a_xy = np.array([0, 0], dtype=int)
        self.b_xy = np.array([3, 3], dtype=int)
        self.states_alpha = deepcopy(self.a_xy)
        self.states_beta = deepcopy(self.b_xy)
        self.joint_state = np.concatenate([self.states_alpha, self.states_beta])
        self.terminal = False
        self.step_num = 0
        self.max_step_num = 80

    def _is_valid_pos(self, xy):
        # not in the border
        # return not (xy[0] >= self.image_dim-1 or xy[1] >= self.image_dim-1)
        return not (xy[0] > 3 or xy[0] < 0 or xy[1] > 3 or xy[1] < 0)

    def restart(self):
        self.map = np.zeros((4, 4))
        self.a_xy = np.array([0, 0], dtype=int)
        self.b_xy = np.array([3, 3], dtype=int)
        self.states_alpha = deepcopy(self.a_xy)
        self.states_beta = deepcopy(self.b_xy)
        self.joint_state = np.concatenate([self.states_alpha, self.states_beta])
        self.terminal = False
        self.step_num = 0

    def act(self, action):
        act_a, act_b = divmod(action, 4)
        new_a_xy = self.a_xy + self.move[act_a]
        new_b_xy = self.b_xy + self.move[act_b]
        if self._is_valid_pos(new_a_xy):
            self.a_xy = new_a_xy
        if self._is_valid_pos(new_b_xy):
            self.b_xy = new_b_xy
        r_a = -1
        r_b = -1
        # reward = (r_a + r_b) / 2
        manhattan_distance = sum(abs(self.b_xy - self.a_xy))
        reward = -manhattan_distance
        if manhattan_distance <= 1:
            self.terminal = True
        else:
            self.terminal = False
        self.step_num += 1
        if self.step_num >= self.max_step_num:
            self.terminal = True
        self.states_alpha = deepcopy(self.a_xy)
        self.states_beta = deepcopy(self.b_xy)
        self.joint_state = np.concatenate([self.states_alpha, self.states_beta])
        return reward

    def getState(self):
        return self.states_alpha, self.states_beta, self.joint_state

    def isTerminal(self):
        return self.terminal

map = np.zeros((4, 4))
plt.pcolormesh(map, cmap=plt.cm.gray, vmin=-2, vmax=1, edgecolors="k", linewidth=2)
point_list = []
plt.scatter(x=0, y=0, s=180, marker=".", c="red", linewidths=15)
plt.scatter(x=3, y=3, s=180, marker=".", c="blue", linewidths=15)

plt.xlim((-1, 5))
plt.ylim((-1, 5))
plt.savefig("./map.pdf", format="pdf", dpi=1000)


import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

import settings
import image
import logger

LOGGER = logger.Logger(name=__name__).get()

def _read_remote_webdriver_key():
    data = settings.DEFAULT.webdriver
    return str.format('{}:{}', data['user'], data['key'])

def setup_webdriver(remote=True, capabilities=None):
    if remote:
        if not capabilities:
            raise ValueError("capabilities must be defined for remote runs!")

        remote_webdriver_url = "http://{}@{}".format(
            _read_remote_webdriver_key(), settings.DEFAULT.webdriver['url'])
        driver = webdriver.Remote(
            command_executor=remote_webdriver_url,
            desired_capabilities=capabilities)
    else:
        driver = webdriver.Firefox()

    driver.implicitly_wait(20)
    driver.get("about:blank")
    return driver

def wait_until_element_disappears(driver, locator):
    WebDriverWait(driver, 20).until(
        EC.invisibility_of_element_located(locator=locator))

def display_tag(driver, tag):
    script = _make_script(tag)
    driver.execute_script(script)

    # Wait until the load spinner goes away
    load_spinner_locator = (By.CSS_SELECTOR, "img[class*='pl-loader-'")
    wait_until_element_disappears(driver=driver, locator=load_spinner_locator)
    time.sleep(5)  # For good measure

def _make_script(tag):
    script = "document.body.innerHTML=\"{}\";".format(tag) \
        .replace('\n', '').replace('\r', '').rstrip()
    LOGGER.debug("_make_script: %s", script)
    return script

def screenshot_element(driver, element, output_path):
    """Take a screenshot of a specific webelement
    """
    size = element.size
    location = element.location

    left = location['x']
    top = location['y']
    right = location['x'] + size['width']
    bottom = location['y'] + size['height']
    cropbox = (left, top, right, bottom)

    f = _screenshot(driver, output_path)
    img = image.crop(f, cropbox)
    return img

def _screenshot(driver, output_path):
    if not output_path.endswith('.png'):
        output_path += ".png"
    LOGGER.debug("capturing screenshot to %s", output_path)
    driver.get_screenshot_as_file(output_path)
    return output_path


"""
Support for getting statistical data from a Pi-hole system.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.pi_hole/
"""
from datetime import timedelta
import logging

import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
    CONF_HOST, CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_SSL, CONF_VERIFY_SSL)
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle

REQUIREMENTS = ['hole==0.3.0']

_LOGGER = logging.getLogger(__name__)

ATTR_BLOCKED_DOMAINS = 'domains_blocked'
ATTR_PERCENTAGE_TODAY = 'percentage_today'
ATTR_QUERIES_TODAY = 'queries_today'

CONF_LOCATION = 'location'
DEFAULT_HOST = 'localhost'

DEFAULT_LOCATION = 'admin'
DEFAULT_METHOD = 'GET'
DEFAULT_NAME = 'Pi-Hole'
DEFAULT_SSL = False
DEFAULT_VERIFY_SSL = True

MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)

MONITORED_CONDITIONS = {
    'ads_blocked_today':
        ['Ads Blocked Today', 'ads', 'mdi:close-octagon-outline'],
    'ads_percentage_today':
        ['Ads Percentage Blocked Today', '%', 'mdi:close-octagon-outline'],
    'clients_ever_seen':
        ['Seen Clients', 'clients', 'mdi:account-outline'],
    'dns_queries_today':
        ['DNS Queries Today', 'queries', 'mdi:comment-question-outline'],
    'domains_being_blocked':
        ['Domains Blocked', 'domains', 'mdi:block-helper'],
    'queries_cached':
        ['DNS Queries Cached', 'queries', 'mdi:comment-question-outline'],
    'queries_forwarded':
        ['DNS Queries Forwarded', 'queries', 'mdi:comment-question-outline'],
    'unique_clients':
        ['DNS Unique Clients', 'clients', 'mdi:account-outline'],
    'unique_domains':
        ['DNS Unique Domains', 'domains', 'mdi:domain'],
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
    vol.Optional(CONF_LOCATION, default=DEFAULT_LOCATION): cv.string,
    vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
    vol.Optional(CONF_MONITORED_CONDITIONS,
                 default=['ads_blocked_today']):
    vol.All(cv.ensure_list, [vol.In(MONITORED_CONDITIONS)]),
})

async def async_setup_platform(
        hass, config, async_add_entities, discovery_info=None):
    """Set up the Pi-hole sensor."""
    from hole import Hole

    name = config.get(CONF_NAME)
    host = config.get(CONF_HOST)
    use_tls = config.get(CONF_SSL)
    location = config.get(CONF_LOCATION)
    verify_tls = config.get(CONF_VERIFY_SSL)

    session = async_get_clientsession(hass, verify_tls)
    pi_hole = PiHoleData(Hole(
        host, hass.loop, session, location=location, tls=use_tls))

    await pi_hole.async_update()

    if pi_hole.api.data is None:
        raise PlatformNotReady

    sensors = [PiHoleSensor(pi_hole, name, condition)
               for condition in config[CONF_MONITORED_CONDITIONS]]

    async_add_entities(sensors, True)

class PiHoleSensor(Entity):
    """Representation of a Pi-hole sensor."""

    def __init__(self, pi_hole, name, condition):
        """Initialize a Pi-hole sensor."""
        self.pi_hole = pi_hole
        self._name = name
        self._condition = condition

        variable_info = MONITORED_CONDITIONS[condition]
        self._condition_name = variable_info[0]
        self._unit_of_measurement = variable_info[1]
        self._icon = variable_info[2]
        self.data = {}

    @property
    def name(self):
        """Return the name of the sensor."""
        return "{} {}".format(self._name, self._condition_name)

    @property
    def icon(self):
        """Icon to use in the frontend, if any."""
        return self._icon

    @property
    def unit_of_measurement(self):
        """Return the unit the value is expressed in."""
        return self._unit_of_measurement

    @property
    def state(self):
        """Return the state of the device."""
        try:
            return round(self.data[self._condition], 2)
        except TypeError:
            return self.data[self._condition]

    @property
    def device_state_attributes(self):
        """Return the state attributes of the Pi-Hole."""
        return {
            ATTR_BLOCKED_DOMAINS: self.data['domains_being_blocked'],
        }

    @property
    def available(self):
        """Could the device be accessed during the last update call."""
        return self.pi_hole.available

    async def async_update(self):
        """Get the latest data from the Pi-hole API."""
        await self.pi_hole.async_update()
        self.data = self.pi_hole.api.data

class PiHoleData:
    """Get the latest data and update the states."""

    def __init__(self, api):
        """Initialize the data object."""
        self.api = api
        self.available = True

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    async def async_update(self):
        """Get the latest data from the Pi-hole."""
        from hole.exceptions import HoleError

        try:
            await self.api.get_data()
            self.available = True
        except HoleError:
            _LOGGER.error("Unable to fetch data from Pi-hole")
            self.available = False


from gtts import gTTS
import speech_recognition as sr
import os
import time
import webbrowser
def talk(audio):
	tts=gTTS(text=audio,lang ="en" )
	tts.save("file1.mp3")
	os.system("start file1.mp3")
def listenForCmd():

	r = sr.Recognizer()

	with sr.Microphone() as source:
		print("Hello. i am SASHA. i'm online and ready")
		r.adjust_for_ambient_noise(source, duration = 1)
		audio = r.listen(source)
	try:
		command = r.recognize_google(audio)
		print(command)

	except sr.UnknownValueError:
		print("the mic isnt working right... i didnt hear any command")
		command =listenForCmd()

	return command
command = listenForCmd()
def cmd():
	if "hey" in command:
		os.system('start sounds/Bidding.mp3')
talk("ready when you are")
while True:
	listenForCmd()
	cmd()
	time.sleep(3)


# -*- coding: utf-8 -*-
"""
Created on Thu May  7 16:15:53 2020

@author: Tom
"""

import os
import cv2
import imageio
import numpy as np
'''
import tensorflow as tf
from network import pyramid_processing
from data_augmentation import flow_resize
from config.extract_config import config_dict
from flowlib import flow_to_color
from utils import mvn

#--------------------------------------------------------------------------

config = config_dict('./config/config.ini')
restore_model = config['test']['restore_model']

b_img0 = tf.placeholder(tf.float32, shape=(1, None, None, 3))
b_img1 = tf.placeholder(tf.float32, shape=(1, None, None, 3))
b_img2 = tf.placeholder(tf.float32, shape=(1, None, None, 3))

#b_img0 = tf.placeholder(tf.float32, shape=(1, 384, 512, 3))
#b_img1 = tf.placeholder(tf.float32, shape=(1, 384, 512, 3))
#b_img2 = tf.placeholder(tf.float32, shape=(1, 384, 512, 3))

b_img0 = mvn(b_img0)
b_img1 = mvn(b_img1)
b_img2 = mvn(b_img2)

img_shape = tf.shape(b_img0)
h = img_shape[1]
w = img_shape[2]

new_h = tf.where(tf.equal(tf.mod(h, 64), 0), h, (tf.to_int32(tf.floor(h / 64) + 1)) * 64)
new_w = tf.where(tf.equal(tf.mod(w, 64), 0), w, (tf.to_int32(tf.floor(w / 64) + 1)) * 64)

batch_img0 = tf.image.resize_images(b_img0, [new_h, new_w], method=1, align_corners=True)
batch_img1 = tf.image.resize_images(b_img1, [new_h, new_w], method=1, align_corners=True)
batch_img2 = tf.image.resize_images(b_img2, [new_h, new_w], method=1, align_corners=True)

flow_fw, flow_bw = pyramid_processing(batch_img0, batch_img1, batch_img2, train=False, trainable=False, is_scale=True)
flow_fw['full_res'] = flow_resize(flow_fw['full_res'], [h, w], method=1)
flow_bw['full_res'] = flow_resize(flow_bw['full_res'], [h, w], method=1)

flow_fw_color = flow_to_color(flow_fw['full_res'], mask=None, max_flow=256)
flow_bw_color = flow_to_color(flow_bw['full_res'], mask=None, max_flow=256)

restore_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
saver = tf.train.Saver(var_list=restore_vars)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
saver.restore(sess, restore_model)

def fb_check(w_warp, w_back): #optimizable? cuda?

  weights = np.ones(w_warp.shape[:2])

  norm_wb = np.linalg.norm(w_warp + w_back, axis=2)**2.0
  norm_w = np.linalg.norm(w_warp, axis=2)**2.0
  norm_b = np.linalg.norm(w_back, axis=2)**2.0

  disoccluded_regions = norm_wb > 0.01*(norm_w + norm_b) + 0.5

  norm_u = np.linalg.norm(np.gradient(w_back[...,0]), axis=0)**2.0
  norm_v = np.linalg.norm(np.gradient(w_back[...,1]), axis=0)**2.0

  motion_boundaries = norm_u + norm_v > 0.01*norm_b + 0.002

  weights[np.where(disoccluded_regions)] = 0
  weights[np.where(motion_boundaries)] = 0

  return weights

def warp_flow(A, flow):

  assert A.shape[-3:-1] == flow.shape[-3:-1], "dimension error: input and \
                                               flow size do not match"
  h, w = flow.shape[:2]
  x = (flow[...,0] + np.arange(w)).astype(A.dtype)
  y = (flow[...,1] + np.arange(h)[:,np.newaxis]).astype(A.dtype)

  return cv2.remap(A, x, y, cv2.INTER_LINEAR)

def warp_image(A, flow):

  #A_m = np.moveaxis(A, 0, 2)
  #flow = np.moveaxis(flow, 0, 2)
  A_m = A

  #print('A', A.shape)
  #print('flow', flow.shape)

  #print(A_m.shape[-3:-1], flow.shape[-3:-1])
  #print(A_m.dtype, flow.dtype)
  assert A_m.shape[-3:-1] == flow.shape[-3:-1], "dimension error: input and flow size do not match"
  h, w = flow.shape[:2]
  x = (flow[...,0] + np.arange(w)).astype(A.dtype)
  y = (flow[...,1] + np.arange(h)[:,np.newaxis]).astype(A.dtype)

  W_m = cv2.remap(A_m, x, y, cv2.INTER_LINEAR)

  return W_m.reshape(A.shape)
'''
#==============================================================================

#sintel_video = 'alley_2'
#sintel_video = 'market_6'
sintel_video = 'temple_2'
out_path = 'D:/Datasets/FC5/' + sintel_video + '/'
sintel_path = 'D:/Datasets/MPI-Sintel-complete/training/'

frame_path = sintel_path + 'final/' + sintel_video + '/'
flow_path = sintel_path + 'flow/' + sintel_video + '/'
mask_path = sintel_path + 'occlusions/' + sintel_video + '/'

if not os.path.exists(out_path):
  os.makedirs(out_path)

if not os.path.exists(frame_path):
  print('invalid path')

frame_list = os.listdir(frame_path)
flow_list = os.listdir(flow_path)
mask_list = os.listdir(mask_path)

frame_list.sort(reverse=True)
flow_list.sort(reverse=True)
mask_list.sort(reverse=True)

long_term_length = 5

for i in range(len(frame_list)):

  if i - long_term_length - 1 < 0:
    continue
  #print(frame_list[i])
  #blah
  img3 = imageio.imread(frame_path + frame_list[i])
  img2 = imageio.imread(frame_path + frame_list[i-1])
  img1 = imageio.imread(frame_path + frame_list[i-long_term_length])
  img0 = imageio.imread(frame_path + frame_list[i-long_term_length-1])
  print(frame_list[i-long_term_length-1], i-long_term_length-1)#49
  print(frame_list[i-long_term_length], i-long_term_length)#48
  print(frame_list[i-1], i-1)
  print(frame_list[i], i)
  blah

  #imageio.imwrite(out_path + 'img0.png', img0)
  #imageio.imwrite(out_path + 'img1.png', img1)
  #imageio.imwrite(out_path + 'img2.png', img2)
  #imageio.imwrite(out_path + 'img3.png', img3)

  img0 = np.float32(img0.reshape((1,) + img0.shape)/255.0)
  img1 = np.float32(img1.reshape((1,) + img1.shape)/255.0)
  img2 = np.float32(img2.reshape((1,) + img2.shape)/255.0)
  img3 = np.float32(img3.reshape((1,) + img3.shape)/255.0)

  ff = sess.run([flow_fw['full_res']], feed_dict={b_img0:img0, b_img1:img1, b_img2:img2})[0]
  bf = sess.run([flow_bw['full_res']], feed_dict={b_img0:img1, b_img1:img2, b_img2:img3})[0]

  wf = warp_flow(ff[0], bf[0])
  mask = fb_check(wf, bf[0])

  #test_warp = warp_image(img2[0], ff[0])
  #test_warp2 = warp_image(img1[0], bf[0])

  #imageio.imwrite(out_path + 'test_mask.png', mask)
  #imageio.imwrite(out_path + 'test_warp1.png', test_warp)
  #imageio.imwrite(out_path + 'test_warp2.png', test_warp2)

  mask = mask.reshape((1,) + mask.shape + (1,))

  batch_data = np.concatenate([bf, mask], axis=3)

  print(out_path + frame_list[i][:10] + '.npy')
  #blah
  np.save(out_path + frame_list[i][:10] + '.npy', batch_data)


# Copyright (c) 2016, Mayo Clinic
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
#     list of conditions and the following disclaimer.
#
#     Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions and the following disclaimer in the documentation
#     and/or other materials provided with the distribution.
#
#     Neither the name of the Mayo Clinic nor the names of its contributors
#     may be used to endorse or promote products derived from this software
#     without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.
from io import TextIOWrapper
from jsonasobj import load

class TransformationContext:
    """
    A Transformation Context consists of:
        Element                 Type        Description
        --------                ----------  -----------
        MODULE                  int         SCTID of the module being transformed to owl
        VERSION                 int         VERSION identifier of particular release or extension
        VERSION_DESCRIPTION     str         A description of the version
        LANGUAGES               List[int]   A list of language refset SCTIDS
        LANGUAGE_MAP            Map[int, str] A map from a language refset to an english language description
        MODULE_LABEL            str         The label of the module/ontology
        MODULE_DESCRIPTION      str         A description of the module
        MODULE_COPYRIGHT        str         The module copyright
        NEVER_GROUPED           List[int]   A list of SCTIDS that never occur inside relationship groups
        RIGHT_ID                Map[int, List[int]]
        USE_STATED_RELATIONSHIPS bool   True or absent means use the stated relationships, else use inferred
    """
    def __init__(self, context_config: TextIOWrapper):
        ctxt = load(context_config)
        self.MODULE = ctxt.MODULE
        self.VERSION = ctxt.VERSION
        self.VERSION_DESCRIPTION = self.multi_line(ctxt.VERSION_DESCRIPTION)
        self.LANGUAGES = ctxt.LANGUAGES
        self.LANGUAGE_MAP = {int(k): v for k, v in ctxt.LANGUAGE_MAP._as_dict.items()}
        self.MODULE_LABEL = ctxt.MODULE_LABEL
        self.MODULE_DESCRIPTION = self.multi_line(ctxt.MODULE_DESCRIPTION)
        self.MODULE_COPYRIGHT = self.multi_line(ctxt.MODULE_COPYRIGHT)
        self.NEVER_GROUPED = ctxt.NEVER_GROUPED
        self.RIGHT_ID = {int(k): v for k, v in ctxt.RIGHT_ID._as_dict.items()}
        self.USE_STATED_RELATIONSHIPS = ctxt._as_dict.get("USE_STATED_RELATIONSHIPS", True)

    @staticmethod
    def multi_line(txt):
        return '\n'.join(txt) if isinstance(txt, list) else txt


from typing import List

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        out = []
        # k is width of each combination
        # n is range of combinations.
        current = []
        def helper(depth: int) -> None:
            if depth == k:
                out.append(current[:])
                return
            if len(current) != 0 and current[-1] == n:
                return
            min_n = 1 if len(current) == 0 else current[-1] + 1
            for each_n in range(min_n, n+1):
                current.append(each_n)
                helper(depth + 1)
                current.pop()
        helper(0)
        return out

n = 4
k = 2
print(Solution().combine(n,k))


import constants
import random
import eel
from threading import Thread

class Player():
    def __init__(self, name, sheet, coz):
        self.cards = []
        self.name = name
        self.who_move = 0
        self.decisions = {0: 'attack', 1: 'defence', 2: 'take', 3: 'wait', 4: 'end_move'}
        self.end_move = False
        self.awaiting = False
        self.first_attack_move = False
        self.sheet = sheet
        self.coz = coz
        self.take_card=False
        self.plant=[]

    def get_card(self, card: list):
        while (self.cards.__len__() < 6):
            self.cards.append(card.pop())

    def wait_an(self):
        pass

    def add_after_take(self,board):
        av_to_attack = []
        values = []
        at = board['attack'].copy()
        de = board['defence'].copy()
        all = at + de
        for i in all:
            values.append(i)
        values = set(values)
        for i in values:
            for j in self.cards:
                if (j.__contains__(i[1:])):
                    av_to_attack.append(j)
        av_to_attack = list(set(av_to_attack))
        if(len(av_to_attack)==0):return
        print(self.name, ' attack', av_to_attack,self.cards)
        def two_more_cards(att):  # проверка на законность атаковать 2+ картами одновременно
            b = True
            f = att[0][1:]
            for i in att:
                if (not i[1:] == f):
                    b = False
                    break
            return b
        c=input('Добавить карты сопернику ').split(' ')

        if (c.__len__() > 1):
            if (two_more_cards(c)):
                self.plant+=c
            else:
                print('Разные значения карт')
        elif (c.__len__() == 1):
            self.plant+= c
        else:
            print('Вы не ввели карты')

    def make_move(self, board: dict, move_style):
        if (move_style == 0):
            self.attack(board)
        if (move_style == 1):
            self.defence(board, self.sheet, self.coz)

    def take(self, board):
        for i in board['attack'] + board['defence']:
            self.cards.append(i)
        # board['attack'].clear()
        # board['defence'].clear()
        self.end_move = True
        self.take_card=True

    def defence(self, board, sheet, coz):
        av_defence = {}
        CAN_BEAT = True

        def can_beat(a, b) -> bool:
            if ((a[0] == b[0] or a[0] == coz) and (sheet[a] > sheet[b])):
                # print(a, b, sheet[a], sheet[b])
                return True
            else:
                return False

        already_beat = board['defence'].__len__()
        num = 1
        for i in board['attack']:
            # print('here',num,already_beat)
            if (num <= already_beat):
                num += 1
                continue
            av_defence[i] = []
            num += 1
            for j in self.cards:
                if (can_beat(j, i)):
                    av_defence[i].append(j)
            if (av_defence[i].__len__() == 0):
                CAN_BEAT = False
                break
        print(self.name,'defence')
        print(av_defence)

        if (CAN_BEAT):
            t=input('Взять или бить?(t or d) ')
            if(t=='d'):
                for i in av_defence.keys():
                    de = ''
                    res=False
                    eel.set_def_false()()
                    while(not res):
                        a = eel.choose_def_card_js(i, av_defence[i])()
                        res=a[0]
                        de=a[1]
                        print('in defence',res)
                        eel.sleep(0.05)
                    eel.remove_css_def();
                    eel.sleep(0.01);

                    if (de in av_defence[i]):
                        board['defence'].append(de)
                        self.cards.pop(self.cards.index(de))
                self.end_move = True
            else:
                self.take(board)
        else:
            self.take(board)
        print(board)

    def attack(self, board):
        av_to_attack = []

        def two_more_cards(att):  # проверка на законность атаковать 2+ картами одновременно
            b = True
            f = att[0][1:]
            for i in att:
                if (not i[1:] == f):
                    b = False
                    break
            return b

        if (board['attack'].__len__() == 0 and board['defence'].__len__() == 0):
            av_to_attack.clear()
            av_to_attack = [i for i in self.cards]
        else:
            av_to_attack.clear()
            values = []
            at = board['attack'].copy()
            de = board['defence'].copy()
            all = at + de
            # print('all', all)
            for i in all:
                values.append(i)
            values = set(values)
            for i in values:
                for j in self.cards:
                    if (j.__contains__(i[1:])):
                        av_to_attack.append(j)
            av_to_attack = list(set(av_to_attack))
        print(self.name, ' attack', av_to_attack)
        eel.highlight_av_att_cards_js(av_to_attack)
        eel.sleep(1)
        if(not self.first_attack_move and len(board['attack'])==0 and len(board['defence'])==0):# подбрасывать карты когда соперник взял
            c = input("Выберите какие карты подбросить").split(' ')
            self.plant=c
            self.end_move = True
            self.awaiting = False
            return

        if (not self.first_attack_move):
            dec = input('Закончить ход? ')
            if (dec == 'y'):
                self.end_move = True
                self.awaiting=False
                print(*zip(board['attack'],board['defence']))
                eel.add_out(*zip(board['attack'],board['defence']))
                return
            if (dec == 'n'):
                self.awaiting = True
        else:
            self.awaiting = True
        if (self.first_attack_move): self.first_attack_move = False

        # c = input("Выберите карты для атаки ").split(' ')
        print(av_to_attack)
        res=True
        c=[]
        eel.set_res_true()()
        while(res):# выбор карт для атаки, ждем пока не нажмёт кнопку end move
            res,c=eel.choose_att_card_js(av_to_attack)()
            eel.sleep(1)
        # c = eel.end_move_js(av_to_attack)()
        print('c', c)
        if (c.__len__() > 1):
            if (two_more_cards(c)):
                board['attack'] = board['attack'] + c
                for i in c:
                    self.cards.pop(self.cards.index(i))
            else:
                print('Разные значения карт')
        elif (c.__len__() == 1):
            board['attack'] = board['attack'] + c
            for i in c:
                self.cards.pop(self.cards.index(i))
        else:
            print('Вы не ввели карты')

class Game():
    def create_sheet(self, cards, coz):
        sheet = {}
        for i in cards:
            point = 0
            if (i[1:] == 'T'):
                point = 14
            elif (i[1:] == 'J'):
                point = 11
            elif (i[1:] == 'Q'):
                point = 12
            elif (i[1:] == 'K'):
                point = 13
            else:
                point = int(i[1:])
            if (i[0] == coz):
                point += 100
            sheet[i] = point
        return sheet

    def get_sheet(self):
        return self.sheet

    def __init__(self):
        random.seed(3)
        self.start_cards = constants.cards
        random.shuffle(self.start_cards)
        self.coz = self.start_cards[0][0]
        self.who_first = 0
        self.who_move = 0
        self.move_number = 1
        self.game_over = False
        self.move_style = 0
        self.board = {'attack': [], 'defence': []}
        self.sheet = self.create_sheet(self.start_cards, self.coz)
        self.plant_board=[]

    def give_start_cards(self, p1: Player, p2: Player):
        while (len(p1.cards) < 6 and len(p2.cards) < 6):
            p1.cards.append(self.start_cards.pop())
            p2.cards.append(self.start_cards.pop())
            p1.who_move = 1
            p2.who_move = 2

    def board_clear(self):
        for i in self.board.keys():
            self.board[i].clear()

    def reset(self,p1:Player,p2:Player):
        p1.end_move = False
        p1.awaiting = False
        p1.first_attack_move = False
        p1.take_card=False
        p2.end_move = False
        p2.awaiting = False
        p2.first_attack_move = False
        p2.take_card=False
        p1.plant.clear()
        p2.plant.clear()
        self.board_clear()

    def move(self,game, p1: Player, p2: Player):
        self.reset(p1,p2)

        if (self.move_number % 2 == 1):
            self.who_move = 1
        else:
            self.who_move = 2
        if (self.who_move == 1):
            p1.first_attack_move = True
            while (not p1.end_move):
                if(p2.take_card==True):
                    print('p2 take card')
                    p1.add_after_take(self.board)
                    p1.end_move=True
                    continue
                p1.make_move(self.board, 0)  # attack
                print('board:', self.board['attack'], self.board['defence'])
                if (p1.awaiting):
                    print('wait')
                    p2.end_move = False
                    while (not p2.end_move):
                        p2.make_move(self.board, 1)  # defence
        else:
            p2.first_attack_move = True
            while (not p2.end_move):
                if (p1.take_card == True):
                    print('p1 take card')
                    p2.add_after_take(self.board)
                    p2.end_move = True
                    continue
                p2.make_move(self.board, 0)  # attack
                print('board:', self.board['attack'], self.board['defence'])
                if (p2.awaiting):
                    p1.end_move = False
                    while (not p1.end_move):
                        p1.make_move(self.board, 1)  # defence
        print('plant',p1.plant,p2.plant)
        if(len(p1.plant)>0):#подкидывание  карт когда противник взял
            for i in p1.plant:
                p2.cards.append(p1.cards.pop(p1.cards.index(i)))
        elif(len(p2.plant)>0):
            for i in p2.plant:
                p1.cards.append(p2.cards.pop(p2.cards.index(i)))

        if ((p1.cards.__len__() == 0 or p2.cards.__len__() == 0) and self.start_cards.__len__() == 0):
            game.game_over = True
        else:
            if(self.who_move==1 and not p1.take_card):
                p1.get_card(self.start_cards)
                p2.get_card(self.start_cards)
            else:
                p2.get_card(self.start_cards)
                p1.get_card(self.start_cards)
        print(p1.take_card,p2.take_card)
        if(p1.take_card or p2.take_card):
            self.move_number += 2
        else:
            self.move_number+=1

    def move_is_available(self) -> bool:
        pass

def game_start():
    game = Game()
    p1 = Player('Nick', game.sheet, game.coz)
    p2 = Player('Anna', game.sheet, game.coz)
    print(game.start_cards)
    game.give_start_cards(p1, p2)
    eel.start_hands_js(p1.cards,p2.cards)
    eel.sleep(3)

    print('coz', game.coz, '\np1', p1.cards, 'p2\n', p2.cards, '\n', '#' * 30)
    while (not game.game_over):

        eel.coloda_js(game.start_cards)
        eel.sleep(1)

        print('Ход № ',game.move_number)
        print('p1 ', p1.cards, '\n','p2', p2.cards, '\n',game.start_cards,game.start_cards.__len__(),'\n', '#' * 30)
        game.move(game,p1, p2)
        print('#'*50)


import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection

def traveling_map_plotter(map):
    fig, ax = plt.subplots(dpi=150)
    x = [t[0] for t in map.city_list]
    y = [t[1] for t in map.city_list]

    edges = LineCollection(map.get_map(), linewidths=1)

    ax.scatter(x, y, s=0.5, c='g')
    ax.add_collection(edges)
    ax.set_title("Travelling salesman map")

    plt.show()