import gradio as gr
import numpy as np
import polars as pl
import pydicom
from PIL import Image
from pydicom.errors import InvalidDicomError


def read_and_preprocess_dicom(file_path: str):
    """
    Function to read and preprocess DICOM files
    :param file_path: Path to the DICOM file
    :return: Image data (in PIL format) and metadata (in pandas DataFrame format)
    """
    try:
        # Read the DICOM file
        dicom_data = pydicom.dcmread(file_path)
    except InvalidDicomError:
        raise gr.Error("The uploaded file is not a valid DICOM file.")

    # Get the pixel data
    try:
        pixel_array = dicom_data.pixel_array
    except AttributeError:
        raise gr.Error("The uploaded DICOM file has no pixel data.")

    # Normalize the pixel data to 8-bit and convert to a PIL image
    if pixel_array.dtype != np.uint8:
        pixel_array = ((pixel_array - np.min(pixel_array)) / (np.max(pixel_array) - np.min(pixel_array)) * 255).astype(
            np.uint8)
    image = Image.fromarray(pixel_array)

    # Collect metadata in dictionary format and convert to DataFrame
    metadata_dict = {elem.name: str(elem.value) for elem in dicom_data.iterall() if elem.name != 'Pixel Data'}
    df_metadata = pl.DataFrame({
        "Key": list(metadata_dict.keys()),
        "Value": list(metadata_dict.values())
    })

    return image, df_metadata.to_pandas()  # Convert to pandas DataFrame for Gradio compatibility


def build_interface():
    """
    Function to build the Gradio interface
    """
    theme = gr.themes.Soft(
        primary_hue=gr.themes.colors.emerald,
        secondary_hue=gr.themes.colors.emerald
    )

    with gr.Blocks(title='DICOM Viewer', theme=theme) as demo:
        gr.Markdown(
            """
            # DICOM Viewer
            This app reads a DICOM file and displays the image and metadata.
            """
        )
        with gr.Column():
            file_path = gr.File(label="Input DICOM Data")

            with gr.Row():
                dicom_image = gr.Image(type="pil", label="DICOM Image")
                dicom_meta = gr.Dataframe(headers=None, label="Metadata")

            inputs = [file_path]
            outputs = [dicom_image, dicom_meta]
            file_path.upload(fn=read_and_preprocess_dicom, inputs=inputs, outputs=outputs)

            clear_button = gr.ClearButton(components=inputs + outputs, )

            example = gr.Examples(
                ['assets/sample.dcm'],
                inputs=inputs,
                outputs=outputs,
                fn=read_and_preprocess_dicom,
                cache_examples=True
            )

    return demo


if __name__ == '__main__':
    demo = build_interface()
    demo.launch()