from lib import (
    BLACK_ICON_TEXT_STYLE,
    draw_lanes,
    draw_template,
    drawing_save,
    EXTRA_HEIGHT,
    GREY,
    sect_ths,
    ICON_TEXT_STYLE,
    SMALL_ICON_TEXT_STYLE,
    TEXTURE_COLOR_0,
    TEXTURE_COLOR_1,
    TEXTURE_COLOR_2,
    TEXTURE_COLOR_3,
    TEXTURE_COLOR_4,
    TIME_WIDTH,
    TIME_X,
    TOTAL_DUR,
    WIDTH,
    Y_PAD,
)
import svgwrite

FILENAME = "macroharm.svg"

LANE_HEIGHT = 75
LANE_THICKNESS = 2
LANE_TEXT_X, LANE_TEXT_Y = 12, 15
Y_SPAN = LANE_HEIGHT - LANE_THICKNESS - 2 * Y_PAD

MACROHARMS = [
    # end, tonic, macroharmony
    (15, "F#", "major"),
    (20, "F#", "minor"),
    (24.5, "F#", "melodic major"),
    (32.75, "F#", "minor"),
    (37.5, "F#", "melodic major"),
    (46.25, "F#", "minor"),
    (51, "F#", "melodic major"),
    (1 * 60 + 32, "F#", "minor"),
    (1 * 60 + 45, "F#", "melodic major"),
    (2 * 60 + 5, "F#", "minor"),
    (2 * 60 + 47, "G#", "minor"),
    (3 * 60 + 39, "C#", "minor"),
    (4 * 60 + 21, "E", "major"),
    (4 * 60 + 44, "C#", "minor"),
]
MACROHARMS_FILLED = [
    # end, tonic, macroharmony
    (15, "F#", "major"),
    (20, "F#", "minor"),
    (24.5, "F#", "melodic major"),
    (32.75, "F#", "minor"),
    (37.5, "F#", "melodic major"),
    (46.25, "F#", "minor"),
    (51, "F#", "melodic major"),
    (55, "F#", "minor"),
    (1 * 60 + 16, "F#", "minor"),
    (1 * 60 + 32, "F#", "minor"),
    # (1 * 60 + 33, "F#", "melodic major"),
    (1 * 60 + 45, "F#", "melodic major"),
    # (1 * 60 + 46, "F#", "minor"),
    (2 * 60 + 5, "F#", "minor"),
    # (2 * 60 + 6, "G#", "minor"),
    (2 * 60 + 28, "G#", "minor"),
    (2 * 60 + 47, "G#", "minor"),
    # (2 * 60 + 48, "C#", "minor"),
    (3 * 60 + 11, "C#", "minor"),
    (3 * 60 + 39, "C#", "minor"),
    # (3 * 60 + 40, "E", "major"),
    (4 * 60 + 21, "E", "major"),
    # (4 * 60 + 22, "C#", "minor"),
    (4 * 60 + 44, "C#", "minor"),
]
LANES = [("in C\u266f", 3), ("tonic", 2), ("MH", 7)]

HEIGHT = len(LANES) * LANE_HEIGHT + EXTRA_HEIGHT


def tonic_color(s):
    match s:
        case "F#":
            return TEXTURE_COLOR_0
        case "G#":
            return TEXTURE_COLOR_1
        case "C#":
            return TEXTURE_COLOR_2
        case "E":
            return GREY
        case _:
            raise ValueError(f"Unknown tonic: {s}")


def macroharm_color(s):
    match s:
        case "major":
            return TEXTURE_COLOR_3
        case "minor":
            return TEXTURE_COLOR_2
        case "melodic major":
            return TEXTURE_COLOR_4
        case _:
            raise ValueError(f"Unknown macroharmony: {s}")


def macroharm_str(s):
    match s:
        case "major":
            return "Major", 27
        case "minor":
            return "minor", 28
        case "melodic major":
            return "mel Maj", 38
        case _:
            raise ValueError(f"Unknown macroharmony: {s}")


def macroharm_str_short(s):
    match s:
        case "major":
            return "M", 5
        case "minor":
            return "m", 9
        case "melodic major":
            return "MM", 15
        case _:
            raise ValueError(f"Unknown macroharmony: {s}")


def rna_str(tonic, macroharm):
    def maj(s, macroharm):
        if "major" == macroharm:
            return f"({s.upper()})"

        if "major" in macroharm:
            return s.upper()

        return s.lower()

    def rn(s):
        match tonic:
            case "F#":
                return "iv"
            case "G#":
                return "v"
            case "C#":
                return "i"
            case "E":
                return "\u266diii"
            case _:
                raise ValueError(f"Unknown tonic: {s}")

    return maj(rn(tonic), macroharm)


def rna_m_str(tonic, macroharm):
    def maj(s, macroharm):
        if "major" == macroharm:
            return s.upper().replace("B", "b"), True

        if "major" in macroharm:
            return s.upper().replace("B", "b"), False

        return s.lower(), False

    def rn(s):
        match tonic:
            case "F#":
                return "iv"
            case "G#":
                return "v"
            case "C#":
                return "i"
            case "E":
                return "biii"
            case _:
                raise ValueError(f"Unknown tonic: {s}")

    return maj(rn(tonic), macroharm)


def main():
    dwg = svgwrite.Drawing(filename=FILENAME, size=(WIDTH, HEIGHT), debug=True)

    draw_template(dwg, len(LANES), LANE_HEIGHT)
    draw_lanes(dwg, LANES, LANE_HEIGHT)

    diag_hatch = dwg.add(
        dwg.pattern(
            id="diagHatch1",
            width=10,
            height=10,
            patternTransform="rotate(45 0 0)",
            patternUnits="userSpaceOnUse",
        )
    )
    hatch_line = dwg.line(start=(0, 0), end=(0, 10))
    hatch_line.stroke(color=TEXTURE_COLOR_3, width=6)
    diag_hatch.add(hatch_line)

    prev_end_t, prev_tonic, prev_macroharm = 0, None, None
    prev_tonic_x_start, prev_macroharm_x_start = 0, 0
    for end_t, tonic, macroharm in MACROHARMS + [(float("inf"), None, None)]:
        x_start, x_end = TIME_X + TIME_WIDTH * (
            prev_end_t / TOTAL_DUR
        ), TIME_X + TIME_WIDTH * (end_t / TOTAL_DUR)

        if tonic is not None and macroharm is not None:
            rna_text = rna_str(tonic, macroharm)
            dwg.add(
                dwg.text(
                    rna_text,
                    insert=(
                        x_start + (x_end - x_start) / 2 - 4 * len(rna_text),
                        Y_PAD + Y_SPAN / 2 + 8,
                    ),
                    style=ICON_TEXT_STYLE,
                )
            )

        if tonic != prev_tonic:
            if prev_tonic is not None:
                x_span = x_start - prev_tonic_x_start
                y_start = Y_PAD + LANE_HEIGHT
                tonic_rect = dwg.add(
                    dwg.rect(
                        insert=(prev_tonic_x_start, y_start),
                        size=(x_span, Y_SPAN),
                    )
                )
                tonic_rect.fill(color=tonic_color(prev_tonic), opacity=1.0)

                dwg.add(
                    dwg.text(
                        prev_tonic.replace("#", "\u266f"),
                        insert=(
                            prev_tonic_x_start + x_span / 2 - 6,
                            y_start + Y_SPAN / 2 + 6,
                        ),
                        style=(
                            BLACK_ICON_TEXT_STYLE
                            if prev_tonic == "C#"
                            else ICON_TEXT_STYLE
                        ),
                    )
                )

            prev_tonic_x_start = x_start

        if macroharm != prev_macroharm:
            if prev_macroharm is not None:
                x_span = x_start - prev_macroharm_x_start
                y_start = Y_PAD + 2 * LANE_HEIGHT
                if prev_macroharm == "major":
                    macroharm_rect = dwg.rect(
                        insert=(
                            prev_macroharm_x_start + 2,
                            y_start + 2,
                        ),
                        size=(x_span - 4, Y_SPAN - 4),
                    )
                else:
                    macroharm_rect = dwg.rect(
                        insert=(
                            prev_macroharm_x_start,
                            y_start,
                        ),
                        size=(x_span, Y_SPAN),
                    )
                dwg.add(macroharm_rect)
                macroharm_fill = (
                    "url(#diagHatch1)"
                    if prev_macroharm == "major"
                    else macroharm_color(prev_macroharm)
                )
                macroharm_rect.fill(color=macroharm_fill, opacity=1.0)
                if prev_macroharm == "major":
                    macroharm_rect.stroke(
                        color=TEXTURE_COLOR_3, width=4, opacity=1.0
                    )

                s, s_offset = (
                    macroharm_str_short(prev_macroharm)
                    if x_span < 70
                    else macroharm_str(prev_macroharm)
                )
                dwg.add(
                    dwg.text(
                        s,
                        insert=(
                            prev_macroharm_x_start + x_span / 2 - s_offset,
                            y_start + Y_SPAN / 2 + 7,
                        ),
                        style=(
                            BLACK_ICON_TEXT_STYLE
                            if prev_macroharm == "minor"
                            else (
                                SMALL_ICON_TEXT_STYLE
                                if s == "MM"
                                else ICON_TEXT_STYLE
                            )
                        ),
                    )
                )

            prev_macroharm_x_start = x_start

        prev_end_t = end_t
        prev_tonic = tonic
        prev_macroharm = macroharm

    drawing_save(dwg)

    ts = [0] + [e for e, _, _ in MACROHARMS_FILLED]

    table = """<table>
  <thead>
    <tr>
      <td rowspan="2"></td>"""
    table += sect_ths(ts)

    rnas, tonics, mhs = "", "", ""
    rna_prev = None
    rna_defer = 0
    tonic_prev = None
    tonic_defer = 0
    mh_prev = None
    mh_defer = 0
    for _, tonic, macroharm in MACROHARMS_FILLED + [(TOTAL_DUR, None, None)]:
        rna = rna_m_str(tonic, macroharm) if tonic is not None else None
        if rna == rna_prev:
            rna_defer += 1
        elif rna_prev is not None:
            colspan = rna_defer + 1
            rna_defer = 0
            colspan_text = "" if colspan == 1 else f' colspan="{colspan}"'
            if rna_prev[1]:
                rnas += f'\n      <td{colspan_text} class="center">({{% m "{rna_prev[0]}" %}})</td>'
            else:
                rnas += f'\n      <td{colspan_text} class="center">{{% m "{rna_prev[0]}" %}}</td>'

        if tonic == tonic_prev:
            tonic_defer += 1
        elif tonic_prev is not None:
            colspan = tonic_defer + 1
            tonic_defer = 0
            colspan_text = "" if colspan == 1 else f' colspan="{colspan}"'
            tonics += f'\n      <td{colspan_text} class="center">{{% m "{tonic_prev}" %}}</td>'

        if macroharm == mh_prev:
            mh_defer += 1
        elif mh_prev is not None:
            colspan = mh_defer + 1
            mh_defer = 0
            colspan_text = "" if colspan == 1 else f' colspan="{colspan}"'
            mhs += f'\n      <td{colspan_text} class="center">{mh_prev.replace("major", "Major")}</td>'

        rna_prev = rna
        tonic_prev = tonic
        mh_prev = macroharm

    table += f"""
    <tr>
      <th scope="row">in&nbsp;{{% m "C#" %}}</th>{rnas}
      <td rowspan="3"></td>
    </tr>
    <tr>
      <th scope="row">tonic</th>{tonics}
    </tr>
    <tr>
      <th scope="row">{{% gloss "macroharmony", "macro&shy;harmony" %}}</th>{mhs}
    </tr>
  </tbody>
</table>"""

    print(table)


if __name__ == "__main__":
    main()
