import pygame

from math import dist, cos, sin, atan2, pi

class Laby:
    L1=1.50
    L2=2.00
    L3=0.20
    L4=0.40
    L5=0.20
    L6=0.40
    L7=L4+L2-L5-L6
    L8=0.40
    L9=0.40
    L10=0.40
    L11=1.10
    L12=2.95
    L13=0.15
    L14=0.40
    L15=0.90
    L16=0.20
    L17=0.20
    walls = [
        ((0,0),(0,L17)),
        #((0,L17+L4),(0,L17+L4+L2)),
        ((0,0),(L8,0)),
        ((L8,0),(L8,L7)),
        ((L8,L7),(L8+L3,L7)),
        ((L8+L3,L7),(L8+L3,0)),
        ((L8+L3,0),(L8+L3+L9,0)),
        ((L8+L3+L9,0),(L8+L3+L9,L17)),
        
        ((0,L17+L4),(0,L17+L4+L2)),
        ((0,L17+L4+L2),(L8,L17+L4+L2)),
        ((L8,L17+L4+L2),(L8,L17+L4+L2-L5)),
        ((L8,L17+L4+L2-L5),(L8+L3,L17+L4+L2-L5)),
        ((L8+L3,L17+L4+L2-L5),(L8+L3,L17+L4+L2)),
        ((L8+L3,L17+L4+L2),(L8+L3+L9,L17+L4+L2)),
        ((L8+L3+L9,L17+L4+L2),(L8+L3+L9,L17+L10)),
        
        ((L8+L3+L9,L17),(L8+L3+L9+L14,L17+L13)),
        ((L8+L3+L9+L14,L17+L13),(L8+L3+L9+L11-L16,L17)),
        ((L8+L3+L9,L17+L10),(L8+L3+L9+L14,L17+L10+L13)),
        ((L8+L3+L9+L14,L17+L10+L13),(L8+L3+L9+L11-L16,L17+L10)),
    ]
    
    def __init__(self, pos):
        self.pos = pos
        self.walls = [([w+p for w,p in zip(l[0], self.pos)],[w+p for w,p in zip(l[1], self.pos)]) for l in self.walls]
        
    def draw(self, surf):
        for l in self.walls:
            pygame.draw.line(surf, (255,255,255), [100*wp for wp in l[0]], [100*wp for wp in l[1]])
            
    def get_closest_wall_dist(self, ray):
        inters = [self.get_inter(ray,w) for w in self.walls]
        inters = list(filter(None, inters))
        if len(inters) == 0:
            return None
        dists = [dist(ray[0], inter) for inter in inters]
        index_min = min(range(len(dists)), key=dists.__getitem__)
        closest = inters[index_min]
        return closest
        
    def get_closest_wall_dist(self, center, dir, lgt, scatter):
        inters = [self.get_inter_area(center, dir, lgt, scatter, w) for w in self.walls]
        inters = list(filter(None, inters))
        if len(inters) == 0:
            return None
        return self.closest_point(center, inters)
        
    def get_inter(self,l1,l2):
        (x1,y1),(x2,y2) = l1
        (x3,y3),(x4,y4) = l2
        # https://en.wikipedia.org/wiki/Line–line_intersection#Given_two_points_on_each_line_segment
        tn = (x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)
        td = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
        un = (x1-x3)*(y1-y2) - (y1-y3)*(x1-x2)
        ud = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
        if td == 0 or ud == 0:
            return None
        t = tn/td
        u = un/ud
        if not (0 <= t <= 1 and 0 <= u <= 1):
            return None
        pint = (x1+t*(x2-x1),y1+t*(y2-y1))
        return pint
        
    def get_inter_area(self, center, dir, lgt, scatter, l):
        endL = (center[0]+lgt*sin(dir-scatter), center[1]+lgt*cos(dir-scatter))
        endR = (center[0]+lgt*sin(dir+scatter), center[1]+lgt*cos(dir+scatter))
        interL = self.get_inter((center,endL), l)
        interR = self.get_inter((center,endR), l)
        interE = self.get_inter((endL, endR), l)
        inters = [interL, interR, interE]
        inters = list(filter(None, inters))
        line_points_in_area = list(filter(lambda p: self.point_in_area(center, dir, lgt, scatter, p), l))
        inters += line_points_in_area
        if len(inters) == 0:
            return None
        return self.closest_point(center, inters)       
            
    def point_in_area(self, center, dir, lgt, scatter, p):
        pdir = atan2(p[0]-center[0], p[1]-center[1])%(2*pi)
        dirn = (dir)%(2*pi)
        if abs(dirn-pdir)>scatter and abs(dirn-pdir+2*pi)>scatter:
            return False
        if dist(center, p) > lgt:
            return False
        return True
        
    def closest_point(self, pc, ps):
        dists = [dist(pc, p) for p in ps]
        index_min = min(range(len(dists)), key=dists.__getitem__)
        return ps[index_min]
