# SPDX-License-Identifier: GPL-2.0-or-later

import mathutils

class BasePrimitiveDXFExporter(object):

    INSTANCES = False
    PROJECTION = False
    HIDDEN_LINES = False

    def __init__(self, settings):
        self._settings = settings

    def projected_co(self, verts, matrix):
        """ converts coordinates of points from OCS to WCS->ScreenCS
        needs matrix: a projection matrix
        needs verts: a list of vectors[x,y,z]
        returns a list of [x,y,z]
        """
        #print 'deb:projected_co()  verts=', verts #---------
        temp_verts = [matrix @ mathutils.Vector(v) for v in verts]
        #print 'deb:projected_co()  temp_verts=', temp_verts #---------

    #    if GUI_A['Z_force_on'].val: locZ = GUI_A['Z_elev'].val
    #    else:locZ = 0.0
        locZ = 0.0

        if self.PROJECTION:
            if self.PERSPECTIVE:
                clipStart = 10.0
                for v in temp_verts:
                    coef = - clipStart / v[2]
                    v[0] *= coef
                    v[1] *= coef
                    v[2] = locZ
            for v in temp_verts:
                v[2] = locZ
        temp_verts = [v[:3] for v in temp_verts]
        #print 'deb:projected_co()  out_verts=', temp_verts #---------
        return temp_verts

    def isLeftHand(self, matrix):
        #Is the matrix a left-hand-system, or not?
        ma = matrix.to_euler().to_matrix()
        crossXY = self.M_CrossVecs(ma[0], ma[1])
        check = self.M_DotVecs(ma[2], crossXY)
        if check < 0.00001: return 1
        return 0

    #-----------------------------------------------------
    def hidden_status(self, faces, mx, mx_n):
        # sort out back-faces = with normals pointed away from camera
        #print 'HIDDEN_LINES: caution! not full implemented yet'
        front_faces = []
        front_edges = []
        for f in faces:
            #print 'deb: face=', f #---------
            #print 'deb: dir(face)=', dir(f) #---------
            # get its normal-vector in localCS
            vec_normal = f.no.copy()
            #print 'deb: vec_normal=', vec_normal #------------------
            # must be transferred to camera/view-CS
            vec_normal @= mx_n
            #vec_normal *= mb.rotationPart()
            #print 'deb:2vec_normal=', vec_normal #------------------
            #vec_normal *= mw0.rotationPart()
            #print 'deb:3vec_normal=', vec_normal, '\n' #------------------


            frontFace = False
            if not self.PERSPECTIVE: #for ortho mode ----------
                # normal must point the Z direction-hemisphere
                if vec_normal[2] > 0.00001:
                    frontFace = True
            else:
                v = f.verts[0]
                vert = mathutils.Vector(v.co) @ mx
                if mathutils.DotVecs(vert, vec_normal) < 0.00001:
                    frontFace = True

            if frontFace:
                front_faces.append(f.index)
                for key in f.edge_keys:
                    #this test can be done faster with set()
                    if key not in front_edges:
                        front_edges.append(key)

        #print 'deb: amount of visible faces=', len(front_faces) #---------
        #print 'deb: visible faces=', front_faces #---------
        #print 'deb: amount of visible edges=', len(front_edges) #---------
        #print 'deb: visible edges=', front_edges #---------
        return front_faces, front_edges

    #-----------------------------------------------------
    def toGlobalOrigin(self, points):
        """relocates points to the new location
        needs a list of points [x,y,z]
        """
    #    if GUI_A['g_origin_on'].val:
    #        for p in points:
    #            p[0] += G_ORIGIN[0]
    #            p[1] += G_ORIGIN[1]
    #            p[2] += G_ORIGIN[2]
        return points

    #---- migration to 2.49-------------------------------------------------

    #Draw.PupMenu('DXF exporter: Abort%t|This script version works for Blender up 2.49 only!')
    def M_CrossVecs(self, v1,v2):
        if 'cross' in dir(mathutils.Vector()):
            return v1.cross(v2) #for up2.49
        else:
            return mathutils.CrossVecs(v1,v2) #for pre2.49

    def M_DotVecs(self, v1,v2):
        if 'cross' in dir(mathutils.Vector()):
            return v1.dot(v2) #for up2.49
        else:
            return mathutils.DotVecs(v1,v2) #for pre2.49

#-----------------------------------------------------
    def getExtrusion(self, matrix):
        """ calculates DXF-Extrusion = Arbitrary Xaxis and Zaxis vectors """
        AZaxis = matrix[2].copy().resize3D().normalize() # = ArbitraryZvector
        Extrusion = [AZaxis[0],AZaxis[1],AZaxis[2]]
        if AZaxis[2]==1.0:
            Extrusion = None
            AXaxis = matrix[0].copy().resize3D() # = ArbitraryXvector
        else:
            threshold = 1.0 / 64.0
            if abs(AZaxis[0]) < threshold and abs(AZaxis[1]) < threshold:
                # AXaxis is the intersection WorldPlane and ExtrusionPlane
                AXaxis = self.M_CrossVecs(WORLDY,AZaxis)
            else:
                AXaxis = self.M_CrossVecs(WORLDZ,AZaxis)
        #print 'deb:\n' #-------------
        #print 'deb:getExtrusion()  Extrusion=', Extrusion #---------
        return Extrusion, AXaxis.normalize()

    #-----------------------------------------------------
#    def getZRotation(AXaxis, rot_matrix_invert):
#        """calculates ZRotation = angle between ArbitraryXvector and obj.matrix.Xaxis
#
#        """
#        # this works: Xaxis is the obj.matrix-Xaxis vector
#        # but not correct for all orientations
#        #Xaxis = matrix[0].copy().resize3D() # = ArbitraryXvector
#        ##Xaxis.normalize() # = ArbitraryXvector
#        #ZRotation = - mathutils.AngleBetweenVecs(Xaxis,AXaxis) #output in radians
#
#        # this works for all orientations, maybe a bit faster
#        # transform AXaxis into OCS:Object-Coord-System
#        #rot_matrix = normalizeMat(matrix.rotationPart())
#        #rot_matrix_invert = rot_matrix.invert()
#        vec = AXaxis * rot_matrix_invert
#        ##vec = AXaxis * matrix.copy().invert()
#        ##vec.normalize() # not needed for atan2()
#        #print '\ndeb:getExtrusion()  vec=', vec #---------
#        ZRotation = - atan2(vec[1],vec[0]) #output in radians
#
#        #print 'deb:ZRotation()  ZRotation=', ZRotation*r2d #---------
#        return ZRotation
#
#
#    #-----------------------------------------------------
#    def getTargetOrientation(mx,Extrusion,AXaxis,WCS_loc,sizeX,sizeY,sizeZ,rotX,rotY,rotZ):
#        """given
#        """
#        if 1:
#            rot_matrix = normalizeMat(mx.to_euler().to_matrix())
#            #TODO: workaround for blender negative-matrix.invert()
#            # partially done: works only for rotX,rotY==0.0
#            if sizeX<0.0: rot_matrix[0] *= -1
#            if sizeY<0.0: rot_matrix[1] *= -1
#            #if sizeZ<0.0: rot_matrix[2] *= -1
#            rot_matrix_invert = rot_matrix.invert()
#        else: #TODO: to check, why below rot_matrix_invert is not equal above one
#            rot_euler_matrix = euler2matrix(rotX,rotY,rotZ)
#            rot_matrix_invert = euler2matrix(-rotX,-rotY,-rotZ)
#
#        # OCS_origin is Global_Origin in ObjectCoordSystem
#        OCS_origin = mathutils.Vector(WCS_loc) * rot_matrix_invert
#        #print 'deb: OCS_origin=', OCS_origin #---------
#
#        ZRotation = rotZ
#        if Extrusion is not None:
#            ZRotation = getZRotation(AXaxis,rot_matrix_invert)
#        #Zrotmatrix = mathutils.RotationMatrix(-ZRotation, 3, "Z")
#        rs, rc = sin(ZRotation), cos(ZRotation)
#        Zrotmatrix = mathutils.Matrix([rc, rs,0.0],[-rs,rc,0.0],[0.0,0.0,1.0])
#        #print 'deb: Zrotmatrix=\n', Zrotmatrix #--------------
#
#        # ECS_origin is Global_Origin in EntityCoordSystem
#        ECS_origin = OCS_origin * Zrotmatrix
#        #print 'deb: ECS_origin=', ECS_origin #---------
#        #TODO: it doesn't work yet for negative scaled curve-objects!
#        return ZRotation,Zrotmatrix,OCS_origin,ECS_origin
