from OCC.Core.TDocStd import TDocStd_Document
from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool
from OCC.Core.STEPCAFControl import STEPCAFControl_Writer
from OCC.Core.Quantity import Quantity_Color, Quantity_NOC_PERU, Quantity_ColorRGBA

from OCC.Extend.DataExchange import write_stl_file
from OCC.Extend.TopologyUtils import discretize_edge, dump_topology_to_string

from OCC.Core.TopAbs import TopAbs_EDGE
from OCC.Core.TopoDS import TopoDS_Iterator

from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse

from OCC.Core.Interface import Interface_Static_SetCVal
from OCC.Core.IFSelect import IFSelect_RetDone
from OCC.Core.STEPControl import STEPControl_AsIs

import logging


class SCLWriter:
    extensions = []


class SCLSTLWriter(SCLWriter):
    extensions = ['.stl']

    def __init__(self):
        self.linear_deflection = 1.0
        self.angular_deflection = 1.0
        self.mode = "binary"
        self.shapes = []

    def set_resolution(self, linear_deflection=0.5, angular_deflection=0.3):
        self.linear_deflection = linear_deflection
        self.angular_deflection = angular_deflection

    def set_mode(self, mode):
        self.mode = mode

    def Write(self, path):
        if len(self.shapes)>0:
            u = self.shapes[0]
            for s in self.shapes[1:]:
                u = BRepAlgoAPI_Fuse(u, s).Shape()

            write_stl_file(u, path, mode=self.mode, linear_deflection=self.linear_deflection, angular_deflection=self.angular_deflection)

    def Transfer(self, shape):
        self.shapes.append(shape)


class SCLSTEPWriter(SCLWriter):
    extensions = ['.step', '.stp']

    def __init__(self):
        try:
            logging.info("[STEP-Writer] Создание документа TDocStd_Document...")
            self.doc = TDocStd_Document("pythonocc-doc")

            logging.info("[STEP-Writer] Получение ShapeTool...")
            self.shape_tool = XCAFDoc_DocumentTool.ShapeTool(self.doc.Main())

            logging.info("[STEP-Writer] Получение ColorTool...")
            self.color_tool = XCAFDoc_DocumentTool.ColorTool(self.doc.Main())

            logging.info("[STEP-Writer] Настройка схемы AP214...")
            Interface_Static_SetCVal("write.step.schema", "AP214")

            self.writer = STEPCAFControl_Writer()
            logging.info("[STEP-Writer] Конструктор завершен успешно!")
        except Exception as e:
            logging.error(f"[STEP-Writer] ОШИБКА В КОНСТРУКТОРЕ: {e}")
            import traceback
            traceback.print_exc()
            raise e


    def TransferColor(self, shape, rgba_color):
        try:
            if shape is None or shape.IsNull():
                logging.warning("[STEP-Writer] Предупреждение: TopoDS_Shape является Null. Пропускаем.")
                return

            logging.info(f"[STEP-Writer] Экспорт формы {shape} с цветом {rgba_color}...")
            label = self.shape_tool.AddShape(shape, False)

            if rgba_color is not None:
                self.color_tool.SetColor(label, rgba_color, 0)
            else:
                default_color = Quantity_ColorRGBA(Quantity_Color(Quantity_NOC_PERU), 1.0)
                self.color_tool.SetColor(label, default_color, 0)
            logging.info("[STEP-Writer] Экспорт формы завершен.")
        except Exception as e:
            logging.error(f"[STEP-Writer] ОШИБКА ПРИ ЭКСПОРТЕ ДЕТАЛИ: {e}")
            import traceback
            traceback.print_exc()
            raise e

    def Transfer(self, shape):
        self.TransferColor(shape, None)

    def Write(self, path):
        try:
            logging.info("[STEP-Writer] Передача OCAF-документа в транслятор STEP...")
            self.writer.Transfer(self.doc, STEPControl_AsIs)

            logging.info(f"[STEP-Writer] Физическая запись файла в {path}...")
            status = self.writer.Write(path)
            if status != IFSelect_RetDone:
                raise AssertionError("Write failed")
            logging.info("[STEP-Writer] Запись файла завершена успешно!")
        except Exception as e:
            logging.error(f"[STEP-Writer] ОШИБКА ПРИ ЗАПИСИ ФАЙЛА: {e}")
            import traceback
            traceback.print_exc()
            raise e


class SCLDXFWriter(SCLWriter):
    extensions = ['.dxf']

    def __init__(self):
        import ezdxf
        self.doc = ezdxf.new('R2010')
        self.msp = self.doc.modelspace()
        self.shapes = []

    def collect_dumpable_shapes(self, shape):
        if shape.ShapeType() == TopAbs_EDGE:
            points = []
            try:
                points = discretize_edge(shape, 0.25)
            except Exception:
                points = []
            else:
                debug("Discretize done !")
            return points
        else:
            lines = []
            it = TopoDS_Iterator(shape)
            while it.More():
                shp = it.Value()
                lines.append(self.collect_dumpable_shapes(shp))
                it.Next()
            return lines

    def Write(self, path):
        from bcad.binterpreter.scl_context import SCLProjection
        p = SCLProjection(None)
        p.hlr_project(self.shapes)
        shapes = []
        for c in p.children:
            if c.shape is not None:
                dump_topology_to_string(c.shape.get_shape())
                shapes.append(c.shape.get_shape())
        lines = []
        for s in shapes:
            lines.extend(self.collect_dumpable_shapes(s))

        for ln in lines:
            if len(ln)>1:
                s = ln[0][:2]
                for p in ln[1:]:
                    e = p[:2]
                    self.msp.add_line(s, e)
                    s = e
        self.doc.saveas(path)
        return

    def Transfer(self, shape):
        self.shapes.append(shape)
