import pygame as pg
import math
from settings import *


class RayCasting:
    def __init__(self, game):
        self.game = game

    def ray_cast(self):
        ox, oy = self.game.player.pos
        x_map, y_map = self.game.player.map_pos

        ray_angle = self.game.player.angle - HALF_FOV + NOT_ZERO_OFFSET
        for ray in range(NUM_RAYS):
            sin_a = math.sin(ray_angle)
            cos_a = math.cos(ray_angle)

            # horizontals
            y_hor, dy = (y_map + 1, 1) if sin_a > 0 else (y_map - SMALL_COEFFICIENT, -1)

            depth_hor = (y_hor - oy) / sin_a
            x_hor = ox + depth_hor * cos_a

            delta_depth = dy / sin_a
            dx = delta_depth * cos_a

            for i in range(MAX_DEPTH):
                tile_hor = int(x_hor), int(y_hor)
                if tile_hor in self.game.map.world_map:
                    # hit a wall
                    break
                x_hor += dx
                y_hor += dy
                depth_hor += delta_depth

            # verticals
            x_vert, dx = (x_map + 1, 1) if cos_a > 0 else (x_map - SMALL_COEFFICIENT, -1)

            depth_vert = (x_vert - ox) / cos_a
            y_vert = oy + depth_vert * sin_a

            delta_depth = dx / cos_a
            dy = delta_depth * sin_a

            for i in range(MAX_DEPTH):
                tile_vert = int(x_vert), int(y_vert)
                if tile_vert in self.game.map.world_map:
                    # hit a wall
                    break
                x_vert += dx
                y_vert += dy
                depth_vert += delta_depth

            # depth
            if depth_vert < depth_hor:
                depth = depth_vert
            else:
                depth = depth_hor

            if DRAW_TWO_D_GAME:
                # draw for debug
                pg.draw.line(self.game.screen, COLOUR_RAYCAST_RAYS, (100 * ox, 100 * oy), (100 * ox + 100 * depth * cos_a, 100 * oy + 100 * depth * sin_a), 2)
            else:

                # remove fishbowl effect
                depth *= math.cos(self.game.player.angle - ray_angle)

                # projection
                proj_height = SCREEN_DIST / (depth + NOT_ZERO_OFFSET)

                # calculate a colour based on distance to wall (ray depth)
                colour = [255 / (1 + depth ** 5 * 0.00002)] * 3
                # draw walls
                # pg.draw.rect(self.game.screen, COLOUR_PSEUDO_THREE_D_WALLS, (ray * SCALE, HALF_HEIGHT - proj_height // 2, SCALE, proj_height))
                pg.draw.rect(self.game.screen, colour, (ray * SCALE, HALF_HEIGHT - proj_height // 2, SCALE, proj_height))

            ray_angle += DELTA_ANGLE

    def update(self):
        self.ray_cast()