from functools import partial

import torch


def get_plucker_embedding(intrinsics, cam_c2w, height, width, height_dit=None, width_dit=None, flip_flag=None):
    custom_meshgrid = partial(torch.meshgrid, indexing="ij")
    batch_size, num_frames = intrinsics.shape[:2]

    use_dit_hw = height_dit is not None and width_dit is not None
    if not use_dit_hw:
        height_dit = height
        width_dit = width
    else:
        patch_height = height / height_dit
        patch_width = width / width_dit

    j, i = custom_meshgrid(
        torch.linspace(0, height_dit - 1, height_dit, device=cam_c2w.device, dtype=cam_c2w.dtype),
        torch.linspace(0, width_dit - 1, width_dit, device=cam_c2w.device, dtype=cam_c2w.dtype),
    )
    i = i.reshape(1, 1, height_dit * width_dit).expand(batch_size, num_frames, height_dit * width_dit) + 0.5
    j = j.reshape(1, 1, height_dit * width_dit).expand(batch_size, num_frames, height_dit * width_dit) + 0.5

    if use_dit_hw:
        i = i * patch_width + (patch_width / 2)
        j = j * patch_height + (patch_height / 2)

    if flip_flag is not None and torch.sum(flip_flag).item() > 0:
        j_flip, i_flip = custom_meshgrid(
            torch.linspace(0, height_dit - 1, height_dit, device=cam_c2w.device, dtype=cam_c2w.dtype),
            torch.linspace(width_dit - 1, 0, width_dit, device=cam_c2w.device, dtype=cam_c2w.dtype),
        )
        i_flip = i_flip.reshape(1, 1, height_dit * width_dit).expand(batch_size, 1, height_dit * width_dit) + 0.5
        j_flip = j_flip.reshape(1, 1, height_dit * width_dit).expand(batch_size, 1, height_dit * width_dit) + 0.5
        if use_dit_hw:
            i_flip = i_flip * patch_width + (patch_width / 2)
            j_flip = j_flip * patch_height + (patch_height / 2)
        i[:, flip_flag, ...] = i_flip
        j[:, flip_flag, ...] = j_flip

    fx, fy, cx, cy = intrinsics.chunk(4, dim=-1)
    zs = torch.ones_like(i)
    xs = (i - cx) / fx * zs
    ys = (j - cy) / fy * zs
    zs = zs.expand_as(ys)

    directions = torch.stack((xs, ys, zs), dim=-1)
    directions = directions / directions.norm(dim=-1, keepdim=True)
    rays_d = directions @ cam_c2w[..., :3, :3].transpose(-1, -2)
    rays_o = cam_c2w[..., :3, 3]
    rays_o = rays_o[:, :, None].expand_as(rays_d)
    rays_dxo = torch.cross(rays_o, rays_d, dim=-1)
    plucker = torch.cat([rays_dxo, rays_d], dim=-1)
    return plucker.reshape(batch_size, num_frames, height_dit, width_dit, 6)

