#!/usr/bin/env python3
# coding=utf-8
#
# Copyright (C) 2005,2007 Aaron Spike, aaron@ekips.org
# - template dxf_outlines.dxf added Feb 2008 by Alvin Penner, penner@vaxxine.com
# - layers, transformation, flattening added April 2008 by Bob Cook, bob@bobcookdev.com
# - added support for dxf R12, Nov. 2008 by Switcher
# - brought together to replace ps2edit version 2018 by Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

from __future__ import absolute_import, print_function, unicode_literals

import re

import inkex
import inkex.units
from inkex.bezier import cspsubdiv

r12_header = """ 0 
SECTION
 2 
HEADER
 9 
$ACADVER
 1 
AC1009
 9 
$EXTMIN
 10 
 0 
 20 
 0 
 9 
$EXTMAX
 10 
 8.5 
 20 
 11 
 0 
ENDSEC
 0 
SECTION
 2 
ENTITIES
"""

r12_footer = """ 0 
ENDSEC
 0 
EOF"""


class DxfTwelve(inkex.OutputExtension):
    """Create dxf12 output from the svg"""

    def __init__(self):
        super(DxfTwelve, self).__init__()
        self.handle = 255
        self.flatness = 0.1

    def dxf_add(self, line):
        self._stream.write(line.encode("utf-8"))

    def dxf_insert_code(self, code, value):
        self.dxf_add(code + "\n" + value + "\n")

    def dxf_line(self, layer, csp):
        self.dxf_insert_code("0", "LINE")
        self.dxf_insert_code("8", layer)
        # self.dxf_insert_code(  '62', '1' )  #Change the Line Color
        self.dxf_insert_code("10", "{:f}".format(csp[0][0]))
        self.dxf_insert_code("20", "{:f}".format(csp[0][1]))
        self.dxf_insert_code("11", "{:f}".format(csp[1][0]))
        self.dxf_insert_code("21", "{:f}".format(csp[1][1]))

    def dxf_path_to_lines(self, layer, p):
        f = self.flatness
        is_flat = 0
        while is_flat < 1:
            try:
                cspsubdiv(p, self.flatness)
                is_flat = 1
            except:
                f += 0.1

        for sub in p:
            for i in range(len(sub) - 1):
                self.handle += 1
                s = sub[i]
                e = sub[i + 1]
                self.dxf_line(layer, [s[1], e[1]])

    def dxf_path_to_point(self, layer, p):
        bbox = inkex.Path(p).bounding_box() or inkex.BoundingBox(0, 0)
        x, y = bbox.center
        self.dxf_point(layer, x, y)

    def save(self, stream):
        # This extension only supports paths. Everything is converted to path.
        if len(self.svg.xpath("//svg:use|//svg:flowRoot|//svg:text")) > 0:
            self.preprocess(["flowRoot", "text"])

        self._stream = stream
        self.dxf_insert_code("999", '"DXF R12 Output" (www.mydxf.blogspot.com)')
        self.dxf_add(r12_header)

        # Scale, but assume that the viewport is based on mm (dxf expects mm)
        scale = self.svg.scale / inkex.units.convert_unit("1mm", "px")
        h = self.svg.viewbox_height

        for node in self.svg.iterdescendants():
            if not isinstance(
                node,
                (
                    inkex.PathElement,
                    inkex.Rectangle,
                    inkex.Circle,
                    inkex.Polygon,
                    inkex.Polyline,
                    inkex.Ellipse,
                    inkex.Line,
                ),
            ):
                continue
            visible = True
            for parent in node.iterancestors():
                if isinstance(parent, (inkex.ClipPath, inkex.Mask)):
                    visible = False
                    break
            if not visible:
                continue

            layer = (
                node.getparent().label
            )  # TODO this assumes that all elements are direct
            # descendants of layers
            if layer is None:
                layer = "Layer 1"

            node.transform = node.composed_transform()
            node.transform = (
                inkex.Transform([[scale, 0, 0], [0, -scale, h * scale]])
                @ node.transform
            )
            path = node.path.transform(node.transform).to_superpath()

            if re.search("drill$", layer, re.I) is None:
                # if layer == 'Brackets Drill':
                self.dxf_path_to_lines(layer, path)
            else:
                self.dxf_path_to_point(layer, path)

        self.dxf_add(r12_footer)


if __name__ == "__main__":
    DxfTwelve().run()
