import torch
import numpy as np
import tqdm
from dkm.datasets import MegadepthBuilder
from dkm.utils import warp_kpts
from torch.utils.data import ConcatDataset


class MegadepthDenseBenchmark:
    def __init__(self, data_root="data/megadepth", h=384, w=512, num_samples=2000, device=None) -> None:
        mega = MegadepthBuilder(data_root=data_root)
        self.dataset = ConcatDataset(mega.build_scenes(split="test_loftr", ht=h, wt=w))  # fixed resolution of 384,512
        self.num_samples = num_samples
        if device is None:
            device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.device = device

    def geometric_dist(self, depth1, depth2, T_1to2, K1, K2, dense_matches):
        b, h1, w1, d = dense_matches.shape
        with torch.no_grad():
            x1 = dense_matches[..., :2].reshape(b, h1 * w1, 2)
            # x1 = torch.stack((2*x1[...,0]/w1-1,2*x1[...,1]/h1-1),dim=-1)
            mask, x2 = warp_kpts(
                x1.double(),
                depth1.double(),
                depth2.double(),
                T_1to2.double(),
                K1.double(),
                K2.double(),
            )
            x2 = torch.stack((w1 * (x2[..., 0] + 1) / 2, h1 * (x2[..., 1] + 1) / 2), dim=-1)
            prob = mask.float().reshape(b, h1, w1)
        x2_hat = dense_matches[..., 2:]
        x2_hat = torch.stack((w1 * (x2_hat[..., 0] + 1) / 2, h1 * (x2_hat[..., 1] + 1) / 2), dim=-1)
        gd = (x2_hat - x2.reshape(b, h1, w1, 2)).norm(dim=-1)
        gd = gd[prob == 1]
        pck_1 = (gd < 1.0).float().mean()
        pck_3 = (gd < 3.0).float().mean()
        pck_5 = (gd < 5.0).float().mean()
        gd = gd.mean()
        return gd, pck_1, pck_3, pck_5

    def benchmark(self, model, batch_size=8):
        model.train(False)
        with torch.no_grad():
            gd_tot = 0.0
            pck_1_tot = 0.0
            pck_3_tot = 0.0
            pck_5_tot = 0.0
            sampler = torch.utils.data.WeightedRandomSampler(
                torch.ones(len(self.dataset)), replacement=False, num_samples=self.num_samples
            )
            dataloader = torch.utils.data.DataLoader(
                self.dataset, batch_size=8, num_workers=batch_size, sampler=sampler
            )
            for data in tqdm.tqdm(dataloader):
                im1, im2, depth1, depth2, T_1to2, K1, K2 = (
                    data["query"],
                    data["support"],
                    data["query_depth"].to(self.device),
                    data["support_depth"].to(self.device),
                    data["T_1to2"].to(self.device),
                    data["K1"].to(self.device),
                    data["K2"].to(self.device),
                )
                matches, certainty = model.match(im1, im2, batched=True)
                gd, pck_1, pck_3, pck_5 = self.geometric_dist(depth1, depth2, T_1to2, K1, K2, matches)
                gd_tot, pck_1_tot, pck_3_tot, pck_5_tot = (
                    gd_tot + gd,
                    pck_1_tot + pck_1,
                    pck_3_tot + pck_3,
                    pck_5_tot + pck_5,
                )
        return {
            "mega_pck_1": pck_1_tot.item() / len(dataloader),
            "mega_pck_3": pck_3_tot.item() / len(dataloader),
            "mega_pck_5": pck_5_tot.item() / len(dataloader),
        }
