import random, collections
import numpy as np
import torch
from torch import nn
import torch.nn.functional as F

class ReplayBuffer:
    """经验回放池"""

    def __init__(self, capacity) -> None:
        self.buffer = collections.deque(maxlen=capacity)  # 队列，先进先出
        self.change_buffer = collections.deque(maxlen=capacity//10)
        self.tmp_buffer = collections.deque(maxlen=10)
        self.number = 0
        # self.all_buffer = np.zeros((1000000, 66), dtype=np.float32)
        # with open('./out/replay_buffer_test.txt', 'w') as f:
        #     pass

    def add(self, state, action, reward, next_state, truncated, done, info):
        # first compress state info, then add
        state = self._compress(state)
        next_state = self._compress(next_state)
        self.tmp_buffer.append((state, action, reward, next_state, truncated, done))
        lane_center = info["offlane"]
        reward_ttc = info["TTC"]
        if reward_ttc < -0.1:
            self.change_buffer.append((state, action, reward, next_state, truncated, done))
        if lane_center > 1.0:
            self.change_buffer.append((state, action, reward, next_state, truncated, done))
        if abs(info['lane_changing_reward']) > 0.1:
            for buf in self.tmp_buffer:
                self.change_buffer.append(buf)
        self.buffer.append((state, action, reward, next_state, truncated, done))
        reward_com = info["Comfort"]

        reward_eff = info["velocity"]
        reward_yaw = info["yaw_diff"]
        # reward_list = np.array([[reward, reward_ttc, reward_com, reward_eff, reward_lan, reward_yaw]])
        print("reward_eff: ", reward_eff)
        # print("their shapes", state, action, next_state, reward_list, truncated, done)
        # state: [1, 28], action: [1, 2], next_state: [1, 28], reward_list = [1, 6], truncated = [1, 1], done = [1, 1]
        # all: [1, 66]
        if truncated == False or truncated == 0:
            truncated = 0
        else:
            truncated = 1
        if done == False or done == 0:
            done = 0
        else:
            done = 1
        # self.save_all(state, action, next_state, reward_list, np.array([truncated]), np.array([done]))
        # with open('./out/replay_buffer_test.txt','ab') as f:
        #     np.savetxt(f, state, delimiter=',')
        #     np.savetxt(f, action, delimiter=',')
        #     np.savetxt(f, np.array([reward]), delimiter=',')
        #     np.savetxt(f, next_state, delimiter=',')
        #     np.savetxt(f, np.array([truncated]), delimiter=',')
        #     np.savetxt(f, np.array([done]), delimiter=',')
        # np.save("./pre_train/state"+str(self.number)+".npy", np.array(state))
        # np.save("./pre_train/action" + str(self.number) + ".npy", np.array(action))
        # np.save("./pre_train/reward" + str(self.number) + ".npy", np.array(reward_list))
        # np.save("./pre_train/next_state" + str(self.number) + ".npy", np.array(next_state))
        # np.save("./pre_train/truncated" + str(self.number) + ".npy", np.array([truncated]))
        # np.save("./pre_train/done" + str(self.number) + ".npy", np.array([done]))

    def save_all(self, state, action, next_state, reward_list, truncated, done):
        if self.number < 1000000:
            state_ = np.reshape(state, (-1, 1))
            action_ = np.reshape(action, (-1, 1))
            next_state_ = np.reshape(next_state, (-1, 1))
            reward_list_ = np.reshape(reward_list, (-1, 1))
            truncated_ = np.reshape(truncated, (-1, 1))
            done_ = np.reshape(done, (-1, 1))
            all_feature = np.concatenate((state_, action_, next_state_, reward_list_, truncated_, done_), axis=0)
            self.all_buffer[self.number, :] = np.squeeze(all_feature)
            self.number = self.number + 1
        if self.number == 1000000:
            np.save("./out/all_replay_buffer.npy", self.all_buffer)
            self.number = self.number + 1

    def sample(self, batch_size):  # 从buffer中采样数据,数量为batch_size
        pri_size = min(batch_size // 4, len(self.change_buffer))
        normal_size = batch_size - pri_size
        transition = random.sample(self.buffer, normal_size)
        state, action, reward, next_state, truncated, done = zip(*transition)
        pri_transition = random.sample(self.change_buffer, pri_size)
        pri_state, pri_action, pri_reward, pri_next_state, pri_truncated, pri_done = zip(*pri_transition)
        state = np.concatenate((state, pri_state), axis=0)
        action = np.concatenate((action, pri_action), axis=0)
        reward = np.concatenate((reward, pri_reward), axis=0)
        next_state = np.concatenate((next_state, pri_next_state), axis=0)
        truncated = np.concatenate((truncated, pri_truncated), axis=0)
        done = np.concatenate((done, pri_done), axis=0)
        return state, action, reward, next_state, truncated, done

    def size(self):
        return len(self.buffer)

    def _compress(self, state):
        # print('state: ', state)
        state_left_wps = np.array(state['left_waypoints'], dtype=np.float32).reshape((1, -1))
        state_center_wps = np.array(state['center_waypoints'], dtype=np.float32).reshape((1, -1))
        state_right_wps = np.array(state['right_waypoints'], dtype=np.float32).reshape((1, -1))
        state_veh_left_front = np.array(state['vehicle_info'][0], dtype=np.float32).reshape((1, -1))
        state_veh_front = np.array(state['vehicle_info'][1], dtype=np.float32).reshape((1, -1))
        state_veh_right_front = np.array(state['vehicle_info'][2], dtype=np.float32).reshape((1, -1))
        state_veh_left_rear = np.array(state['vehicle_info'][3], dtype=np.float32).reshape((1, -1))
        state_veh_rear = np.array(state['vehicle_info'][4], dtype=np.float32).reshape((1, -1))
        state_veh_right_rear = np.array(state['vehicle_info'][5], dtype=np.float32).reshape((1, -1))
        state_ev = np.array(state['ego_vehicle'], dtype=np.float32).reshape((1, -1))

        state_ = np.concatenate((state_left_wps, state_veh_left_front, state_veh_left_rear,
                                 state_center_wps, state_veh_front, state_veh_rear,
                                 state_right_wps, state_veh_right_front, state_veh_right_rear, state_ev), axis=1)
        return state_

class Split_ReplayBuffer:
    """经验回放池"""

    def __init__(self, capacity) -> None:
        self.number = 0
        split_capacity = capacity // 8
        self.dangerous_buffer = collections.deque(maxlen=split_capacity)
        self.off_center_buffer = collections.deque(maxlen=split_capacity)
        self.low_efficiency_buffer = collections.deque(maxlen=split_capacity)
        self.on_curve_buffer = collections.deque(maxlen=split_capacity)
        self.large_steering = collections.deque(maxlen=split_capacity)
        self.large_th_br = collections.deque(maxlen=split_capacity)
        self.normal_buffer = collections.deque(maxlen=capacity)
        self.all_buffer = np.zeros((1000000, 66), dtype=np.float32)
        self.ttc_thr = -0.00001
        self.lane_thr = 0.5
        self.eff_thr = 5
        self.curve_thr = 0.1
        # with open('./out/replay_buffer_test.txt', 'w') as f:
        #     pass

    def add(self, state, action, reward, next_state, truncated, done, info):
        wps = np.array(state['waypoints'], dtype=np.float32).reshape((1, -1))
        fcurve = abs(wps[0][1] - wps[0][19])
        # first compress state info, then add
        state = self._compress(state)
        next_state = self._compress(next_state)
        # state.shape: [1, 28], action.shape: [1, 2], others are float and boolean
        reward_ttc = info["TTC"]
        reward_com = info["Comfort"]
        reward_eff = info["velocity"]
        reward_lan = info["offlane"]
        reward_yaw = info["yaw_diff"]
        print("fttc: ", reward_ttc, "flane: ", reward_lan, "feff: ", reward_eff, "fcurve: ", fcurve)
        if reward_ttc < self.ttc_thr:
            # print(fTTC)
            self.dangerous_buffer.append((state, action, np.array([[reward]]), next_state, np.array([[truncated]]), np.array([[done]])))
        elif reward_lan > self.lane_thr:
            self.off_center_buffer.append((state, action, np.array([[reward]]), next_state, np.array([[truncated]]), np.array([[done]])))
        elif reward_eff < self.eff_thr:
            self.low_efficiency_buffer.append((state, action, np.array([[reward]]), next_state, np.array([[truncated]]), np.array([[done]])))
        elif fcurve > self.curve_thr:
            self.on_curve_buffer.append((state, action, np.array([[reward]]), next_state, np.array([[truncated]]), np.array([[done]])))
        elif abs(action[0][0]) > 0.8:
            self.large_steering.append((state, action, np.array([[reward]]), next_state, np.array([[truncated]]), np.array([[done]])))
        elif abs(action[0][1]) > 0.8:
            self.large_th_br.append((state, action, np.array([[reward]]), next_state, np.array([[truncated]]), np.array([[done]])))
        else:
            self.normal_buffer.append((state, action, np.array([[reward]]), next_state, np.array([[truncated]]), np.array([[done]])))

        reward_list = np.array([[reward, reward_ttc, reward_com, reward_eff, reward_lan, reward_yaw]])
        # print("reward_eff: ", reward_eff)
        # print("their shapes", state, action, next_state, reward_list, truncated, done)
        # state: [1, 28], action: [1, 2], next_state: [1, 28], reward_list = [1, 6], truncated = [1, 1], done = [1, 1]
        # all: [1, 66]
        if truncated == False or truncated == 0:
            truncated = 0
        else:
            truncated = 1
        if done == False or done == 0:
            done = 0
        else:
            done = 1
        self.save_all(state, action, next_state, reward_list, np.array([truncated]), np.array([done]))

    def save_all(self, state, action, next_state, reward_list, truncated, done):
        if self.number < 100000:
            state_ = np.reshape(state, (-1, 1))
            action_ = np.reshape(action, (-1, 1))
            next_state_ = np.reshape(next_state, (-1, 1))
            reward_list_ = np.reshape(reward_list, (-1, 1))
            truncated_ = np.reshape(truncated, (-1, 1))
            done_ = np.reshape(done, (-1, 1))
            all_feature = np.concatenate((state_, action_, next_state_, reward_list_, truncated_, done_), axis=0)
            self.all_buffer[self.number, :] = np.squeeze(all_feature)
            self.number = self.number + 1
        if self.number == 100000:
            np.save("./out/all_replay_buffer.npy", self.all_buffer)
            self.number = self.number + 1

    def sample(self, batch_size):  # 从buffer中采样数据,数量为batch_size
        specific_size = batch_size // 8
        normal_size = batch_size - 6 * specific_size
        dangerous_transition = random.sample(self.dangerous_buffer, specific_size)
        d_state, d_action, d_reward, d_next_state, d_truncated, d_done = zip(*dangerous_transition)
        off_center_transition = random.sample(self.off_center_buffer, specific_size)
        o_state, o_action, o_reward, o_next_state, o_truncated, o_done = zip(*off_center_transition)
        low_efficiency_transition = random.sample(self.low_efficiency_buffer, specific_size)
        l_state, l_action, l_reward, l_next_state, l_truncated, l_done = zip(*low_efficiency_transition)
        on_curve_transition = random.sample(self.on_curve_buffer, specific_size)
        c_state, c_action, c_reward, c_next_state, c_truncated, c_done = zip(*on_curve_transition)
        steer_transition = random.sample(self.large_steering, specific_size)
        s_state, s_action, s_reward, s_next_state, s_truncated, s_done = zip(*steer_transition)
        th_br_transition = random.sample(self.large_th_br, specific_size)
        t_state, t_action, t_reward, t_next_state, t_truncated, t_done = zip(*th_br_transition)
        normal_transition = random.sample(self.normal_buffer, normal_size)
        n_state, n_action, n_reward, n_next_state, n_truncated, n_done = zip(*normal_transition)

        state = np.concatenate((d_state, o_state, l_state, c_state, n_state, s_state, t_state), axis=0)
        action = np.concatenate((d_action, o_action, l_action, c_action, n_action, s_action, t_action), axis=0)
        reward = np.concatenate((d_reward, o_reward, l_reward, c_reward, n_reward, s_reward, t_reward), axis=0)
        next_state = np.concatenate((d_next_state, o_next_state, l_next_state, c_next_state, n_next_state, s_next_state, t_next_state), axis=0)
        truncated = np.concatenate((d_truncated, o_truncated, l_truncated, c_truncated, n_truncated, s_truncated, t_truncated), axis=0)
        done = np.concatenate((d_done, o_done, l_done, c_done, n_done, s_done, t_done), axis=0)

        return state, action, reward, next_state, truncated, done

    def size(self):
        return len(self.normal_buffer)

    def all_size(self):
        return len(self.dangerous_buffer), len(self.off_center_buffer), len(self.on_curve_buffer), \
               len(self.large_steering), len(self.large_th_br), len(self.low_efficiency_buffer), len(self.normal_buffer)

    def running_mean_std(self):
        d_size, o_size, c_size, l_size, n_size = self.all_size()
        uniform_size = min(d_size//8, o_size, c_size, l_size, n_size)

        mean = 0
        std = 0
        return mean, std

    def _compress(self, state):
        """return state : waypoints info+ vehicle_front info, shape: 1*22,
        first 20 elements are waypoints info, the rest are vehicle info"""
        wps = np.array(state['waypoints'], dtype=np.float32).reshape((1, -1))
        ev = np.array(state['ego_vehicle'],dtype=np.float32).reshape((1,-1))
        vf = np.array(state['vehicle_front'], dtype=np.float32).reshape((1, -1))
        state_ = np.concatenate((wps, ev, vf), axis=1)

        return state_

class offline_replay_buffer:
    """
    manually adjust the sampling of replay buffer and the replay buffer remains unchanged (1,000,000 buffers)
    """
    def __init__(self):
        file_path = "../out/all_replay_buffer.npy"
        # state: [1, 28], action: [1, 2], next_state: [1, 28], reward_list = [1, 6], truncated = [1, 1], done = [1, 1]
        # reward_list = np.array([[reward, reward_ttc, reward_com, reward_eff, reward_lan, reward_yaw]])
        self.replay_buffer = np.load(file_path, allow_pickle=True)
        self.buffer_num = self.replay_buffer.shape[0]
        # print(replay_buffer.shape)
        # split five buffer of different states: dangerous, large off-center, low-efficiency, on-curve, normal
        self.dangerous_buffer = collections.deque(maxlen=250000)
        self.off_center_buffer = collections.deque(maxlen=250000)
        self.low_efficiency_buffer = collections.deque(maxlen=250000)
        self.on_curve_buffer = collections.deque(maxlen=250000)
        self.normal_buffer = collections.deque(maxlen=1000000)

        self.ttc_thr = -0.00001
        self.lane_thr = 0.5
        self.eff_thr = 5
        self.curve_thr = 0.1
        self.split_replay_buffer()

    def size(self):
        return self.buffer_num

    def split_replay_buffer(self):
        for i in range(self.buffer_num):
            current_buffer = self.replay_buffer[i]
            fTTC = current_buffer[59]
            fLane = current_buffer[62]
            feff = current_buffer[61]
            fcurve = abs(current_buffer[1]-current_buffer[19])
            print("fttc: ", fTTC, "flane: ", fLane, "feff: ", feff, "fcurve: ", fcurve)
            if fTTC < self.ttc_thr:
                # print(fTTC)
                self.dangerous_buffer.append(current_buffer)
            elif fLane > self.lane_thr:
                self.off_center_buffer.append(current_buffer)
            elif feff < self.eff_thr:
                self.low_efficiency_buffer.append(current_buffer)
            elif fcurve > self.curve_thr:
                self.on_curve_buffer.append(current_buffer)
            else:
                self.normal_buffer.append(current_buffer)
        print("dangerous_buffer: ", len(self.dangerous_buffer), "off_center_buffer: ", len(self.off_center_buffer),
              "low_efficiency_buffer: ", len(self.low_efficiency_buffer), "on_curve_buffer: ",
              len(self.on_curve_buffer),"normal_buffer: ", len(self.normal_buffer))

    def sample(self, batch_size):
        specific_size = batch_size/6
        normal_size = batch_size-4*specific_size
        dangerous_transition = random.sample(self.dangerous_buffer, specific_size)
        dangerous_transitions = zip(*dangerous_transition)
        off_center_transition = random.sample(self.off_center_buffer, specific_size)
        off_center_transitions = zip(*off_center_transition)
        low_efficiency_transition = random.sample(self.low_efficiency_buffer, specific_size)
        low_efficiency_transitions = zip(*low_efficiency_transition)
        on_curve_transition = random.sample(self.on_curve_buffer, specific_size)
        on_curve_transitions = zip(*on_curve_transition)
        normal_transition = random.sample(self.normal_buffer, normal_size)
        normal_transitions = zip(*normal_transition)
        all_transitions = np.concatenate((dangerous_transitions, off_center_transitions, on_curve_transitions,
                                          low_efficiency_transitions, normal_transitions), axis=0)
        state, action, next_state, reward, truncated, done = all_transitions[:, :28], all_transitions[:, 28:30],
        all_transitions[:, 30:56], all_transitions[:, 56:57], all_transitions[:, -2:-1], all_transitions[:, -1:]
        return state, action, reward, next_state, truncated, done

class PolicyNet(torch.nn.Module):
    def __init__(self, state_dim, action_bound, train=True) -> None:
        # the action bound and state_dim here are dicts
        super().__init__()
        self.state_dim = state_dim
        self.action_bound = action_bound
        self.train = train
        # self.LaneEncoder = LaneEncoder()
        # self.layer_norm=nn.LayerNorm(128)
        # self.batch_norm=nn.BatchNorm2d(128)
        self.dropout = nn.Dropout(0.2)

        self.fc1_1 = nn.Linear(state_dim['waypoints'], 64)
        self.fc1_2 = nn.Linear(state_dim['ego_vehicle'],32)
        self.fc1_3 = nn.Linear(state_dim['vehicle_front'], 32)
        # concat the first layer output and input to second layer
        self.fc2 = nn.Linear(128,128)
        self.fc_out = nn.Linear(128, 2)

        # torch.nn.init.normal_(self.fc1_1.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc1_2.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc_out.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc_out.weight.data,0,0.01)
        # torch.nn.init.xavier_normal_(self.fc1_1.weight.data)
        # torch.nn.init.xavier_normal_(self.fc1_2.weight.data)
        # torch.nn.init.xavier_normal_(self.fc_out.weight.data)

    def forward(self, state):
        # state : waypoints info+ vehicle_front info, shape: batch_size*22, first 20 elements are waypoints info,
        # the rest are vehicle info
        state_wp = state[:, :self.state_dim['waypoints']]
        state_ev = state[:,-self.state_dim['vehicle_front']-self.state_dim['ego_vehicle']:-self.state_dim['vehicle_front']]
        state_vf = state[:, -self.state_dim['vehicle_front']:]
        state_wp = F.relu(self.fc1_1(state_wp))
        state_ev=F.relu((self.fc1_2(state_ev)))
        state_vf = F.relu(self.fc1_3(state_vf))
        state_ = torch.cat((state_wp,state_ev, state_vf), dim=1)
        hidden = F.relu(self.fc2(state_))
        action = torch.tanh(self.fc_out(hidden))
        # steer,throttle_brake=torch.split(out,split_size_or_sections=[1,1],dim=1)
        # steer=steer.clone()
        # throttle_brake=throttle_brake.clone()
        # steer*=self.action_bound['steer']
        # throttle=throttle_brake.clone()
        # brake=throttle_brake.clone()
        # for i in range(throttle.shape[0]):
        #     if throttle[i][0]<0:
        #         throttle[i][0]=0
        #     if brake[i][0]>0:
        #         brake[i][0]=0
        # throttle*=self.action_bound['throttle']
        # brake*=self.action_bound['brake']

        return action

class veh_lane_encoder(torch.nn.Module):
    def __init__(self, state_dim, train=True):
        super().__init__()
        self.state_dim = state_dim
        self.train = train
        self.lane_encoder = nn.Linear(state_dim['waypoints'], 32)
        self.veh_encoder = nn.Linear(state_dim['conventional_vehicle'] * 2, 32)
        self.agg = nn.Linear(64, 64)

    def forward(self, lane_veh):
        lane = lane_veh[:, :self.state_dim["waypoints"]]
        veh = lane_veh[:, self.state_dim["waypoints"]:]
        lane_enc = F.relu(self.lane_encoder(lane))
        veh_enc = F.relu(self.veh_encoder(veh))
        state_cat = torch.cat((lane_enc, veh_enc), dim=1)
        state_enc = F.relu(self.agg(state_cat))
        return state_enc

class PolicyNet_multi(torch.nn.Module):
    def __init__(self, state_dim, action_bound, train=True) -> None:
        # the action bound and state_dim here are dicts
        super().__init__()
        self.state_dim = state_dim
        self.action_bound = action_bound
        self.train = train
        self.left_encoder = veh_lane_encoder(self.state_dim)
        self.center_encoder = veh_lane_encoder(self.state_dim)
        self.right_encoder = veh_lane_encoder(self.state_dim)
        self.ego_encoder = nn.Linear(self.state_dim['ego_vehicle'], 64)
        self.fc = nn.Linear(256, 256)
        self.fc_out = nn.Linear(256, 2)
        # torch.nn.init.normal_(self.fc1_1.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc1_2.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc_out.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc_out.weight.data,0,0.01)
        # torch.nn.init.xavier_normal_(self.fc1_1.weight.data)
        # torch.nn.init.xavier_normal_(self.fc1_2.weight.data)
        # torch.nn.init.xavier_normal_(self.fc_out.weight.data)

    def forward(self, state):
        # state: (waypoints + 2 * conventional_vehicle0 * 3
        one_state_dim = self.state_dim['waypoints'] + self.state_dim['conventional_vehicle'] * 2
        left_enc = self.left_encoder(state[:, :one_state_dim])
        center_enc = self.center_encoder(state[:, one_state_dim:2*one_state_dim])
        right_enc = self.right_encoder(state[:, 2*one_state_dim:3*one_state_dim])
        ego_enc = self.ego_encoder(state[:, 3*one_state_dim:])
        state_ = torch.cat((left_enc, center_enc, right_enc, ego_enc), dim=1)
        hidden = F.relu(self.fc(state_))
        action = torch.tanh(self.fc_out(hidden))
        # steer,throttle_brake=torch.split(out,split_size_or_sections=[1,1],dim=1)
        # steer=steer.clone()
        # throttle_brake=throttle_brake.clone()
        # steer*=self.action_bound['steer']
        # throttle=throttle_brake.clone()
        # brake=throttle_brake.clone()
        # for i in range(throttle.shape[0]):
        #     if throttle[i][0]<0:
        #         throttle[i][0]=0
        #     if brake[i][0]>0:
        #         brake[i][0]=0
        # throttle*=self.action_bound['throttle']
        # brake*=self.action_bound['brake']

        return action

class QValueNet_multi(torch.nn.Module):
    def __init__(self, state_dim, action_dim) -> None:
        # parameter state_dim here is a dict
        super().__init__()
        self.state_dim = state_dim
        self.action_dim = action_dim
        self.left_encoder = veh_lane_encoder(self.state_dim)
        self.center_encoder = veh_lane_encoder(self.state_dim)
        self.right_encoder = veh_lane_encoder(self.state_dim)
        self.ego_encoder = nn.Linear(self.state_dim['ego_vehicle'], 32)
        self.action_encoder = nn.Linear(self.action_dim, 32)
        self.fc = nn.Linear(256, 256)
        self.fc_out = nn.Linear(256, 1)

        # torch.nn.init.normal_(self.fc1.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc_out.weight.data,0,0.01)
        # torch.nn.init.xavier_normal_(self.fc1.weight.data)
        # torch.nn.init.xavier_normal_(self.fc_out.weight.data)

    def forward(self, state, action):
        one_state_dim = self.state_dim['waypoints'] + self.state_dim['conventional_vehicle'] * 2
        left_enc = self.left_encoder(state[:, :one_state_dim])
        center_enc = self.center_encoder(state[:, one_state_dim:2*one_state_dim])
        right_enc = self.right_encoder(state[:, 2*one_state_dim:3*one_state_dim])
        ego_enc = self.ego_encoder(state[:, 3*one_state_dim:])
        action_enc = self.action_encoder(action)
        state_ = torch.cat((left_enc, center_enc, right_enc, ego_enc, action_enc), dim=1)
        hidden = F.relu(self.fc(state_))
        out = self.fc_out(hidden)
        return out

class QValueNet(torch.nn.Module):
    def __init__(self, state_dim, action_dim) -> None:
        # parameter state_dim here is a dict
        super().__init__()

        #self.state_dim = state_dim['waypoints'] + state_dim['ego_vehicle']+state_dim['vehicle_front']
        self.state_dim=state_dim

        self.action_dim = action_dim
        self.layer_norm = nn.LayerNorm(128)
        self.batch_norm = nn.BatchNorm1d(128)
        self.dropout = nn.Dropout(0.2)
        #self.fc1 = nn.Linear(self.state_dim + action_dim, 64)

        self.fc1_1=nn.Linear(self.state_dim['waypoints'],32)
        self.fc1_2=nn.Linear(self.state_dim['ego_vehicle'],32)
        self.fc1_3=nn.Linear(self.state_dim['vehicle_front'],32)
        self.fc1_4=nn.Linear(self.action_dim,32)
        self.fc2=nn.Linear(128,128)
        self.fc_out = nn.Linear(128, 1)

        # torch.nn.init.normal_(self.fc1.weight.data,0,0.01)
        # torch.nn.init.normal_(self.fc_out.weight.data,0,0.01)
        # torch.nn.init.xavier_normal_(self.fc1.weight.data)
        # torch.nn.init.xavier_normal_(self.fc_out.weight.data)

    def forward(self, state, action):

        # state : waypoints info+ vehicle_front info, shape: batch_size*22, first 20 elements are waypoints info,
        # the rest are vehicle info
        state_wp = state[:, :self.state_dim['waypoints']]
        state_ev = state[:, -self.state_dim['vehicle_front']-self.state_dim['ego_vehicle']:-self.state_dim['vehicle_front']]
        state_vf = state[:, -self.state_dim['vehicle_front']:]
        state_wp=F.relu(self.fc1_1(state_wp))
        state_ev=F.relu(self.fc1_2(state_ev))
        state_vf=F.relu(self.fc1_3(state_vf))
        state_ac=F.relu(self.fc1_4(action))
        state = torch.cat((state_wp,state_ev,state_vf, state_ac), dim=1)
        hidden=F.relu(self.fc2(state))
        out = self.fc_out(hidden)

        return out

class MLP(nn.Module):
    r"""
    Construct a MLP in LaneEncoder, include a single fully-connected layer,
    followed by layer normalization and then ReLU.
    """

    def __init__(self, input_size, hidden_size=64):
        r"""
        self.norm is layer normalization.
        Args:
            input_size: the size of input layer.
            hidden_size: the size of output layer.
        """
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.norm = torch.nn.LayerNorm(hidden_size)
        # self.fc2 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        r"""
        Args:
            x: x.shape = [batch_size, n, input_size]
        """
        x = self.fc1(x)
        x = self.norm(x)
        x = F.relu(x)
        # x = self.fc2(x)
        return x

class LaneEncoder(torch.nn.Module):
    def __init__(self, waypoint_dim, hidden_size):
        super(LaneEncoder, self).__init__()
        self.waypoint_dim = waypoint_dim
        self.hidden_size = hidden_size
        self.MLP = MLP(self.waypoint_dim, self.hidden_size)

    def forward(self, waypoints):
        """
        :param waypoints: [batch_size, n, input_size]
        :return: (batch_size, n, input_size*2)
        """
        x = self.MLP(waypoints)
        batch_size, n, input_size = x.shape
        x2 = x.permute(0, 2, 1)  # [batch_size, input_size, n]
        x2 = F.max_pool1d(x2, kernel_size=x2.shape[2])  # [batch_size, input_size, 1]
        x2 = torch.cat([x2]*n, dim=2)  # [batch_size, input_size, n]
        y = torch.cat((x2.permute(0, 2, 1), x), dim=2)  # [batch_size, n, input_size*2]
        return y

class DDPG:
    def __init__(self, state_dim, action_dim, action_bound, gamma, tau, sigma, theta, epsilon,
                 buffer_size, batch_size, actor_lr, critic_lr, clip_grad, device) -> None:
        self.learn_time = 0
        self.replace_a = 0
        self.replace_c = 0
        self.s_dim = state_dim  # state_dim here is a dict
        self.s_dim['waypoints'] *= 3  # 2 is the feature dim of each waypoint
        self.a_dim, self.a_bound = action_dim, action_bound
        self.theta = theta
        self.gamma, self.tau, self.sigma, self.epsilon = gamma, tau, sigma, epsilon  # sigma:高斯噪声的标准差，均值直接设置为0
        self.buffer_size, self.batch_size, self.device = buffer_size, batch_size, device
        self.actor_lr, self.critic_lr = actor_lr, critic_lr
        self.clip_grad = clip_grad
        # adjust different types of replay buffer
        #self.replay_buffer = Split_ReplayBuffer(buffer_size)
        self.replay_buffer = ReplayBuffer(buffer_size)
        # self.replay_buffer = offline_replay_buffer()
        """self.memory=torch.tensor((buffer_size,self.s_dim*2+self.a_dim+1+1),
            dtype=torch.float32).to(self.device)"""
        self.pointer = 0  # serve as updating the memory data
        self.train = True
        self.actor = PolicyNet_multi(self.s_dim, self.a_bound).to(self.device)
        self.actor_target = PolicyNet_multi(self.s_dim, self.a_bound).to(self.device)
        self.actor_target.load_state_dict(self.actor.state_dict())
        self.critic = QValueNet_multi(self.s_dim, self.a_dim).to(self.device)
        self.critic_target = QValueNet_multi(self.s_dim, self.a_dim).to(self.device)
        # self.actor = PolicyNet(self.s_dim, self.a_bound).to(self.device)
        # self.actor_target = PolicyNet(self.s_dim, self.a_bound).to(self.device)
        # self.actor_target.load_state_dict(self.actor.state_dict())
        # self.critic = QValueNet(self.s_dim, self.a_dim).to(self.device)
        # self.critic_target = QValueNet(self.s_dim, self.a_dim).to(self.device)
        self.critic_target.load_state_dict(self.critic.state_dict())

        self.actor_optimizer = torch.optim.Adam(self.actor.parameters(), lr=actor_lr)
        self.critic_optimizer = torch.optim.Adam(self.critic.parameters(), lr=critic_lr)
        self.loss = nn.MSELoss()

        self.steer_noise = OrnsteinUhlenbeckActionNoise(self.sigma, self.theta)
        self.tb_noise = OrnsteinUhlenbeckActionNoise(self.sigma, self.theta)

    def take_action(self, state):
        # print('vehicle_info', state['vehicle_info'])
        state_left_wps = torch.tensor(state['left_waypoints'], dtype=torch.float32).view(1, -1).to(self.device)
        state_center_wps = torch.tensor(state['center_waypoints'], dtype=torch.float32).view(1, -1).to(self.device)
        state_right_wps = torch.tensor(state['right_waypoints'], dtype=torch.float32).view(1, -1).to(self.device)
        state_veh_left_front = torch.tensor(state['vehicle_info'][0], dtype=torch.float32).view(1, -1).to(self.device)
        state_veh_front = torch.tensor(state['vehicle_info'][1], dtype=torch.float32).view(1, -1).to(self.device)
        state_veh_right_front = torch.tensor(state['vehicle_info'][2], dtype=torch.float32).view(1, -1).to(self.device)
        state_veh_left_rear = torch.tensor(state['vehicle_info'][3], dtype=torch.float32).view(1, -1).to(self.device)
        state_veh_rear = torch.tensor(state['vehicle_info'][4], dtype=torch.float32).view(1, -1).to(self.device)
        state_veh_right_rear = torch.tensor(state['vehicle_info'][5], dtype=torch.float32).view(1, -1).to(self.device)
        state_ev = torch.tensor(state['ego_vehicle'],dtype=torch.float32).view(1,-1).to(self.device)
        state_ = torch.cat((state_left_wps, state_veh_left_front, state_veh_left_rear,
                            state_center_wps, state_veh_front, state_veh_rear,
                            state_right_wps, state_veh_right_front, state_veh_right_rear, state_ev), dim=1)
        # print(state_.shape)
        action = self.actor(state_)
        print(f'Network Output - Steer: {action[0][0]}, Throttle_brake: {action[0][1]}')
        if (action[0, 0].is_cuda):
            action = np.array([action[:, 0].detach().cpu().numpy(), action[:, 1].detach().cpu().numpy()]).reshape((-1, 2))
        else:
            action = np.array([action[:, 0].detach().numpy(), action[:, 1].detach().numpy()]).reshape((-1, 2))
        # if np.random.random()<self.epsilon:
        if self.train:
            action[:, 0] = np.clip(np.random.normal(action[:, 0], self.sigma), -1, 1)
            action[:, 1] = np.clip(np.random.normal(action[:, 1], self.sigma), -1, 1)
        # if self.train:
        #     action[:,0]=np.clip(action[:,0]+self.steer_noise(),-1,1)
        #     action[:,1]=np.clip(action[:,1]+self.tb_noise(),-1,1)
        print(f'After noise - Steer: {action[0][0]}, Throttle_brake: {action[0][1]}')
        # for i in range(action.shape[0]):
        #     if action[i,1]>0:
        #         action[i,1]+=np.clip(np.random.normal(action[i,1],self.sigma),0,self.a_bound['throttle'])
        #     elif action[i,2]<0:
        #         action[i,2]+=np.clip(np.random.normal(action[i,2],self.sigma),-self.a_bound['brake'],0)

        return action

    def learn(self):
        self.learn_time += 1
        # if self.learn_time > 100000:
        #     self.train = False
        self.replace_a += 1
        self.replace_c += 1
        b_s, b_a, b_r, b_ns, b_t, b_d = self.replay_buffer.sample(self.batch_size)
        # 此处得到的batch是否是pytorch.tensor?
        batch_s = torch.tensor(b_s, dtype=torch.float32).view((self.batch_size, -1)).to(self.device)
        batch_ns = torch.tensor(b_ns, dtype=torch.float32).view((self.batch_size, -1)).to(self.device)
        batch_a = torch.tensor(b_a, dtype=torch.float32).view((self.batch_size, -1)).to(self.device)
        batch_r = torch.tensor(b_r, dtype=torch.float32).view((self.batch_size, -1)).to(self.device)
        batch_d = torch.tensor(b_d, dtype=torch.float32).view((self.batch_size, -1)).to(self.device)
        batch_t = torch.tensor(b_t, dtype=torch.float32).view((self.batch_size, -1)).to(self.device)
        # compute the target Q value using the information of next state
        action_target = self.actor_target(batch_ns)
        next_q_values = self.critic_target(batch_ns, action_target)
        q_targets = batch_r + self.gamma * next_q_values * (1 - batch_t)
        q_ = self.critic(batch_s, batch_a)
        critic_loss = self.loss(q_, q_targets)
        td_max = 0
        index = 0
        for i in range(self.batch_size):
            if abs(q_[i]-q_targets[i]) > td_max:
                td_max = abs(q_[i]-q_targets[i])
                index = i
        print(f'TD-error:{critic_loss}', td_max, index)
        # print(batch_s[index], batch_ns[index], batch_a[index], batch_r[index], batch_t[index])
        self.critic_optimizer.zero_grad()
        critic_loss.backward()
        if self.clip_grad > 0:
            torch.nn.utils.clip_grad_norm_(self.critic.parameters(), self.clip_grad)
        self.critic_optimizer.step()

        action = self.actor(batch_s)
        q = self.critic(batch_s, action)
        actor_loss = -torch.mean(q)
        self.actor_optimizer.zero_grad()
        actor_loss.backward()
        if self.clip_grad > 0:
            torch.nn.utils.clip_grad_norm_(self.actor.parameters(), self.clip_grad)
        self.actor_optimizer.step()

        self.soft_update(self.actor, self.actor_target)
        self.soft_update(self.critic, self.critic_target)

    def _print_grad(self, model):
        '''Print the grad of each layer'''
        for name, parms in model.named_parameters():
            print('-->name:', name, '-->grad_requirs:', parms.requires_grad, ' -->grad_value:', parms.grad)

    def set_sigma(self, sigma):
        self.sigma = sigma
        self.steer_noise.set_sigma(sigma)
        self.tb_noise.set_sigma(sigma)

    def reset_noise(self):
        self.steer_noise.reset()
        self.tb_noise.reset()

    def soft_update(self, net, target_net):
        for param_target, param in zip(target_net.parameters(), net.parameters()):
            param_target.data.copy_(param_target.data * (1.0 - self.tau) + param.data * self.tau)

    def hard_update(self, net, target_net):
        net.load_state_dict(target_net.state_dict())

    def store_transition(self, transition_dict):  # how to store the episodic data to buffer
        index = self.pointer % self.buffer_size
        states = torch.tensor(transition_dict['states'],
                              dtype=torch.float32).view(-1, 1).to(self.device)
        actions = torch.tensor(transition_dict['actions'],
                               dtype=torch.float32).to(self.device)
        rewards = torch.tensor(transition_dict['rewards'],
                               dtype=torch.float32).to(self.device)
        states_next = torch.tensor(transition_dict['states_next'],
                                   dtype=torch.float32).view(-1, 1).to(self.device)
        dones = torch.tensor(transition_dict['dones'],
                             dtype=torch.float32).to(self.device)
        return

    def save_net(self,file='./out/ddpg_final.pth'):
        state = {
            'actor': self.actor.state_dict(),
            'actor_target':self.actor_target.state_dict(),
            'critic': self.critic.state_dict(),
            'critic_target':self.critic_target.state_dict(),
            'actor_optimizer': self.actor_optimizer.state_dict(),
            'critic_optimizer': self.critic_optimizer.state_dict()
        }
        torch.save(state, file)

    def load_net(self, state):
        self.critic.load_state_dict(state['critic'])
        self.critic_target.load_state_dict(state['critic_target'])
        self.actor.load_state_dict(state['actor'])
        self.actor_target.load_state_dict(state['actor_target'])
        self.actor_optimizer.load_state_dict(state['actor_optimizer'])
        self.critic_optimizer.load_state_dict(state['critic_optimizer'])

class OrnsteinUhlenbeckActionNoise:
    def __init__(self, sigma, theta=0.001, mu=np.array([0.0]), dt=1e-2, x0=None):
        """
        mu: The mean value of action
        theta: The bigger the value, the faster noise get close to mu
        sigma: Noise amplifier, the bigger the value, the more amplification of the disturbance
        """
        self.theta = theta
        self.mu = mu
        self.sigma = sigma
        self.dt = dt
        self.x0 = x0
        self.reset()

    def __call__(self):
        x = self.x_prev + self.theta * (self.mu - self.x_prev) * self.dt + \
            self.sigma * np.sqrt(self.dt) * np.random.normal(size=self.mu.shape)
        self.x_prev = x
        return x

    def reset(self):
        self.x_prev = self.x0 if self.x0 is not None else np.zeros_like(self.mu)

    def set_sigma(self, sigma):
        self.sigma = sigma

    def __repr__(self):
        return 'OrnsteinUhlenbeckActionNoise(mu={}, sigma={})'.format(self.mu, self.sigma)

class SumTree(object):
    """
    This SumTree code is a modified version and the original code is from:
    https://github.com/jaara/AI-blog/blob/master/SumTree.py

    Story data with its priority in the tree.
    """
    data_pointer = 0

    def __init__(self, capacity):
        self.capacity = capacity  # for all priority values
        self.tree = np.zeros(2 * capacity - 1)
        # [--------------Parent nodes-------------][-------leaves to recode priority-------]
        #             size: capacity - 1                       size: capacity
        self.data = np.zeros(capacity, dtype=object)  # for all transitions
        # [--------------data frame-------------]
        #             size: capacity

    def add(self, p, data):
        tree_idx = self.data_pointer + self.capacity - 1
        self.data[self.data_pointer] = data  # update data_frame
        self.update(tree_idx, p)  # update tree_frame

        self.data_pointer += 1
        if self.data_pointer >= self.capacity:  # replace when exceed the capacity
            self.data_pointer = 0

    def update(self, tree_idx, p):
        change = p - self.tree[tree_idx]
        self.tree[tree_idx] = p
        # then propagate the change through tree
        while tree_idx != 0:    # this method is faster than the recursive loop in the reference code
            tree_idx = (tree_idx - 1) // 2
            self.tree[tree_idx] += change

    def get_leaf(self, v):
        """
        Tree structure and array storage:

        Tree index:
             0         -> storing priority sum
            / \
          1     2
         / \   / \
        3   4 5   6    -> storing priority for transitions

        Array type for storing:
        [0,1,2,3,4,5,6]
        """
        parent_idx = 0
        while True:     # the while loop is faster than the method in the reference code
            cl_idx = 2 * parent_idx + 1         # this leaf's left and right kids
            cr_idx = cl_idx + 1
            if cl_idx >= len(self.tree):        # reach bottom, end search
                leaf_idx = parent_idx
                break
            else:       # downward search, always search for a higher priority node
                if v <= self.tree[cl_idx]:
                    parent_idx = cl_idx
                else:
                    v -= self.tree[cl_idx]
                    parent_idx = cr_idx

        data_idx = leaf_idx - self.capacity + 1
        return leaf_idx, self.tree[leaf_idx], self.data[data_idx]

    @property
    def total_p(self):
        return self.tree[0]  # the root


import pygame
from pygame.sprite import Sprite

class Ship(Sprite):
	"""Класс для управления кораблем"""

	def __init__(self, ai_game):
		"""Задает начальную позицию"""
		super().__init__()

		self.screen = ai_game.screen
		self.settings = ai_game.settings
		# Rectangle surface
		self.screen_rect = ai_game.screen.get_rect()

		# Загружает изображение корабля и получает прямоугольник
		self.image = pygame.image.load('images/ship.bmp')
		# Положение корабля
		self.rect = self.image.get_rect()
		# Каждый новый корабль появляется у нижнего края экрана midtop etc positions
		self.rect.midbottom = self.screen_rect.midbottom

		# Прямоугольники принимают только целые значения, а скорость взяли дробную, поэтому
		self.x = float(self.rect.x)
		self.y = float(self.rect.y)

		# Флаги для перемещения
		self.moving_right = False
		self.moving_left = False
		self.moving_up = False
		self.moving_down = False

	def update(self):

		if self.moving_right and self.rect.right < self.screen_rect.right:
			self.x += self.settings.ship_speed

		if self.moving_left and self.rect.left > 0:
			self.x -= self.settings.ship_speed

		if self.moving_up and self.rect.top > self.screen_rect.top:
			self.y -= self.settings.ship_speed

		if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
			self.y += self.settings.ship_speed

		self.rect.x = self.x
		self.rect.y = self.y

	def update1(self):
		"""Обновляет позицию корабля с учетом нажатия клавиш"""
		if self.moving_right and self.rect.right < self.screen_rect.right:
			self.x += self.settings.ship_speed

		# Можно поставить и > 0, т.к. (0,0) в левом углу, но мы это опустим
		if self.moving_left and self.rect.left > self.screen_rect.left:
			self.x -= self.settings.ship_speed

		# Будет сохранена только целая часть
		self.rect.x = self.x

	def blitme(self):
		"""Рисует корабль в текущей позиции"""
		self.screen.blit(self.image, self.rect)

	def center_ship(self):
		"""Размещает корабль в центр экрана"""
		self.rect.midbottom = self.screen_rect.midbottom
		self.x = float(self.rect.x)
		self.y = float(self.rect.y)


from __future__ import absolute_import, print_function, division

import random

import yelp_data as yd
from utils.textdata import tokenize_text
from utils.statistics import map_range

from nltk.sentiment.vader import SentimentIntensityAnalyzer

from sklearn.feature_extraction import DictVectorizer
from sklearn.naive_bayes import MultinomialNB
import sklearn.metrics as metrics

def get_ngram_counts(n, text):
    """
    Return a dictionary counts of all ngrams in the list of sentences.
    """
    ngram_dict = {}
    for s in text:
        for i in range(len(s)):
            if i + n > len(s): break
            ngram = tuple(s[i:i+n])
            ngram_dict[ngram] = ngram_dict.get(ngram, 0) + 1
    return ngram_dict

def evaluate(classifier, data_x, data_y, report=False):
    y_true = data_y
    y_pred = classifier.predict(data_x)
    print("Accuracy =", metrics.accuracy_score(y_true, y_pred))
    if report:
        print(metrics.classification_report(y_true, y_pred))

def run():
    random.seed(42)
    sid = SentimentIntensityAnalyzer()

    print("Load data...")
    train_x, train_y = yd.load_yelp_reviews(yd.TRAIN_FILE)
    test_x, test_y = yd.load_yelp_reviews(yd.TEST_FILE)
    all_x = train_x + test_x

    # Extract features as dictionaries (it seems bigram works best)
    print("Extract features...")
    train_feat = [get_ngram_counts(2, tokenize_text(t)) for t in train_x]
    test_feat = [get_ngram_counts(2, tokenize_text(t)) for t in test_x]

    # Vectorize
    vec = DictVectorizer()
    vec.fit(train_feat)
    train_feat = vec.transform(train_feat)
    test_feat = vec.transform(test_feat)

    print("Begin training...")
    classifier = MultinomialNB().fit(train_feat, train_y)

    # Evaluating
    print("Evaluating trainset")
    evaluate(classifier, train_feat, train_y)
    print("Evaluating testset")
    evaluate(classifier, test_feat, test_y)

if __name__ == '__main__':
    run()


from django.contrib.auth.models import User
from django.db import transaction
from rest_framework import serializers

from anime.models import Anime
from user.serializer import UsuarioListSerializer

class AnimeListSerializer(serializers.ModelSerializer):
    cd_usuario = UsuarioListSerializer()

    class Meta:
        model = Anime
        fields = (
            'id',
            'nm_anime',
            'ds_anime',
            'ie_assistido',
            'ie_favorito',
            'cd_usuario'
        )

class AnimeCreateSerializer(serializers.ModelSerializer):

    class Meta:
        model = Anime
        fields = (
            'nm_anime',
            'ds_anime',
            'ie_assistido',
            'ie_favorito',
            'cd_usuario'
        )

class AnimeUpdadeSerializer(serializers.ModelSerializer):

    class Meta:
        model = Anime
        fields = (
            'nm_anime',
            'ds_anime',
            'ie_assistido',
            'ie_favorito',
        )


import os
from datetime import datetime

import handlers
from boto_retry import get_client_with_retries
from helpers import safe_json

MESSAGE_TYPE_ENDED = "task-ended"
MESSAGE_TYPE_STARTED = "task-started"

ERR_SEND_NOTIFICATION = "Cannot send notification to topic {}, {}"

ENV_RESULT_TOPIC = "SNS_RESULT_TOPIC_ARN"
MAX_SIZE = 262143

class ResultNotifications(object):

    def __init__(self, context, logger):
        self._sns_client = None
        self._context = context
        self._logger = logger

    @property
    def sns_client(self):
        if self._sns_client is None:
            self._sns_client = get_client_with_retries("sns", methods=["publish"], context=self._context)
        return self._sns_client

    @property
    def topic_arn(self):
        return os.getenv(ENV_RESULT_TOPIC, None)

    @classmethod
    def _build_common_attributes(cls, task):
        message = {a: task.get(a, "") for a in [
            handlers.TASK_TR_ID,
            handlers.TASK_TR_NAME,
            handlers.TASK_TR_ACTION,
            handlers.TASK_TR_ACCOUNT,
            handlers.TASK_TR_RESOURCES,
            handlers.TASK_TR_PARAMETERS
        ]}

        message["Time"] = datetime.now().isoformat()
        return message

    def _publish(self, message):
        self.sns_client.publish_with_retries(TopicArn=self.topic_arn, Message=safe_json(message)[0:MAX_SIZE])

    def publish_started(self, task):
        try:
            if task.get(handlers.TASK_TR_NOTIFICATIONS, False):
                message = self._build_common_attributes(task)
                message["Type"] = MESSAGE_TYPE_STARTED
                self._publish(message)
        except Exception as ex:
            self._logger.error(ERR_SEND_NOTIFICATION, self.topic_arn, ex)

    def publish_ended(self, task):
        try:
            if task.get(handlers.TASK_TR_NOTIFICATIONS, False):
                message = self._build_common_attributes(task)
                message["Type"] = MESSAGE_TYPE_ENDED
                message[handlers.TASK_TR_STATUS] = task.get(handlers.TASK_TR_STATUS, "")
                if task[handlers.TASK_TR_STATUS] == handlers.STATUS_COMPLETED:
                    message[handlers.TASK_TR_RESULT] = task.get(handlers.TASK_TR_RESULT)
                else:
                    message[handlers.TASK_TR_ERROR] = task.get(handlers.TASK_TR_ERROR, "")
                self._publish(message)
        except Exception as ex:
            self._logger.error(ERR_SEND_NOTIFICATION, self.topic_arn, ex)


#Graph from a txt file
import Tkinter as tk
import ttk
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import matplotlib.animation as animation
from matplotlib.figure import Figure

Large_Font= ("Verdana", 12)

f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)

def animate(i):
    pullData = open("Test-Data.txt", 'r').read()
    dataList = pullData.split('\n')
    xList = []
    yList = []
    for eachLine in dataList:
        if len (eachLine) > 1:
            x, y = eachLine.split(',')
            xList.append(int(x))
            yList.append(int(y))
    a.clear()
    a.plot(xList, yList)

class TextDapp (tk.Tk):

      def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args,**kwargs)
        tk.Tk.wm_title(self, "Sea of BTC Client")
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand="true")
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in (StartPage, GraphP):

            frame = F(container, self)

            self.frames[F] = frame

            frame.grid(row=0, column=0, sticky='nsew')

        self.show_frame(StartPage)

      def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()

class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text='Text Data Graph', font=Large_Font)
        label.pack(pady=10, padx=10)
        label = tk.Label(self, text='This is a Test Python script that Graphs numbers from a .txt File')
        label.pack()
        button0 = ttk.Button(self,text='Graph',command=lambda: controller.show_frame(GraphP))
        button0.pack()
        button1 = ttk.Button(self, text='Exit', command=quit)
        button1.pack()
        label = tk.Label(self, text='Make sure Test-Data.txt is in the same Dir as the script & 1,2,3 ect in Each Line then a (,) &  then your # ')
        label.pack()

class GraphP(tk.Frame):

    def __init__(self,parent , controller):
        tk.Frame.__init__(self,parent)
        label = tk.Label(self, text="Graph Page", font=Large_Font)
        label.pack(pady=10, padx=10)
        button5 = ttk.Button(self, text='Quit', command=quit)
        button5.pack()
        button6 = ttk.Button(self, text='Home', command=lambda: controller.show_frame(StartPage))
        button6.pack()

        canvas = FigureCanvasTkAgg(f, self)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand = "true")

        toolbar = NavigationToolbar2TkAgg(canvas, self)
        toolbar.update()
        canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand = "true")

app = TextDapp()
ani = animation.FuncAnimation(f, animate, interval=2000)
app.mainloop()


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

import argparse

import pandas as pd

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

import warnings
warnings.filterwarnings("ignore")

import logging
import datetime
from datetime import datetime, timedelta

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

from sklearn.model_selection import KFold

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

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

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

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

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

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

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

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

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

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

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

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

        return self.trainer, self.tester

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

if __name__ == '__main__':

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

    EPOCHS = args.epochs
    BATCH_SIZE = args.batchsize

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

    loss_fn = nn.BCEWithLogitsLoss()

    # For fold results
    results = {}

    dataset = ConcatDataset([train_loader, test_loader])

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

        fold_str = "fold_" + str(fold)

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

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

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

        for epoch in range(0, EPOCHS):

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

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

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

    print("---"*10)

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

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

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

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

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

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


from django.contrib import admin
from jobs.models import Job

class JobAdmin(admin.ModelAdmin):
	ordering = ['created']
	list_display = ['title', 'organisation', 'location', 'experience', 'employment_type', 'salary', 'approved',]
	list_filter = ['approved','source','created']

admin.site.register(Job, JobAdmin)


import cv2
import numpy as np

#burada gruplandırm, bölgeye göre eşik değeri ayarlanabiliyor
image= cv2.imread("101/ders15/color.jpg",0) #gri tonlamalı olmalı

ret,thresh = cv2.threshold(image,160,255,cv2.THRESH_BINARY) #simple thresholding

thresh2 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,23,2) #piksel sınırları
t#karmaşık görsellerde belirli bir bölge için istediğimiz gibi thresh uygulaması yaparız.
thresh3 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,23,2)

cv2.imshow("thresh",thresh)
cv2.imshow("thresh2",thresh2)
cv2.imshow("thresh3",thresh3)

cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(1)


"""crm1 URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from accounts import views as auth_views
from users import views as user_views
from django.contrib.auth.views import LoginView, LogoutView
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', auth_views.home, name='dashboards'),
    path('product/', auth_views.product, name='products'),
    path('customer/<int:pk>/', auth_views.customer, name='customers'),
    path('create_order/<int:pk>/', auth_views.createOrder, name='create-order'),
    path('update_order/<int:pk>/', auth_views.updateOrder, name='order-update'),
    path('update_form/<int:pk>/', auth_views.updateCustomer, name='update-customer'),
    path('delete_order/<int:pk>/', auth_views.deleteOrder, name='delete'),
    path('create_customer/', auth_views.createCustomer, name='create-customer'),
    path('users/register/', user_views.register, name='user-registration'),
    path('users/login/', LoginView.as_view(template_name='users/login.html'), name='user-login'),
    path('users/logout/', LogoutView.as_view(template_name='users/logout.html'), name='user-logout'),
    path('user/', auth_views.userPage, name='user-page'),
    path('user_account/', user_views.user_account, name='user-account')
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


from flask import Blueprint, current_app, jsonify, request

from bhadrasana.models.laudo import get_empresas, get_ncm, get_empresa, \
    get_empresas_nome

cadastrosapi = Blueprint('cadastrosapi', __name__)

@cadastrosapi.route('/api/empresa/<cnpj>', methods=['GET'])
def empresa_(cnpj):
    db_session = current_app.config['db_session']
    try:
        empresa = get_empresa(db_session, cnpj)
        if empresa is None:
            return jsonify({'msg': 'Empresa %s não encontrada' % cnpj}), 404
    except ValueError as err:
        return jsonify(
            {'msg': str(err)}), 400
    except Exception as err:  # pragma: no cover
        current_app.logger.error(err, exc_info=True)
        return jsonify(
            {'msg': 'Erro inesperado! Detalhes no log da aplicação. ' + str(err)}), 500
    return jsonify(empresa.dump()), 200

@cadastrosapi.route('/api/empresa_e_filiais/<cnpj>', methods=['GET'])
def empresa_filiais(cnpj):
    db_session = current_app.config['db_session']
    try:
        empresas = get_empresas(db_session, cnpj)
        result = [empresa.dump() for empresa in empresas]
    except ValueError as err:
        return jsonify(
            {'msg': str(err)}), 400
    except Exception as err:  # pragma: no cover
        current_app.logger.error(err, exc_info=True)
        return jsonify(
            {'msg': 'Erro inesperado! Detalhes no log da aplicação. ' + str(err)}), 500
    return jsonify(result), 404 if len(result) == 0 else 200

@cadastrosapi.route('/api/empresas', methods=['GET'])
def empresas_():
    db_session = current_app.config['db_session']
    try:
        nome = request.args.get('nome')
        empresas = get_empresas_nome(db_session, nome)
        result = [empresa.dump() for empresa in empresas]
        if len(result) == 0:
            return jsonify(
                {'msg': 'Empresas não encontradas com filtro %s ' % request.args}), 404
    except ValueError as err:
        return jsonify(
            {'msg': str(err)}), 400
    except Exception as err:  # pragma: no cover
        current_app.logger.error(err, exc_info=True)
        return jsonify(
            {'msg': 'Erro inesperado! Detalhes no log da aplicação. ' + str(err)}), 500
    return jsonify(result), 404 if len(result) == 0 else 200

@cadastrosapi.route('/api/ncm/<codigo>', methods=['GET'])
def ncm_(codigo):
    db_session = current_app.config['db_session']
    try:
        ncm = get_ncm(db_session, codigo)
        if ncm is None:
            return jsonify({'msg': 'NCM %s não encontrado' % codigo}), 404
    except ValueError as err:
        return jsonify(
            {'msg': str(err)}), 400
    except Exception as err:  # pragma: no cover
        current_app.logger.error(err, exc_info=True)
        return jsonify(
            {'msg': 'Erro inesperado! Detalhes no log da aplicação. ' + str(err)}), 500
    return jsonify(ncm.dump()), 200


import pytest

URL = "https://casinoextreme.eu/games"
RUN_CSS = "a.playgame-demo[onclick^='playGame']"
IFRAME_CSS = "#gameplay > iframe"
GOOD_MSG = "GameViewModel"
BAD_MSG = "UnsupportedDevice"

async def check_for_message(client, message):
    await client.navigate(URL)
    client.soft_click(client.await_css(RUN_CSS))
    client.switch_to_frame(client.await_css(IFRAME_CSS))
    await (await client.promise_console_message_listener(message))

@pytest.mark.only_platforms("android")
@pytest.mark.asyncio
@pytest.mark.with_interventions
async def test_enabled(client):
    await check_for_message(client, GOOD_MSG)

@pytest.mark.only_platforms("android")
@pytest.mark.asyncio
@pytest.mark.without_interventions
async def test_disabled(client):
    await check_for_message(client, BAD_MSG)


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

from builtins import super

import requests
import sys

from requests.exceptions import HTTPError
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

DEFAULT_TIMEOUT = 295  # seconds
TOTAL_RETRIES = 5
DEBUG_FLAG = False

def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
    if DEBUG_FLAG:
        debug_hook(exception_type, exception, traceback)
    else:
        print("%s: %s" % (exception_type.__name__, exception))

sys.excepthook = exception_handler

class TimeoutHTTPAdapter(HTTPAdapter):
    def __init__(self, *args, **kwargs):
        self.timeout = DEFAULT_TIMEOUT
        if "timeout" in kwargs:
            self.timeout = kwargs["timeout"]
            del kwargs["timeout"]
        super().__init__(*args, **kwargs)

    def send(self, request, **kwargs):
        timeout = kwargs.get("timeout")
        if timeout is None:
            kwargs["timeout"] = self.timeout
        return super().send(request, **kwargs)

retry_strategy = Retry(total=TOTAL_RETRIES,
                       backoff_factor=2,
                       status_forcelist=[429, 500, 502, 503, 504],
                       method_whitelist=["GET", "POST"])
adapter = TimeoutHTTPAdapter(max_retries=retry_strategy)

def expanded_raise_for_status(res):
    """
    Take a "requests" response object and expand the raise_for_status method to return more helpful errors
    :param res:
    :return: None
    """
    try:
        res.raise_for_status()
    except HTTPError as e:
        try:
            raise HTTPError('{}\nReason: {}'.format(str(e), res.json()['message']))
        except ValueError:
            raise e
    return

def resilient_requests(func, *args, **kwargs):
    with requests.Session() as session:
        session.mount("https://", adapter)
        response = getattr(session, func)(*args, **kwargs)
        expanded_raise_for_status(response)
    return response


import sqlite3

conn = sqlite3.connect('database.db')
db = conn.cursor()

sql = """
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT,
    `name` TEXT UNIQUE
);
"""
db.executescript(sql)

conn.commit()
conn.close()

print("SETUP DONE")


import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn import init

def conv3x3(in_channels, out_channels, stride=1,
            padding=1, bias=True, groups=1):
    return nn.Conv2d(
        in_channels,
        out_channels,
        kernel_size=3,
        stride=stride,
        padding=padding,
        bias=bias,
        groups=groups)

def upconv2x2(in_channels, out_channels, mode='transpose'):
    if mode == 'transpose':
        return nn.ConvTranspose2d(
            in_channels,
            out_channels,
            kernel_size=2,
            stride=2)
    else:
        # out_channels is always going to be the same
        # as in_channels
        return nn.Sequential(
            nn.Upsample(mode='bilinear', scale_factor=2),
            conv1x1(in_channels, out_channels))

def conv1x1(in_channels, out_channels, groups=1):
    return nn.Conv2d(
        in_channels,
        out_channels,
        kernel_size=1,
        groups=groups,
        stride=1)

class DownConv(nn.Module):
    """
    A helper Module that performs 2 convolutions and 1 MaxPool.
    A ReLU activation follows each convolution.
    """

    def __init__(self, in_channels, out_channels, pooling=True):
        super(DownConv, self).__init__()

        self.in_channels = in_channels
        self.out_channels = out_channels
        self.pooling = pooling

        self.conv1 = conv3x3(self.in_channels, self.out_channels)
        self.conv2 = conv3x3(self.out_channels, self.out_channels)

        if self.pooling:
            self.pool = nn.MaxPool2d(kernel_size=2, stride=2)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        before_pool = x
        if self.pooling:
            x = self.pool(x)
        return x, before_pool

class UpConv(nn.Module):
    """
    A helper Module that performs 2 convolutions and 1 UpConvolution.
    A ReLU activation follows each convolution.
    """

    def __init__(self, in_channels, out_channels,
                 merge_mode='concat', up_mode='transpose'):
        super(UpConv, self).__init__()

        self.in_channels = in_channels
        self.out_channels = out_channels
        self.merge_mode = merge_mode
        self.up_mode = up_mode

        self.upconv = upconv2x2(self.in_channels, self.out_channels,
                                mode=self.up_mode)

        if self.merge_mode == 'concat':
            self.conv1 = conv3x3(
                2*self.out_channels, self.out_channels)
        else:
            # num of input channels to conv2 is same
            self.conv1 = conv3x3(self.out_channels, self.out_channels)
        self.conv2 = conv3x3(self.out_channels, self.out_channels)

    def forward(self, from_down, from_up):
        """ Forward pass
        Arguments:
            from_down: tensor from the encoder pathway
            from_up: upconv'd tensor from the decoder pathway
        """
        from_up = self.upconv(from_up)
        if self.merge_mode == 'concat':
            x = torch.cat((from_up, from_down), 1)
        else:
            x = from_up + from_down
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        return x

class AbstractUNet(nn.Module):
    """ `UNet` class is based on https://arxiv.org/abs/1505.04597
    The U-Net is a convolutional encoder-decoder neural network.
    Contextual spatial information (from the decoding,
    expansive pathway) about an input tensor is merged with
    information representing the localization of details
    (from the encoding, compressive pathway).
    Modifications to the original paper:
    (1) padding is used in 3x3 convolutions to prevent loss
        of border pixels
    (2) merging outputs does not require cropping due to (1)
    (3) residual connections can be used by specifying
        UNet(merge_mode='add')
    (4) if non-parametric upsampling is used in the decoder
        pathway (specified by upmode='upsample'), then an
        additional 1x1 2d convolution occurs after upsampling
        to reduce channel dimensionality by a factor of 2.
        This channel halving happens with the convolution in
        the tranpose convolution (specified by upmode='transpose')
    """
    # * enforces named parameters to avoid accidential misconfigurations
    def __init__(self, config):
        super(AbstractUNet, self).__init__()
        up_mode = config['UP_MODE']
        if up_mode in ('transpose', 'upsample'):
            self.up_mode = up_mode
        else:
            raise ValueError("\"{}\" is not a valid mode for "
                             "upsampling. Only \"transpose\" and "
                             "\"upsample\" are allowed.".format(up_mode))
        merge_mode = config['MERGE_MODE']
        if merge_mode in ('concat', 'add'):
            self.merge_mode = merge_mode
        else:
            raise ValueError("\"{}\" is not a valid mode for"
                             "merging up and down paths. "
                             "Only \"concat\" and "
                             "\"add\" are allowed.".format(up_mode))

        # NOTE: up_mode 'upsample' is incompatible with merge_mode 'add'
        if self.up_mode == 'upsample' and self.merge_mode == 'add':
            raise ValueError("up_mode \"upsample\" is incompatible "
                             "with merge_mode \"add\" at the moment "
                             "because it doesn't make sense to use "
                             "nearest neighbour to reduce "
                             "depth channels (by half).")
        self.mean = config['MEAN']
        self.std = config['STD']
        self.in_channels = config['IN_CHANNELS']
        self.depth = config['DEPTH']
        self.patch_size = config['PRED_PATCH_SIZE']
        self.overlap = config['OVERLAP']
        self.augment_data = config['AUGMENT_DATA']
        self.start_filts = config['START_FILTS']
        self.up_mode = config['UP_MODE']
        self.merge_mode = config['MERGE_MODE']
        self.device = torch.device(config['DEVICE'])

        AbstractUNet._verify_depth_config(self.depth, self.patch_size,
                                          self.overlap)

        self.down_convs = []
        self.up_convs = []

        self.noiseSTD = nn.Parameter(data=torch.log(torch.tensor(0.5)))
        self.noiseSTD.requires_grad = False

        # create the encoder pathway and add to a list
        for i in range(self.depth):
            ins = self.in_channels if i == 0 else outs
            outs = self.start_filts*(2**i)
            pooling = True if i < self.depth-1 else False
            down_conv = DownConv(ins, outs, pooling=pooling)
            self.down_convs.append(down_conv)

        # create the decoder pathway and add to a list
        # - careful! decoding only requires depth-1 blocks
        for i in range(self.depth-1):
            ins = outs
            outs = ins // 2
            up_conv = UpConv(ins, outs, up_mode=up_mode,
                             merge_mode=merge_mode)
            self.up_convs.append(up_conv)

        # add the list of modules to current module
        self.down_convs = nn.ModuleList(self.down_convs)
        self.up_convs = nn.ModuleList(self.up_convs)

        self._build_network_head(outs)

        self.reset_params()

        # Push network to respective device
        self.to(self.device)

        if config.get('FREEZE_WEIGHTS', False):
            for param in self.parameters():
                param.requires_grad = False

    def _build_network_head(self, outs):
        raise NotImplementedError

    @staticmethod
    def _verify_depth_config(depth, patch_size, overlap):
        while depth > 0:
            patch_size = patch_size / 2.0
            depth -= 1
        assert patch_size.is_integer(), 'Patch size must be [depth]-many times'+\
                                        ' divisible by 2.'
        # No idea what to do with overlap...

    @staticmethod
    def weight_init(m):
        if isinstance(m, nn.Conv2d):
            init.xavier_normal_(m.weight)
            init.constant_(m.bias, 0)

    def reset_params(self):
        for _, m in enumerate(self.modules()):
            self.weight_init(m)

    def params_for_key(self, key):
        """This function returns the parameters of this network corresponding to
        the specified key. This is to enable different learning rates throughout
        the network. For example, a network could return its subnetwork's parameters
        for the key "LEARNING_RATE_SUBNETWORK_1". The appropriate learning rate
        gets automatically set by the trainer.

        Can be overwritten by subclasses to return custom parameters for custom
        param keys.

        Arguments:
            key {string} -- the key of the parameters
        """
        if key != "LEARNING_RATE":
            raise ValueError("Unrecognized learning rate.")
        return self.parameters()

    def loss_function(self, result):
        """The loss function of this network.

        Arguments:
            result {dict} -- dictionary containting at least the keys 'output',
                             'gt', 'mask'. Might contain more keys depending
                             one the implementation of the subclass.
        """
        raise NotImplementedError

    def forward(self, x):
        """Runs this network on the given input x.

        Arguments:
            x {torch.Tensor} -- the input image to process, must be of shape
                                [batch_size, C, H, W]

        Returns:
            {torch.Tensor} -- the denoised image
            NOTE -- subclasses might return additional artifacts
        """
        raise NotImplementedError

    def training_predict(self, sample):
        """Performs a forward step during training.

        Arguments:
            train_data {dict} -- Dictionary containting at least the keys 'raw',
                                 'gt' and 'mask'. Subclasses might require
                                 additional keys.

        Returns:
            {dict} -- containing at leas the keys 'output', 'gt' and 'mask
        """
        raise NotImplementedError

    def predict(self, image):
        """Performs prediction on the whole image. Since we have certain memory
        constraints on the GPU it is important to define an appropriate patch
        size. The overlap defines the overlap between patches.

        Arguments:
            image {torch.Tensor} -- 2D image (grayscale or RGB), must be of
                                    shape [batch_size, C, H, W]

        Returns:
            {np.array} -- the denoised image of shape [H, W, C]
            NOTE -- subclasses might return additional artifacts
        """
        data = self._pre_process_predict(image)
        image_height = image.shape[-2]
        image_width = image.shape[-1]
        xmin = 0
        ymin = 0
        xmax = self.patch_size
        ymax = self.patch_size
        ovLeft = 0
        # Image is in [C, H, W] shape
        while (xmin < image_width):
            ovTop = 0
            while (ymin < image_height):
                # We do not receive anything from this method because it
                # modifies the data in-place
                self._process_patch(data, ymin, ymax, xmin, xmax, ovTop, ovLeft)
                ymin = ymin-self.overlap+self.patch_size
                ymax = ymin+self.patch_size
                ovTop = self.overlap//2
            ymin = 0
            ymax = self.patch_size
            xmin = xmin-self.overlap+self.patch_size
            xmax = xmin+self.patch_size
            ovLeft = self.overlap//2
        return self._post_process_predict(data)

    def _pre_process_predict(self, image):
        """Subclasses are supposed to prepare needed variables to perform
        the actual prediction.

        Returns:
            {dict} -- dictionary containing all the necessary variables
        """
        raise NotImplementedError

    def _process_patch(self, data, ymin, ymax, xmin, xmax, ovTop, ovLeft):
        """Performs prediction on the specified patch coordinates given the
        specified data dictionary. This method is for internal use of the
        prediction pipeline only.

        NOTE: Does not return anything because it modifies the data in-place.

        Arguments:
            data {dict} -- a dictionary with the data to work on
            ymin {int} -- y minimum of patch
            ymax {int} -- y maximum of patch
            xmin {int} -- x minimum of patch
            xmax {int} -- x maximum of patch
            ovTop {int} -- overlap top
            ovLef {int} -- overlap left
        """
        raise NotImplementedError

    def predict_patch(self, patch):
        """Performs prediction on the specified patch without any further
        processing.

        Arguments:
            patch {torch.Tensor} -- the patch to process of shape
                                    [batch_size, C, H, W]
        """
        raise NotImplementedError

    def _post_process_predict(self, result):
        """In this method subclasses can assemble the final results of the
        prediction.

        Arguments:
            result {dict} -- containing all the keys that the subclasses need

        Returns:
            {dict}        -- a dictionary containing the final results
        """
        raise NotImplementedError


from gi.repository import RwcalYang
from gi.repository.RwTypes import RwStatus
import logging
import rw_peas
import rwlogger
import time
import argparse
import os
import sys
import uuid
from os.path import basename

FLAVOR_NAME = 'm1.medium'
DEFAULT_IMAGE='/net/sharedfiles/home1/common/vm/rift-root-latest.qcow2'

persistent_resources = {
    'vms'      : ['mission_control','launchpad',],
    'networks' : ['public', 'private', 'multisite'],
    'flavors'  : ['m1.tiny', 'm1.small', 'm1.medium', 'm1.large', 'm1.xlarge'],
    'images'   : ['rwimage','rift-root-latest.qcow2','rift-root-latest-trafgen.qcow2', 'rift-root-latest-trafgen-f.qcow2']
}

#
# Important information about openstack installation. This needs to be manually verified
#
openstack_info = {
    'username'           : 'pluto',
    'password'           : 'mypasswd',
    'project_name'       : 'demo',
    'mgmt_network'       : 'private',
    'physical_network'   : 'physnet1',
    'network_type'       : 'VLAN',
    'segmentation_id'    : 42, ### What else?
    'subnets'            : ["11.0.0.0/24", "12.0.0.0/24", "13.0.0.0/24", "14.0.0.0/24"],
    'subnet_index'       : 0,
    }

logging.basicConfig(level=logging.INFO)

USERDATA_FILENAME = os.path.join(os.environ['RIFT_INSTALL'],
                                 'etc/userdata-template')

RIFT_BASE_USERDATA = '''
#cloud-config
runcmd:
 - sleep 5
 - /usr/rift/scripts/cloud/enable_lab
 - /usr/rift/etc/fix_this_vm
'''

try:
    fd = open(USERDATA_FILENAME, 'r')
except Exception as e:
    #logger.error("Received exception during opening of userdata (%s) file. Exception: %s" %(USERDATA_FILENAME, str(e)))
    sys.exit(-1)
else:
    LP_USERDATA_FILE = fd.read()
    # Run the enable lab script when the openstack vm comes up
    LP_USERDATA_FILE += "runcmd:\n"
    LP_USERDATA_FILE += " - /usr/rift/scripts/cloud/enable_lab\n"
    LP_USERDATA_FILE += " - /usr/rift/etc/fix_this_vm\n"

def get_cal_plugin():
    """
    Loads rw.cal plugin via libpeas
    """
    plugin = rw_peas.PeasPlugin('rwcal_openstack', 'RwCal-1.0')
    engine, info, extension = plugin()
    cal = plugin.get_interface("Cloud")
    # Get the RwLogger context
    rwloggerctx = rwlogger.RwLog.Ctx.new("Cal-Log")
    try:
        rc = cal.init(rwloggerctx)
        assert rc == RwStatus.SUCCESS
    except:
        logger.error("ERROR:Cal plugin instantiation failed. Aborting tests")
    else:
        logger.info("Openstack Cal plugin successfully instantiated")
        return cal

def get_cal_account(auth_url):
    """
    Returns cal account
    """
    account                        = RwcalYang.CloudAccount()
    account.account_type           = "openstack"
    account.openstack.key          = openstack_info['username']
    account.openstack.secret       = openstack_info['password']
    account.openstack.auth_url     = auth_url
    account.openstack.tenant       = openstack_info['project_name']
    account.openstack.mgmt_network = openstack_info['mgmt_network']
    return account

logger = logging.getLogger('rift.cal.openstackresources')

class OpenstackResources(object):
    """
    A stupid class to manage bunch of openstack resources
    """
    def __init__(self, controller):
        self._cal      = get_cal_plugin()
        self._acct     = get_cal_account('http://'+controller+':5000/v3/')
        self._id       = 0
        self._image_id = None
        self._flavor_id = None

    def _destroy_vms(self):
        """
        Destroy VMs
        """
        logger.info("Initiating VM cleanup")
        rc, rsp = self._cal.get_vdu_list(self._acct)
        vdu_list = [vm for vm in rsp.vdu_info_list if vm.name not in persistent_resources['vms']]
        logger.info("Deleting VMs : %s" %([x.name for x in vdu_list]))

        for vdu in vdu_list:
            self._cal.delete_vdu(self._acct, vdu.vdu_id)

        logger.info("VM cleanup complete")

    def _destroy_networks(self):
        """
        Destroy Networks
        """
        logger.info("Initiating Network cleanup")
        rc, rsp = self._cal.get_virtual_link_list(self._acct)
        vlink_list = [vlink for vlink in rsp.virtual_link_info_list if vlink.name not in persistent_resources['networks']]

        logger.info("Deleting Networks : %s" %([x.name for x in vlink_list]))
        for vlink in vlink_list:
            self._cal.delete_virtual_link(self._acct, vlink.virtual_link_id)
        logger.info("Network cleanup complete")

    def _destroy_flavors(self):
        """
        Destroy Flavors
        """
        logger.info("Initiating flavor cleanup")
        rc, rsp = self._cal.get_flavor_list(self._acct)
        flavor_list = [flavor for flavor in rsp.flavorinfo_list if flavor.name not in persistent_resources['flavors']]

        logger.info("Deleting flavors : %s" %([x.name for x in flavor_list]))

        for flavor in flavor_list:
            self._cal.delete_flavor(self._acct, flavor.id)

        logger.info("Flavor cleanup complete")

    def _destroy_images(self):
        logger.info("Initiating image cleanup")
        rc, rsp = self._cal.get_image_list(self._acct)
        image_list = [image for image in rsp.imageinfo_list if image.name not in persistent_resources['images']]

        logger.info("Deleting images : %s" %([x.name for x in image_list]))

        for image in image_list:
            self._cal.delete_image(self._acct, image.id)

        logger.info("Image cleanup complete")

    def destroy_resource(self):
        """
        Destroy resources
        """
        logger.info("Cleaning up openstack resources")
        self._destroy_vms()
        self._destroy_networks()
        self._destroy_flavors()
        self._destroy_images()
        logger.info("Cleaning up openstack resources.......[Done]")

    def create_mission_control(self):
        vm_id = self.create_vm('mission_control',
                               userdata = RIFT_BASE_USERDATA)
        return vm_id

    def create_launchpad_vm(self, salt_master=None):
        node_id = str(uuid.uuid4())
        if salt_master is not None:
           userdata = LP_USERDATA_FILE.format(master_ip = salt_master,
                                           lxcname = node_id)
        else:
           userdata = RIFT_BASE_USERDATA

        vm_id = self.create_vm('launchpad',
                              userdata = userdata,
                              node_id = node_id)
#        vm_id = self.create_vm('launchpad2',
#                               userdata = userdata,
#                               node_id = node_id)
        return vm_id

    def create_vm(self, name, userdata, node_id = None):
        """
        Creates a VM. The VM name is derived from username

        """
        vm = RwcalYang.VDUInitParams()
        vm.name = name
        vm.flavor_id = self._flavor_id
        vm.image_id  = self._image_id
        if node_id is not None:
            vm.node_id = node_id
        vm.vdu_init.userdata = userdata
        vm.allocate_public_address = True
        logger.info("Starting a VM with parameter: %s" %(vm))

        rc, vm_id = self._cal.create_vdu(self._acct, vm)
        assert rc == RwStatus.SUCCESS
        logger.info('Created vm: %s with id: %s', name, vm_id)
        return vm_id

    def create_network(self, name):
        logger.info("Creating network with name: %s" %name)
        network                = RwcalYang.NetworkInfoItem()
        network.network_name   = name
        network.subnet         = openstack_info['subnets'][openstack_info['subnet_index']]

        if openstack_info['subnet_index'] == len(openstack_info['subnets']):
            openstack_info['subnet_index'] = 0
        else:
            openstack_info['subnet_index'] += 1

        if openstack_info['physical_network']:
            network.provider_network.physical_network = openstack_info['physical_network']
        if openstack_info['network_type']:
            network.provider_network.overlay_type     = openstack_info['network_type']
        if openstack_info['segmentation_id']:
            network.provider_network.segmentation_id  = openstack_info['segmentation_id']
            openstack_info['segmentation_id'] += 1

        rc, net_id = self._cal.create_network(self._acct, network)
        assert rc == RwStatus.SUCCESS

        logger.info("Successfully created network with id: %s" %net_id)
        return net_id

    def create_image(self, location):
        img = RwcalYang.ImageInfoItem()
        img.name = basename(location)
        img.location = location
        img.disk_format = "qcow2"
        img.container_format = "bare"

        logger.info("Uploading image : %s" %img.name)
        rc, img_id = self._cal.create_image(self._acct, img)
        assert rc == RwStatus.SUCCESS

        rs = None
        rc = None
        image = None
        for i in range(100):
            rc, rs = self._cal.get_image(self._acct, img_id)
            assert rc == RwStatus.SUCCESS
            logger.info("Image (image_id: %s) reached status : %s" %(img_id, rs.state))
            if rs.state == 'active':
                image = rs
                break
            else:
                time.sleep(2) # Sleep for a second

        if image is None:
            logger.error("Failed to upload openstack image: %s", img)
            sys.exit(1)

        self._image_id = img_id
        logger.info("Uploading image.......[Done]")

    def create_flavor(self):
        """
        Create Flavor suitable for rift_ping_pong VNF
        """
        flavor = RwcalYang.FlavorInfoItem()
        flavor.name = FLAVOR_NAME
        flavor.vm_flavor.memory_mb   = 16384 # 16GB
        flavor.vm_flavor.vcpu_count  = 4
        flavor.vm_flavor.storage_gb  = 20 # 20 GB

        logger.info("Creating new flavor. Flavor Info: %s" %str(flavor.vm_flavor))

        rc, flavor_id = self._cal.create_flavor(self._acct, flavor)
        assert rc == RwStatus.SUCCESS
        logger.info("Creating new flavor.......[Done]")
        return flavor_id

    def find_image(self, name):
        logger.info("Searching for uploaded image: %s" %name)
        rc, rsp = self._cal.get_image_list(self._acct)
        image_list = [image for image in rsp.imageinfo_list if image.name ==  name]

        if not image_list:
            logger.error("Image %s not found" %name)
            return None

        self._image_id = image_list[0].id
        logger.info("Searching for uploaded image.......[Done]")
        return self._image_id

    def find_flavor(self, name=FLAVOR_NAME):
        logger.info("Searching for required flavor: %s" %name)
        rc, rsp = self._cal.get_flavor_list(self._acct)
        flavor_list = [flavor for flavor in rsp.flavorinfo_list if flavor.name == name]

        if not flavor_list:
            logger.error("Flavor %s not found" %name)
            self._flavor_id = self.create_flavor()
        else:
            self._flavor_id = flavor_list[0].id

        logger.info("Searching for required flavor.......[Done]")
        return self._flavor_id

def main():
    """
    Main routine
    """
    parser = argparse.ArgumentParser(description='Script to manage openstack resources')

    parser.add_argument('--controller',
                        action = 'store',
                        dest = 'controller',
                        type = str,
                        help='IP Address of openstack controller. This is mandatory parameter')

    parser.add_argument('--cleanup',
                        action = 'store',
                        dest = 'cleanup',
                        nargs = '+',
                        type = str,
                        help = 'Perform resource cleanup for openstack installation. \n Possible options are {all, flavors, vms, networks, images}')

    parser.add_argument('--persist-vms',
                        action = 'store',
                        dest = 'persist_vms',
                        nargs = '+',
                        type = str,
                        help = 'VM instance name to persist')

    parser.add_argument('--salt-master',
                        action = 'store',
                        dest = 'salt_master',
                        type = str,
                        help='IP Address of salt controller. Required, if VMs are being created.')

    parser.add_argument('--upload-image',
                        action = 'store',
                        dest = 'upload_image',
                        help='Openstack image location to upload and use when creating vms.x')

    parser.add_argument('--use-image',
                        action = 'store',
                        dest = 'use_image',
                        help='Image name to be used for VM creation')

    parser.add_argument('--use-flavor',
                        action = 'store',
                        dest = 'use_flavor',
                        help='Flavor name to be used for VM creation')

    parser.add_argument('--mission-control',
                        action = 'store_true',
                        dest = 'mission_control',
                        help='Create Mission Control VM')

    parser.add_argument('--launchpad',
                        action = 'store_true',
                        dest = 'launchpad',
                        help='Create LaunchPad VM')

    parser.add_argument('--use-project',
                        action = 'store',
                        dest = 'use_project',
                        help='Project name to be used for VM creation')

    argument = parser.parse_args()

    if argument.persist_vms is not None:
        for vm_name in argument.persist_vms:
            vm_name_list = vm_name.split(',')
            for single_vm in vm_name_list:
                persistent_resources['vms'].append(single_vm)
        print("persist: {}".format(persistent_resources['vms']))
        #sys.exit(0)

    if argument.controller is None:
        logger.error('Need openstack controller IP address')
        sys.exit(-1)

    if argument.use_project is not None:
        openstack_info['project_name'] = argument.use_project

    ### Start processing
    logger.info("Instantiating cloud-abstraction-layer")
    drv = OpenstackResources(argument.controller)
    logger.info("Instantiating cloud-abstraction-layer.......[Done]")

    if argument.cleanup is not None:
        for r_type in argument.cleanup:
            if r_type == 'all':
                drv.destroy_resource()
                break
            if r_type == 'images':
                drv._destroy_images()
            if r_type == 'flavors':
                drv._destroy_flavors()
            if r_type == 'vms':
                drv._destroy_vms()
            if r_type == 'networks':
                drv._destroy_networks()

    if argument.upload_image is not None:
        drv.create_image(argument.upload_image)
    elif argument.use_image is not None:
        img = drv.find_image(argument.use_image)
        if img == None:
            logger.error("Image: %s not found" %(argument.use_image))
            sys.exit(-4)
    else:
        if argument.mission_control or argument.launchpad:
            img = drv.find_image(basename(DEFAULT_IMAGE))
            if img == None:
                drv.create_image(DEFAULT_IMAGE)

    if argument.use_flavor is not None:
        drv.find_flavor(argument.use_flavor)
    else:
        drv.find_flavor()

    if argument.mission_control == True:
        drv.create_mission_control()

    if argument.launchpad == True:
        drv.create_launchpad_vm(salt_master = argument.salt_master)

if __name__ == '__main__':
    main()