"""

Hybrid A* path planning

author: Atsushi Sakai (@Atsushi_twi)

"""

import sys
sys.path.append("../ReedsSheppPath/")

import math
import numpy as np
import scipy.spatial
import matplotlib.pyplot as plt
# import reeds_shepp_path_planning as rs
# import heapq

EXTEND_AREA = 5.0  # [m]
H_COST = 1.0

show_animation = True


class Node:

    def __init__(self, xind, yind, yawind, direction, x, y, yaw, directions, steer, cost, pind):
        # store kd-tree
        self.xind = xind
        self.yind = yind
        self.yawind = yawind
        self.direction = direction
        self.xlist = x
        self.ylist = y
        self.yawlist = yaw
        self.directionlist = directions
        self.steer = steer
        self.cost = cost
        self.pind = pind


class KDTree:
    """
    Nearest neighbor search class with KDTree
    """

    def __init__(self, data):
        # store kd-tree
        self.tree = scipy.spatial.cKDTree(data)

    def search(self, inp, k=1):
        """
        Search NN
        inp: input data, single frame or multi frame
        """

        if len(inp.shape) >= 2:  # multi input
            index = []
            dist = []

            for i in inp.T:
                idist, iindex = self.tree.query(i, k=k)
                index.append(iindex)
                dist.append(idist)

            return index, dist
        else:
            dist, index = self.tree.query(inp, k=k)
            return index, dist

    def search_in_distance(self, inp, r):
        """
        find points with in a distance r
        """

        index = self.tree.query_ball_point(inp, r)
        return index


class Config:

    def __init__(self, ox, oy, xyreso, yawreso):
        min_x_m = min(ox) - EXTEND_AREA
        min_y_m = min(oy) - EXTEND_AREA
        max_x_m = max(ox) + EXTEND_AREA
        max_y_m = max(oy) + EXTEND_AREA

        ox.append(min_x_m)
        oy.append(min_y_m)
        ox.append(max_x_m)
        oy.append(max_y_m)

        self.minx = int(min_x_m / xyreso)
        self.miny = int(min_y_m / xyreso)
        self.maxx = int(max_x_m / xyreso)
        self.maxy = int(max_y_m / xyreso)

        self.xw = int(self.maxx - self.minx)
        self.yw = int(self.maxy - self.miny)

        self.minyaw = int(- math.pi / yawreso) - 1
        self.maxyaw = int(math.pi / yawreso)
        self.yaww = int(self.maxyaw - self.minyaw)


def analytic_expantion(current, ngoal, c, ox, oy, kdtree):

    return False, None  # no update


def hybrid_a_star_planning(start, goal, ox, oy, xyreso, yawreso):
    """
    start
    goal
    ox: x position list of Obstacles [m]
    oy: y position list of Obstacles [m]
    xyreso: grid resolution [m]
    yawreso: yaw angle resolution [rad]
    """

    # start[2], goal[2] = rs.pi_2_pi(start[2]), rs.pi_2_pi(goal[2])
    # tox, toy = ox[:], oy[:]

    # obkdtree = KDTree(np.vstack((tox, toy)).T)

    # c = Config(tox, toy, xyreso, yawreso)

    # nstart = Node(int(start[0] / xyreso), int(start[1] / xyreso), int(start[2] / yawreso),
    # True, [start[0]], [start[1]], [start[2]], [True], 0.0, 0.0, -1)
    # ngoal = Node(int(goal[0] / xyreso), int(goal[1] / xyreso), int(goal[2] / yawreso),
    # True, [goal[0]], [goal[1]], [goal[2]], [True], 0.0, 0.0, -1)

    # openList, closedList = {}, {}
    # h = []
    # #  goalqueue = queue.PriorityQueue()
    # pq = []
    # openList[calc_index(nstart, c)] = nstart
    # heapq.heappush(pq, (calc_index(nstart, c), calc_cost(nstart, h, ngoal, c)))

    # while True:
    # if not openList:
    # print("Error: Cannot find path, No open set")
    # return [], [], []

    # c_id, cost = heapq.heappop(pq)
    # current = openList.pop(c_id)
    # closedList[c_id] = current

    # isupdated, fpath = analytic_expantion(
    # current, ngoal, c, ox, oy, obkdtree)

    # #  print(current)

    rx, ry, ryaw = [], [], []

    return rx, ry, ryaw


def calc_cost(n, h, ngoal, c):

    hcost = 1.0

    return (n.cost + H_COST * hcost)


def calc_index(node, c):
    ind = (node.yawind - c.minyaw) * c.xw * c.yw + \
        (node.yind - c.miny) * c.xw + (node.xind - c.minx)

    if ind <= 0:
        print("Error(calc_index):", ind)

    return ind


def main():
    print("Start Hybrid A* planning")

    ox, oy = [], []

    for i in range(60):
        ox.append(i)
        oy.append(0.0)
    for i in range(60):
        ox.append(60.0)
        oy.append(i)
    for i in range(61):
        ox.append(i)
        oy.append(60.0)
    for i in range(61):
        ox.append(0.0)
        oy.append(i)
    for i in range(40):
        ox.append(20.0)
        oy.append(i)
    for i in range(40):
        ox.append(40.0)
        oy.append(60.0 - i)

    # Set Initial parameters
    start = [10.0, 10.0, np.deg2rad(90.0)]
    goal = [50.0, 50.0, np.deg2rad(-90.0)]

    xyreso = 2.0
    yawreso = np.deg2rad(15.0)

    rx, ry, ryaw = hybrid_a_star_planning(
        start, goal, ox, oy, xyreso, yawreso)

    plt.plot(ox, oy, ".k")
    # rs.plot_arrow(start[0], start[1], start[2])
    # rs.plot_arrow(goal[0], goal[1], goal[2])

    plt.grid(True)
    plt.axis("equal")
    plt.show()

    print(__file__ + " start!!")


if __name__ == '__main__':
    main()
