#!/usr/bin/env python3

import argparse
import math
import drawSvg as draw


def is_hoshi(pos, size):
    """
    Is this position a hoshi ?
    """
    for axis in (0, 1):
        if size[axis] > 10:
            if pos[axis] not in (3, round(size[axis]/2)-1, size[axis]-4):
                return False
        elif pos[axis] not in (2, round(size[axis]/2)-1, size[axis]-3):
            return False

    return True


class GobanDraw:
    """
    Drawing a goban and its stones.

    @param config: Config from argparse
    """
    def __init__(self, config) -> None:
        self.config = config
        self.scale = config.resolution
        self.total_size = (
            ((config.size[0]-1)*config.cellsize[0]+
                config.borders[1]+config.borders[3]+
                2*config.stone_radius+2*config.stone_margin)*self.scale,
            ((config.size[1]-1)*config.cellsize[1]+
                config.borders[0]+config.borders[2]+
                2*config.stone_radius+2*config.stone_margin)*self.scale)

        print("Total size: %dx%d" % self.total_size)

        self.recto_drawing = draw.Drawing(*self.total_size)
        self.verso_drawing = draw.Drawing(*self.total_size)
        self.base_drawing = draw.Drawing(*self.total_size)

    def drawEngraving(self) -> None:
        """
        Draw engraving of stones and goban
        """
        if not self.config.engrave:
            return

        engraved_stones = 0
        borders = [b*self.scale for b in self.config.borders]
        cellsize = [s*self.scale for s in self.config.cellsize]
        radius = self.config.stone_radius * self.scale
        margin = self.config.stone_margin * self.scale
        thickness = self.config.thickness * self.scale
        mark_thickness = self.config.mark_thickness * self.scale
        color = self.config.engrave_color
        for x in range(self.config.size[0]):
            for y in range(self.config.size[1]):
                # pos(recto_x, recto_y, verso_x, verso_y)
                pos = (
                    borders[3]+x*cellsize[0]+radius+margin,
                    borders[0]+y*cellsize[1]+radius+margin,
                    self.total_size[0]-borders[3]-x*cellsize[0]-radius-margin,
                    borders[0]+y*cellsize[1]+radius+margin
                )
                # Draw grid on recto
                if x > 0:
                    self.recto_drawing.append(
                        draw.Line(
                            pos[0]-radius-margin,
                            pos[1],
                            pos[0]-cellsize[0]+radius+margin,
                            pos[1],
                            stroke=color,
                            stroke_width=thickness
                        )
                    )
                if y > 0:
                    self.recto_drawing.append(
                        draw.Line(
                            pos[0],
                            pos[1]-radius-margin,
                            pos[0],
                            pos[1]-cellsize[1]+radius+margin,
                            stroke=color,
                            stroke_width=thickness
                        )
                    )
                # Draw engraving on stones (recto and verso)
                if engraved_stones < self.config.engraved_stones:
                    engraved_radius = mark_thickness
                    while engraved_radius < radius:
                        self.recto_drawing.append(
                            draw.Circle(
                                pos[0],
                                pos[1],
                                engraved_radius - mark_thickness/2,
                                fill='none',
                                stroke=color,
                                stroke_width=mark_thickness
                            )
                        )
                        self.verso_drawing.append(
                            draw.Circle(
                                pos[2],
                                pos[3],
                                engraved_radius - mark_thickness/2,
                                fill='none',
                                stroke=color,
                                stroke_width=mark_thickness
                            )
                        )
                        engraved_radius += 2*mark_thickness
                    engraved_stones += 1

                # Draw Hoshi on base
                if is_hoshi((x, y), self.config.size):
                    self.base_drawing.append(
                        draw.Circle(
                            pos[0],
                            pos[1],
                            radius/3,
                            fill='none',
                            stroke=color,
                            stroke_width=thickness
                        )
                    )

    def drawCutting(self) -> None:
        """
        Draw cutting of stones and goban
        """
        if not self.config.cut:
            return

        borders = [b*self.scale for b in self.config.borders]
        cellsize = [s*self.scale for s in self.config.cellsize]
        radius = self.config.stone_radius * self.scale
        margin = self.config.stone_margin * self.scale
        corner_radius = min(borders) / 2
        thickness = self.config.cut_thickness * self.scale
        color = self.config.cut_color

        # Cut corners on recto and base
        if self.config.round_corners:
            self.recto_drawing.append(
                draw.Arc(
                    corner_radius,
                    corner_radius,
                    corner_radius,
                    180,
                    270,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )
            self.base_drawing.append(
                draw.Arc(
                    corner_radius,
                    corner_radius,
                    corner_radius,
                    180,
                    270,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )
            self.recto_drawing.append(
                draw.Arc(
                    self.total_size[0]-corner_radius,
                    corner_radius,
                    corner_radius,
                    270,
                    0,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )
            self.base_drawing.append(
                draw.Arc(
                    self.total_size[0]-corner_radius,
                    corner_radius,
                    corner_radius,
                    270,
                    0,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )
            self.recto_drawing.append(
                draw.Arc(
                    corner_radius,
                    self.total_size[1]-corner_radius,
                    corner_radius,
                    90,
                    180,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )
            self.base_drawing.append(
                draw.Arc(
                    corner_radius,
                    self.total_size[1]-corner_radius,
                    corner_radius,
                    90,
                    180,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )
            self.recto_drawing.append(
                draw.Arc(
                    self.total_size[0]-corner_radius,
                    self.total_size[1]-corner_radius,
                    corner_radius,
                    0,
                    90,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )
            self.base_drawing.append(
                draw.Arc(
                    self.total_size[0]-corner_radius,
                    self.total_size[1]-corner_radius,
                    corner_radius,
                    0,
                    90,
                    stroke=color,
                    stroke_width=thickness,
                    fill='none'
                )
            )

        # Cut stones on recto and holes on base
        for x in range(self.config.size[0]):
            for y in range(self.config.size[1]):
                # pos(recto_x, recto_y)
                pos = (
                    borders[3]+x*cellsize[0]+radius+margin,
                    borders[0]+y*cellsize[1]+radius+margin,
                )

                self.recto_drawing.append(
                    draw.Circle(
                        pos[0],
                        pos[1],
                        radius+margin,
                        fill='none',
                        stroke=color,
                        stroke_width=thickness
                    )
                )
                self.recto_drawing.append(
                    draw.Arc(
                        pos[0],
                        pos[1],
                        radius,
                        90+math.acos(0.75)*360/(2*math.pi),
                        90-math.acos(0.75)*360/(2*math.pi),
                        fill='none',
                        stroke=color,
                        stroke_width=thickness
                    )
                )
                self.recto_drawing.append(
                    draw.Arc(
                        pos[0],
                        pos[1]+3*radius/2,
                        radius,
                        270-math.acos(0.75)*360/(2*math.pi),
                        270+math.acos(0.75)*360/(2*math.pi),
                        fill='none',
                        stroke=color,
                        stroke_width=thickness
                    )
                )
                self.base_drawing.append(
                    draw.Circle(
                        pos[0],
                        pos[1],
                        radius/4,
                        fill='none',
                        stroke=color,
                        stroke_width=thickness
                    )
                )

    def save(self) -> None:
        """
        Save drawing.
        """
        self.recto_drawing.saveSvg(self.config.prefix+'recto.svg')
        self.verso_drawing.saveSvg(self.config.prefix+'verso.svg')
        self.base_drawing.saveSvg(self.config.prefix+'base.svg')

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Create goban and stones for lasercut"
    )
    parser.add_argument(
        '--size', type=int, nargs=2, default=(19, 19),
        help="Size of goban (number of stones)", metavar=('X', 'Y')
    )
    parser.add_argument(
        '--cellsize', type=int, nargs=2, default=(15, 15),
        help="Size of one cell in mm", metavar=('X', 'Y')
    )
    parser.add_argument(
        '--stone-radius', type=int, default=5.5,
        help="Radius of stone in mm"
    )
    parser.add_argument(
        '--stone-margin', type=int, default=1,
        help="Margin of stone in mm"
    )
    parser.add_argument(
        '--borders', type=int, nargs=4, default=(5, 5, 5, 5),
        help="Borders of goban in mm",
        metavar=('UP', 'RIGHT', 'BOTTOM', 'LEFT')
    )
    parser.add_argument(
        '--thickness', type=float, default=2.0,
        help="Thickness of grid in mm"
    )
    parser.add_argument(
        '--mark-thickness', type=float, default=1.5,
        help="Thickness of mark on stone in mm"
    )
    parser.add_argument(
        '--cut-thickness', type=float, default=0.2,
        help="Thickness of cut stroke in mm"
    )
    parser.add_argument(
        '--engrave', default=False, action='store_true',
        help='Enable engraving'
    )
    parser.add_argument(
        '--cut', default=False, action='store_true',
        help='Enable cutting'
    )
    parser.add_argument(
        '--round-corners', default=False, action='store_true',
        help='Round corners for goban'
    )
    parser.add_argument(
        '--engrave-color', type=str, default='#000000',
        help="Engrave color in web format #000000 to #ffffff"
    )
    parser.add_argument(
        '--cut-color', type=str, default='#ff0000',
        help="Cut color in web format #000000 to #ffffff"
    )
    parser.add_argument(
        '--engraved-stones', type=int, default=181,
        help="Number of engraved (black) stones"
    )
    parser.add_argument(
        '--prefix', default='',
        help='Prefix for filenames, ' +
             'create three files named prefixrecto.svg, prefixverso.svg and '+
             'prefixbase.svg'
    )
    parser.add_argument(
        '--resolution', type=int, default=100,
        help="Resolution in pixels/mm"
    )
    conf = parser.parse_args()
    goban = GobanDraw(conf)
    goban.drawEngraving()
    goban.drawCutting()
    goban.save()
