from typing import Optional, TextIO
from yarnover.pattern import Comment, Instruction, InstructionGroup, RepeatedInstructionGroup, BoundedStitchRepeat, MarkerAction, Pattern, Row, RepeatedRowGroup, RowGroup, RepeatedRow, Section, NamedInstructionGroup, Stitches, Break

def box(c : Optional[str] = None):
    """A -> [A]"""
    if not c:
        return chr(0x2610)
    i = ord(c.upper()) - 65 # [Aa] -> 0 and so on
    return chr(0x1f130 + i)

class MarkdownFormatter:
    def __init__(self, outfile : TextIO):
        self.outfile = outfile
        self.boxes = list()
        self.last_line_blank = True

    def write(self, line : str = "", space_before=False):
        if space_before and not self.last_line_blank:
            self.outfile.write("\n")
        self.last_line_blank = line == ""
        self.outfile.write(line + "\n")

    def format(self, item):
        type_name = type(item).__name__.lower()
        return getattr(self, f"format_{type_name}")(item)

    def format_comment(self, comment : Comment):
        self.write(comment.text, space_before=True)
        self.write()

    def format_pattern(self, pattern : Pattern):
        if pattern.title:
            self.write(f"# {pattern.title}")
            self.write()
        for item in pattern.items:
            self.format(item)

    def format_section(self, section : Section):
        section.finalize()
        if section.title:
            self.write(f"## {section.title}")
            self.write()

        for item in section.items:
            self.format(item)

        first = True
        for line in section.get_diagram():
            row_number = min(row.row_number for row in line)
            box_line = " ".join(box(row.tag[:1]) for row in line if not row.continued)
            self.write(f"{row_number} {box_line}", space_before=first)
            first = False
        self.write()
        self.boxes.clear()

    def format_break(self, _break : Break):
        self.write("---")
                   
    def format_row(self, row : Row):
        cont = " (cont.)" if row.continued else ""
        parts = [f"- Row {row.row_number}{cont}:"]
        item_descriptions = " ".join(self.format(item) for item in row.items)
        parts.append(item_descriptions)
        if row.increase_count:
            parts.append(f"({self.format_increase_count(row.increase_count)})")
        self.write(" ".join(parts))

    def format_repeatedrow(self, repeated_row : RepeatedRow):
        parts = [f"- Rows {repeated_row.from_row}-{repeated_row.to_row}:"]
        item_descriptions = " ".join(self.format(item) for item in repeated_row.row_items)
        parts.append(item_descriptions)
        self.write(" ".join(parts))


    def format_instruction(self, instruction : Instruction):        
        return instruction.code

    def format_stitches(self, stitches : Stitches):        
        if stitches.count != 1:
            return f"{stitches.code}{stitches.count}"
        else:
            return stitches.code

    def format_namedinstructiongroup(self, group : NamedInstructionGroup):
        return group.name

    def format_instructiongroup(self, group : InstructionGroup):
        description = " ".join(self.format(item) for item in group.items)
        return f"[{description}]"

    def format_repeatedinstructiongroup(self, group : RepeatedInstructionGroup):
        if group.repeat_type == "count":
            return f"repeat {group.repeat_count} times"
        elif group.repeat_type in("marker", "end"):
            if group.repeat_count == 0:
                return "once"
            else:
                if group.stitches_left:
                    #return f"repeat to {group.stitches_left} before {group.repeat_type} ({group.repeat_count} times)"
                    return f"t{group.repeat_type[0]}-{group.stitches_left}({group.repeat_count+1})"
                else:
                    #return f"repeat to {group.repeat_type} ({group.repeat_count} times)"
                    return f"t{group.repeat_type[0]}({group.repeat_count+1})"

    def format_markeraction(self, item : MarkerAction):
        if item.action == "sm":
            return "|"
        if item.name:
            return f"{item.action}<{item.name}>"
        else:
            return item.action

    def format_boundedstitchrepeat(self, item : BoundedStitchRepeat):
        subitems = [item.code]
        if item.stitches_left > 0:
            subitems.append(f"-{item.stitches_left}")
        if item.log_count:
            subitems.append(f"({item.count})")
        return "".join(subitems)
 
    def format_repeatedrowgroup(self, group : RepeatedRowGroup):
        repeat_count = len(group.rows)
        row_count = len(group.original_row_group.rows)
        count = repeat_count // row_count
        self.write(f"- Rows {self.format_rows(group.rows)}: Repeat {self.format_rows(group.original_row_group.rows)} again {count} more times")

    def format_rowgroup(self, group : RowGroup):
        for row in group.items:
            self.format(row)

    def format_rows(self, rows):
        row_numbers = [row.row_number for row in rows]
        first_row = min(row_numbers)
        last_row = max(row_numbers)
        if first_row == last_row:
            return f"row {first_row}"
        else:
            return f"rows {first_row}-{last_row}"

    def format_increase_count(self, inc):
        if inc is None:
            return ""
        if inc < -1:
            return f"{-inc} sts decreased"
        if inc == -1:
            return f"{-inc} st decreased"
        if inc == 0:
            return f"no sts increased"
        if inc == 1:
            return f"{inc} st increased"
        if inc > 1:
            return f"{inc} sts increased"
