import logging

import numpy as np
import torch

from where_fi.application import CSIApplication
from where_fi.collection import CSIMatrix
from where_fi.collection.ingest import RealtimeCSIProducer
from where_fi.config import config
from where_fi.processing.aoa import AoA
from where_fi.visualise import server as visualise

app = CSIApplication(visualise_raw=True)

logging.basicConfig(level=logging.INFO)

T = 500

N_sub1 = config.antennas.count // 2
N_sub2 = config.subcarriers // 2
L2 = config.subcarriers - N_sub2 + 1
L1 = config.antennas.count - N_sub1 + 1
N_sensors = config.subcarriers * config.antennas.count
historical = torch.zeros(T, N_sensors, N_sensors, dtype=torch.complex64)

cnt = 0


@app.on_sample
def _(sample: CSIMatrix) -> None:
    global cnt
    sample_before_resize = sample[:, :, 0].T
    sample_after = sample_before_resize.reshape(-1, 1)
    sample_tensor = torch.tensor(sample_after)
    historical[cnt] = sample_tensor @ torch.conj(sample_tensor).T
    print(
        f"{sample.shape} => {sample_before_resize.shape} => {sample_after.shape} "
        f"=> {historical[cnt].shape}"
    )
    cnt = (cnt + 1) % T


def get_steering(theta: float, tau: float) -> torch.Tensor:
    """
    Calculate the alpha value for the given angle and time delay.
    """
    sub, ant = np.indices((N_sub1, N_sub2))
    alpha = np.exp(
        -1j
        * (
            2 * np.pi * (sub * config.delta_f * tau)
            + 2
            * np.pi
            * (
                ant
                * config.antennas.spacing
                * np.sin(theta)
                * 299_792_458
                / (config.central_freq_hz + (sub - 28) * config.delta_f)
            )
        )
    )
    alpha = sub + 1j * ant
    alpha = alpha.reshape(-1, 1)
    return torch.tensor(alpha, dtype=torch.complex64)


aoa = AoA()


@app.on_process
def _(_: CSIMatrix) -> None:
    R: torch.Tensor = torch.mean(historical, axis=0)
    # Rss = torch.zeros(N_sub1 * N_sub2, N_sub1 * N_sub2, dtype=torch.complex64)
    # for i in range(L1):
    #     for j in range(L2):
    #         Rss += R[i : i + N_sub1 * N_sub2, j : j + N_sub1 * N_sub2]
    # Rss /= L1 * L2
    Rss = R

    aoa.historical_autocorr = torch.unsqueeze(Rss, 0)
    aoa.heatmap(app.visualise_data)

    # eigvals, eigvecs = torch.linalg.eig(Rss)
    # app.visualise_data(eigvals.numpy(), visualise.figures.Figure.MUSIC_EIGENVALUES)
    # E_n = eigvecs[:, torch.abs(eigvals) < config.music.eigval_threshold]
    # E_n_H = torch.conj(E_n.T)

    # heatmap = np.zeros(
    #     (config.music.heatmap.tof_resolution, config.music.heatmap.theta_resolution)
    # )
    # for i_theta, theta in enumerate(
    #     np.linspace(0, np.pi, config.music.heatmap.theta_resolution)
    # ):
    #     for i_tau, tau in enumerate(
    #         np.linspace(
    #             0, config.music.heatmap.tof_max, config.music.heatmap.tof_resolution
    #         )
    #     ):
    #         steering = get_steering(theta, tau)
    #         steering_h = torch.conj(steering.T)
    #         c = 1 / (steering_h @ E_n @ E_n_H @ steering)
    #         heatmap[i_tau, i_theta] = torch.abs(c)
    # print(steering)
    # app.visualise_data(heatmap, visualise.figures.Figure.AOA_HEATMAP)
    # eigvals, eigvecs = torch.linalg.eig(Rss)
    # app.visualise_data(eigvals.numpy(), visualise.figures.Figure.MUSIC_EIGENVALUES)
    # E_n = eigvecs[:, torch.abs(eigvals) < config.music.eigval_threshold]

    # print(E_n)
    # c: torch.Tensor = 1 / (steering_h @ E_n @ E_n_H @ steering)
    # return torch.abs(c)[:, 0, 0]


if __name__ == "__main__":
    producer = RealtimeCSIProducer()
    app.set_producer(producer)
    app.start()
