# This file is part of the Printrun suite.
#
# Printrun 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 3 of the License, or
# (at your option) any later version.
#
# Printrun 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 Printrun.  If not, see <http://www.gnu.org/licenses/>.

import sys
import struct
import math

def cross(v1, v2):
    return [v1[1] * v2[2] - v1[2] * v2[1], v1[2] * v2[0] - v1[0] * v2[2], v1[0] * v2[1] - v1[1] * v2[0]]

def genfacet(v):
    veca = [v[1][0] - v[0][0], v[1][1] - v[0][1], v[1][2] - v[0][2]]
    vecb = [v[2][0] - v[1][0], v[2][1] - v[1][1], v[2][2] - v[1][2]]
    vecx = cross(veca, vecb)
    vlen = math.sqrt(sum(map(lambda x: x * x, vecx)))
    if vlen == 0:
        vlen = 1
    normal = map(lambda x: x / vlen, vecx)
    return [normal, v]

I = [
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1]
]

def transpose(matrix):
    return zip(*matrix)
    #return [[v[i] for v in matrix] for i in xrange(len(matrix[0]))]

def multmatrix(vector, matrix):
    return map(sum, transpose(map(lambda x: [x[0] * p for p in x[1]], zip(vector, transpose(matrix)))))

def applymatrix(facet, matrix = I):
    #return facet
    #return [map(lambda x:-1.0*x, multmatrix(facet[0]+[1], matrix)[:3]), map(lambda x:multmatrix(x+[1], matrix)[:3], facet[1])]
    return genfacet(map(lambda x: multmatrix(x + [1], matrix)[:3], facet[1]))

f = [[0, 0, 0], [[-3.022642, 0.642482, -9.510565], [-3.022642, 0.642482, -9.510565], [-3.022642, 0.642482, -9.510565]]]
m = [
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 1],
    [0, 0, 0, 1]
]

def emitstl(filename, facets = [], objname = "stltool_export", binary = 1):
    if filename is None:
        return
    if binary:
        f = open(filename, "wb")
        buf = "".join(["\0"] * 80)
        buf += struct.pack("<I", len(facets))
        facetformat = struct.Struct("<ffffffffffffH")
        for i in facets:
            l = list(i[0][:])
            for j in i[1]:
                l += j[:]
            l += [0]
            #print len(l)
            buf += facetformat.pack(*l)
        f.write(buf)
        f.close()
        return

    f = open(filename, "w")
    f.write("solid " + objname + "\n")
    for i in facets:
        f.write("  facet normal " + " ".join(map(str, i[0])) + "\n   outer loop\n")
        for j in i[1]:
            f.write("    vertex " + " ".join(map(str, j)) + "\n")
        f.write("   endloop" + "\n")
        f.write("  endfacet" + "\n")
    f.write("endsolid " + objname + "\n")
    f.close()


class stl(object):
    def __init__(self, filename = None):
        self.facet = [[0, 0, 0], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
        self.facets = []
        self.facetsminz = []
        self.facetsmaxz = []

        self.name = ""
        self.insolid = 0
        self.infacet = 0
        self.inloop = 0
        self.facetloc = 0
        if filename is None:
            return
        with open(filename) as f:
            data = f.read()
        if "facet normal" in data[1:300] and "outer loop" in data[1:300]:
            lines = data.split("\n")
            for line in lines:
                if not self.parseline(line):
                    return
        else:
            print "Not an ascii stl solid - attempting to parse as binary"
            f = open(filename, "rb")
            buf = f.read(84)
            while len(buf) < 84:
                newdata = f.read(84 - len(buf))
                if not len(newdata):
                    break
                buf += newdata
            facetcount = struct.unpack_from("<I", buf, 80)
            facetformat = struct.Struct("<ffffffffffffH")
            for i in xrange(facetcount[0]):
                buf = f.read(50)
                while len(buf) < 50:
                    newdata = f.read(50 - len(buf))
                    if not len(newdata):
                        break
                    buf += newdata
                fd = list(facetformat.unpack(buf))
                self.name = "binary soloid"
                facet = [fd[:3], [fd[3:6], fd[6:9], fd[9:12]]]
                self.facets.append(facet)
                self.facetsminz.append((min(map(lambda x: x[2], facet[1])), facet))
                self.facetsmaxz.append((max(map(lambda x: x[2], facet[1])), facet))
            f.close()
            return

    def translate(self, v = [0, 0, 0]):
        matrix = [[1, 0, 0, v[0]],
                  [0, 1, 0, v[1]],
                  [0, 0, 1, v[2]],
                  [0, 0, 0, 1]
                  ]
        return self.transform(matrix)

    def rotate(self, v = [0, 0, 0]):
        import math
        z = v[2]
        matrix1 = [[math.cos(math.radians(z)), -math.sin(math.radians(z)), 0, 0],
                   [math.sin(math.radians(z)), math.cos(math.radians(z)), 0, 0],
                   [0, 0, 1, 0],
                   [0, 0, 0, 1]
                   ]
        y = v[0]
        matrix2 = [[1, 0, 0, 0],
                   [0, math.cos(math.radians(y)), -math.sin(math.radians(y)), 0],
                   [0, math.sin(math.radians(y)), math.cos(math.radians(y)), 0],
                   [0, 0, 0, 1]
                   ]
        x = v[1]
        matrix3 = [[math.cos(math.radians(x)), 0, -math.sin(math.radians(x)), 0],
                   [0, 1, 0, 0],
                   [math.sin(math.radians(x)), 0, math.cos(math.radians(x)), 0],
                   [0, 0, 0, 1]
                   ]
        return self.transform(matrix1).transform(matrix2).transform(matrix3)

    def scale(self, v = [0, 0, 0]):
        matrix = [[v[0], 0, 0, 0],
                  [0, v[1], 0, 0],
                  [0, 0, v[2], 0],
                  [0, 0, 0, 1]
                  ]
        return self.transform(matrix)

    def transform(self, m = I):
        s = stl()
        s.facets = [applymatrix(i, m) for i in self.facets]
        s.insolid = 0
        s.infacet = 0
        s.inloop = 0
        s.facetloc = 0
        s.name = self.name
        for facet in s.facets:
            s.facetsminz += [(min(map(lambda x:x[2], facet[1])), facet)]
            s.facetsmaxz += [(max(map(lambda x:x[2], facet[1])), facet)]
        return s

    def export(self, f = sys.stdout):
        f.write("solid " + self.name + "\n")
        for i in self.facets:
            f.write("  facet normal " + " ".join(map(str, i[0])) + "\n")
            f.write("   outer loop" + "\n")
            for j in i[1]:
                f.write("    vertex " + " ".join(map(str, j)) + "\n")
            f.write("   endloop" + "\n")
            f.write("  endfacet" + "\n")
        f.write("endsolid " + self.name + "\n")
        f.flush()

    def parseline(self, l):
        l = l.strip()
        if l.startswith("solid"):
            self.insolid = 1
            self.name = l[6:]
            #print self.name

        elif l.startswith("endsolid"):
            self.insolid = 0
            return 0
        elif l.startswith("facet normal"):
            l = l.replace(", ", ".")
            self.infacet = 11
            self.facetloc = 0
            self.facet = [[0, 0, 0], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
            self.facet[0] = map(float, l.split()[2:])
        elif l.startswith("endfacet"):
            self.infacet = 0
            self.facets += [self.facet]
            facet = self.facet
            self.facetsminz += [(min(map(lambda x:x[2], facet[1])), facet)]
            self.facetsmaxz += [(max(map(lambda x:x[2], facet[1])), facet)]
        elif l.startswith("vertex"):
            l = l.replace(", ", ".")
            self.facet[1][self.facetloc] = map(float, l.split()[1:])
            self.facetloc += 1
        return 1

if __name__ == "__main__":
    s = stl("../../Downloads/frame-vertex-neo-foot-x4.stl")
    for i in xrange(11, 11):
        working = s.facets[:]
        for j in reversed(sorted(s.facetsminz)):
            if(j[0] > i):
                working.remove(j[1])
            else:
                break
        for j in (sorted(s.facetsmaxz)):
            if(j[0] < i):
                working.remove(j[1])
            else:
                break

        print i, len(working)
    emitstl("../../Downloads/frame-vertex-neo-foot-x4-a.stl", s.facets, "emitted_object")
#stl("../prusamendel/stl/mendelplate.stl")