import argparse
from pathlib import Path
import numpy as np
from typing import Dict, Any, Optional, Tuple, List, Union
import gradio as gr
from common.utils import (
    matcher_zoo,
    ransac_zoo,
    change_estimate_geom,
    run_matching,
    gen_examples,
    GRADIO_VERSION,
    DEFAULT_RANSAC_METHOD,
    DEFAULT_SETTING_GEOMETRY,
    DEFAULT_RANSAC_REPROJ_THRESHOLD,
    DEFAULT_RANSAC_CONFIDENCE,
    DEFAULT_RANSAC_MAX_ITER,
    DEFAULT_MATCHING_THRESHOLD,
    DEFAULT_SETTING_MAX_FEATURES,
    DEFAULT_DEFAULT_KEYPOINT_THRESHOLD,
)
DESCRIPTION = """
# Image Matching WebUI
This Space demonstrates [Image Matching WebUI](https://github.com/Vincentqyw/image-matching-webui) by vincent qin. Feel free to play with it, or duplicate to run image matching without a queue!
🔎 For more details about supported local features and matchers, please refer to https://github.com/Vincentqyw/image-matching-webui
🚀 All algorithms run on CPU for inference, causing slow speeds and high latency. For faster inference, please download the [source code](https://github.com/Vincentqyw/image-matching-webui) for local deployment.
🐛 Your feedback is valuable to me. Please do not hesitate to report any bugs [here](https://github.com/Vincentqyw/image-matching-webui/issues).
"""
def ui_change_imagebox(choice):
    """
    Updates the image box with the given choice.
    Args:
        choice (list): The list of image sources to be displayed in the image box.
    Returns:
        dict: A dictionary containing the updated value, sources, and type for the image box.
    """
    ret_dict = {
        "value": None,  # The updated value of the image box
        "__type__": "update",  # The type of update for the image box
    }
    if GRADIO_VERSION > "3":
        return {
            **ret_dict,
            "sources": choice,  # The list of image sources to be displayed
        }
    else:
        return {
            **ret_dict,
            "source": choice,  # The list of image sources to be displayed
        }
def ui_reset_state(
    *args: Any,
) -> Tuple[
    Optional[np.ndarray],
    Optional[np.ndarray],
    float,
    int,
    float,
    str,
    Dict[str, Any],
    Dict[str, Any],
    str,
    Optional[np.ndarray],
    Optional[np.ndarray],
    Optional[np.ndarray],
    Dict[str, Any],
    Dict[str, Any],
    Optional[np.ndarray],
    Dict[str, Any],
    str,
    int,
    float,
    int,
]:
    """
    Reset the state of the UI.
    Returns:
        tuple: A tuple containing the initial values for the UI state.
    """
    key: str = list(matcher_zoo.keys())[0]  # Get the first key from matcher_zoo
    return (
        None,  # image0: Optional[np.ndarray]
        None,  # image1: Optional[np.ndarray]
        DEFAULT_MATCHING_THRESHOLD,  # matching_threshold: float
        DEFAULT_SETTING_MAX_FEATURES,  # max_features: int
        DEFAULT_DEFAULT_KEYPOINT_THRESHOLD,  # keypoint_threshold: float
        key,  # matcher: str
        ui_change_imagebox("upload"),  # input image0: Dict[str, Any]
        ui_change_imagebox("upload"),  # input image1: Dict[str, Any]
        "upload",  # match_image_src: str
        None,  # keypoints: Optional[np.ndarray]
        None,  # raw matches: Optional[np.ndarray]
        None,  # ransac matches: Optional[np.ndarray]
        {},  # matches result info: Dict[str, Any]
        {},  # matcher config: Dict[str, Any]
        None,  # warped image: Optional[np.ndarray]
        {},  # geometry result: Dict[str, Any]
        DEFAULT_RANSAC_METHOD,  # ransac_method: str
        DEFAULT_RANSAC_REPROJ_THRESHOLD,  # ransac_reproj_threshold: float
        DEFAULT_RANSAC_CONFIDENCE,  # ransac_confidence: float
        DEFAULT_RANSAC_MAX_ITER,  # ransac_max_iter: int
        DEFAULT_SETTING_GEOMETRY,  # geometry: str
    )
# "footer {visibility: hidden}"
def run(server_name="0.0.0.0", server_port=7860):
    """
    Runs the application.
    Args:
        config (dict): A dictionary containing configuration parameters for the application.
    Returns:
        None
    """
    with gr.Blocks() as app:
        # gr.Markdown(DESCRIPTION)
        with gr.Row():
            with gr.Column(scale=1):
                gr.Image(
                    str(Path(__file__).parent / "assets/logo.webp"),
                    elem_id="logo-img",
                    show_label=False,
                    show_share_button=False,
                    show_download_button=False,
                )
            with gr.Column(scale=3):
                gr.Markdown(DESCRIPTION)
        with gr.Row(equal_height=False):
            with gr.Column():
                with gr.Row():
                    matcher_list = gr.Dropdown(
                        choices=list(matcher_zoo.keys()),
                        value="disk+lightglue",
                        label="Matching Model",
                        interactive=True,
                    )
                    match_image_src = gr.Radio(
                        (
                            ["upload", "webcam", "clipboard"]
                            if GRADIO_VERSION > "3"
                            else ["upload", "webcam", "canvas"]
                        ),
                        label="Image Source",
                        value="upload",
                    )
                with gr.Row():
                    input_image0 = gr.Image(
                        label="Image 0",
                        type="numpy",
                        image_mode="RGB",
                        height=300 if GRADIO_VERSION > "3" else None,
                        interactive=True,
                    )
                    input_image1 = gr.Image(
                        label="Image 1",
                        type="numpy",
                        image_mode="RGB",
                        height=300 if GRADIO_VERSION > "3" else None,
                        interactive=True,
                    )
                with gr.Row():
                    button_reset = gr.Button(value="Reset")
                    button_run = gr.Button(value="Run Match", variant="primary")
                with gr.Accordion("Advanced Setting", open=False):
                    with gr.Accordion("Matching Setting", open=True):
                        with gr.Row():
                            match_setting_threshold = gr.Slider(
                                minimum=0.0,
                                maximum=1,
                                step=0.001,
                                label="Match thres.",
                                value=0.1,
                            )
                            match_setting_max_features = gr.Slider(
                                minimum=10,
                                maximum=10000,
                                step=10,
                                label="Max features",
                                value=1000,
                            )
                        # TODO: add line settings
                        with gr.Row():
                            detect_keypoints_threshold = gr.Slider(
                                minimum=0,
                                maximum=1,
                                step=0.001,
                                label="Keypoint thres.",
                                value=0.015,
                            )
                            detect_line_threshold = gr.Slider(
                                minimum=0.1,
                                maximum=1,
                                step=0.01,
                                label="Line thres.",
                                value=0.2,
                            )
                        # matcher_lists = gr.Radio(
                        #     ["NN-mutual", "Dual-Softmax"],
                        #     label="Matcher mode",
                        #     value="NN-mutual",
                        # )
                    with gr.Accordion("RANSAC Setting", open=True):
                        with gr.Row(equal_height=False):
                            ransac_method = gr.Dropdown(
                                choices=ransac_zoo.keys(),
                                value=DEFAULT_RANSAC_METHOD,
                                label="RANSAC Method",
                                interactive=True,
                            )
                        ransac_reproj_threshold = gr.Slider(
                            minimum=0.0,
                            maximum=12,
                            step=0.01,
                            label="Ransac Reproj threshold",
                            value=8.0,
                        )
                        ransac_confidence = gr.Slider(
                            minimum=0.0,
                            maximum=1,
                            step=0.00001,
                            label="Ransac Confidence",
                            value=DEFAULT_RANSAC_CONFIDENCE,
                        )
                        ransac_max_iter = gr.Slider(
                            minimum=0.0,
                            maximum=100000,
                            step=100,
                            label="Ransac Iterations",
                            value=DEFAULT_RANSAC_MAX_ITER,
                        )
                    with gr.Accordion("Geometry Setting", open=False):
                        with gr.Row(equal_height=False):
                            choice_estimate_geom = gr.Radio(
                                ["Fundamental", "Homography"],
                                label="Reconstruct Geometry",
                                value=DEFAULT_SETTING_GEOMETRY,
                            )
                # collect inputs
                inputs = [
                    input_image0,
                    input_image1,
                    match_setting_threshold,
                    match_setting_max_features,
                    detect_keypoints_threshold,
                    matcher_list,
                    ransac_method,
                    ransac_reproj_threshold,
                    ransac_confidence,
                    ransac_max_iter,
                    choice_estimate_geom,
                ]
                # Add some examples
                with gr.Row():
                    # Example inputs
                    gr.Examples(
                        examples=gen_examples(),
                        inputs=inputs,
                        outputs=[],
                        fn=run_matching,
                        cache_examples=False,
                        label=(
                            "Examples (click one of the images below to Run"
                            " Match)"
                        ),
                    )
                with gr.Accordion("Open for More!", open=False):
                    gr.Markdown(
                        f"""