from datetime import datetime
from structures import Gantt, Row, Job
from gantt_types import Second, Millisecond
import structures
import utility
import operator

COLORS = [
    "#d50000",
    "#00bfa5",
    "#ff6f00",
    "#aa00ff",
    "#006064",
    "#ffd600",
    "#64dd17",
]

HEADER_COLUMN_WIDTH = 240


def ms_to_px(ms: Millisecond) -> float:
    return ms / 10


def job_to_html(job: Job, start: datetime, color: str) -> str:
    left = ms_to_px(utility.duration_ms(start, job.start)) + HEADER_COLUMN_WIDTH
    width = ms_to_px(structures.job_duration_ms(job))

    return f"""<span class="job" data-descr="{job.name}{chr(10)}Duration: {utility.ms_to_s(structures.job_duration_ms(job)):.2f}s" style="left: {left}px; width: {width}px; background-color: {color}"></span>"""


def row_to_html(
    row: Row, start: datetime, process_num: int, color: str, width: float
) -> str:
    legend_html = f"""<span class="legend" style="width: {HEADER_COLUMN_WIDTH}px">Concurrency Slot {process_num} ({utility.ms_to_s(structures.row_computing_duration_ms(row)):.1f}s)</span>"""

    jobs_html = "\n".join([job_to_html(job, start, color) for job in row.jobs])

    return (
        f"""<div class="row" style="width: {width}px;">{legend_html}{jobs_html}</div>"""
    )


def rownum_to_top(num: int) -> float:
    return num * 2


def make_axis_span(left: float, s: Second) -> str:
    return f"""<span class="axis-tick" style="left: {left}px;">{s} sec</span>"""


def make_axis_html(max_seconds: Second) -> str:
    seconds = [Second(i * 2) for i in range(1000)]

    seconds = [i for i in seconds if i < max_seconds]

    axis_spans = "".join(
        [
            make_axis_span(ms_to_px(utility.s_to_ms(s)) + HEADER_COLUMN_WIDTH, s)
            for s in seconds
        ]
    )

    return f"""<div class="row axis">
    <span class="legend" style="width: {HEADER_COLUMN_WIDTH}px">Total Processing Time</span>
    {axis_spans}
</div>"""


def gantt_to_html(g: Gantt) -> str:
    if not g:
        return ""

    start = min([row.jobs[0].start for row in g])

    max_seconds = max([utility.ms_to_s(structures.row_duration_ms(row)) for row in g])

    rows_html = "\n".join(
        [
            row_to_html(
                row,
                start,
                num + 1,
                COLORS[num % len(COLORS)],
                ms_to_px(utility.s_to_ms(max_seconds)) + HEADER_COLUMN_WIDTH,
            )
            for num, row in enumerate(
                sorted(
                    g,
                    reverse=True,
                    key=lambda r: structures.row_computing_duration_ms(r),
                )
            )
        ]
    )

    return f"""<div class="gantt">{make_axis_html(max_seconds)}{rows_html}</div>"""


def style() -> str:
    with open("./gantt/output.css") as css:
        return f"""<style>{css.read()}</style>"""


def html(g: Gantt) -> str:
    html = f"""
<html>
<head></head>
<body>
<main>
<h1>Gantt Chart</h1>
<p>Max parallelism: {len(g)}</p>
{gantt_to_html(g)}
<h1>Explanation</h1>
<p>
    <ul>
    <li>Each row represents a parallelism "slot"; if "maxParallelism" was 4, then there are 4 rows.</li>
    <li>Each colored block is a job; hover with a mouse to show the name and how long it took.</li>
    <li>Each row shows the total time spent doing jobs to highlight bottlenecks.</li>
    </ul>
</p>
</main>
{style()}
</body>
</html>
"""

    return html if g else ""