from __future__ import annotations

from typing import Any

import matplotlib.pyplot as plt
from matplotlib.axes import Axes

from parse_args import parse_args


def subaxis(*varargin: Any) -> Axes | None:
    # SUBAXIS Create axes in tiled positions. (just like subplot)
    # Usage:
    #    h=subaxis(rows,cols,cellno[,settings])
    #    h=subaxis(rows,cols,cellx,celly[,settings])
    #    h=subaxis(rows,cols,cellx,celly,spanx,spany[,settings])
    #
    # SETTINGS: Spacing,SpacingHoriz,SpacingVert
    #           Padding,PaddingRight,PaddingLeft,PaddingTop,PaddingBottom
    #           Margin,MarginRight,MarginLeft,MarginTop,MarginBottom
    #           Holdaxis
    #
    #           all units are relative (e.g from 0 to 1)
    #
    #           Abbreviations of parameters can be used. (E.g. MR instead of MarginRight)
    #           (holdaxis means that it wont delete any axes below.)
    #
    #
    # Example:
    #
    #   subaxis(2,1,1,'SpacingVert',0,'MR',0)
    #   plt.imshow(np.arange(1,10).reshape(3,3))
    #   subaxis(2,'p',.02)
    #   plt.imshow(np.arange(1,17).reshape(4,4))
    #
    # 2001 / Aslak Grinsted  (Feel free to modify this code.)
    f = plt.gcf()

    Args = getattr(f, "_subaxis_args", None)
    UserDataArgsOK = isinstance(Args, dict) and all(
        key in Args for key in ("SpacingHorizontal", "Holdaxis", "rows", "cols")
    )
    OKToStoreArgs = Args is None or UserDataArgsOK

    if Args is None or not UserDataArgsOK:
        Args = {
            "Holdaxis": 0,
            "SpacingVertical": 0.05,
            "SpacingHorizontal": 0.05,
            "PaddingLeft": 0,
            "PaddingRight": 0,
            "PaddingTop": 0,
            "PaddingBottom": 0,
            "MarginLeft": 0.1,
            "MarginRight": 0.1,
            "MarginTop": 0.1,
            "MarginBottom": 0.1,
            "rows": None,
            "cols": None,
        }
    else:
        Args = dict(Args)

    Args = parse_args(
        varargin,
        Args,
        {"Holdaxis"},
        {
            "Spacing": ("sh", "sv"),
            "Padding": ("pl", "pr", "pt", "pb"),
            "Margin": ("ml", "mr", "mt", "mb"),
        },
    )

    numeric_arguments = list(Args["NumericArguments"])
    if len(numeric_arguments) > 1:
        Args["rows"] = int(numeric_arguments[0])
        Args["cols"] = int(numeric_arguments[1])
        # remove these 2 numerical arguments
        numeric_arguments = numeric_arguments[2:]
        Args["NumericArguments"] = numeric_arguments

    if OKToStoreArgs:
        f._subaxis_args = dict(Args)

    if len(numeric_arguments) == 0:
        return None  # no arguments but rows/cols....
    if len(numeric_arguments) == 1:
        cellno = int(numeric_arguments[0])
        x1 = (cellno - 1) % int(Args["cols"]) + 1
        x2 = x1
        y1 = (cellno - 1) // int(Args["cols"]) + 1
        y2 = y1
    elif len(numeric_arguments) == 2:
        x1 = int(numeric_arguments[0])
        x2 = x1
        y1 = int(numeric_arguments[1])
        y2 = y1
    elif len(numeric_arguments) == 4:
        x1 = int(numeric_arguments[0])
        x2 = x1 + int(numeric_arguments[2]) - 1
        y1 = int(numeric_arguments[1])
        y2 = y1 + int(numeric_arguments[3]) - 1
    else:
        raise ValueError("subaxis argument error")

    rows = int(Args["rows"])
    cols = int(Args["cols"])
    cellwidth = (
        (1 - Args["MarginLeft"] - Args["MarginRight"])
        - (cols - 1) * Args["SpacingHorizontal"]
    ) / cols
    cellheight = (
        (1 - Args["MarginTop"] - Args["MarginBottom"])
        - (rows - 1) * Args["SpacingVertical"]
    ) / rows
    xpos1 = (
        Args["MarginLeft"]
        + Args["PaddingLeft"]
        + cellwidth * (x1 - 1)
        + Args["SpacingHorizontal"] * (x1 - 1)
    )
    xpos2 = (
        Args["MarginLeft"]
        - Args["PaddingRight"]
        + cellwidth * x2
        + Args["SpacingHorizontal"] * (x2 - 1)
    )
    ypos1 = (
        Args["MarginTop"]
        + Args["PaddingTop"]
        + cellheight * (y1 - 1)
        + Args["SpacingVertical"] * (y1 - 1)
    )
    ypos2 = (
        Args["MarginTop"]
        - Args["PaddingBottom"]
        + cellheight * y2
        + Args["SpacingVertical"] * (y2 - 1)
    )

    position = [xpos1, 1 - ypos2, xpos2 - xpos1, ypos2 - ypos1]
    if Args["Holdaxis"]:
        h = f.add_axes(position)
    else:
        # MATLAB subplot('position',...) deletes overlapping axes.  The converted
        # scripts create nonoverlapping axes, so add_axes is the closest literal equivalent.
        h = f.add_axes(position)

    h.set_frame_on(True)
    h.set_gid("subaxis")
    return h
