from lar import *
from scipy import *
import json
import scipy
import numpy as np
import time as tm
import gc
import struct
import getopt, sys
import os
import traceback

import logging
logger = logging.getLogger(__name__)
# ------------------------------------------------------------
# Logging & Timer
# ------------------------------------------------------------

logging_level = 0;

# 0 = no_logging
# 1 = few details
# 2 = many details
# 3 = many many details

def log(n, l):
    if __name__=="__main__" and n <= logging_level:
        for s in l:
            print "Log:", s;

timer = 1;

timer_last =  tm.time()

def timer_start(s):
    global timer_last;
    if __name__=="__main__" and timer == 1:
        log(3, ["Timer start:" + s]);
    timer_last = tm.time();

def timer_stop():
    global timer_last;
    if __name__=="__main__" and timer == 1:
        log(3, ["Timer stop :" + str(tm.time() - timer_last)]);

# ------------------------------------------------------------

# ------------------------------------------------------------

def invertIndex(nx,ny,nz):
    nx,ny,nz = nx+1,ny+1,nz+1
    def invertIndex0(offset):
        a0, b0 = offset / nx, offset % nx
        a1, b1 = a0 / ny, a0 % ny
        a2, b2 = a1 / nz, a1 % nz
        return b0,b1,b2
    return invertIndex0


def writeToStlFiles(fileVertex, fileFaces, V, FV,
                    xStart, yStart, zStart,
                    vertex_count, old_vertex_count,
                    b2cells

                    ):
    old_vertex_count
    for vtx in V:
        fileVertex.write("v %s %s %s\n" % (
            str(vtx[0] + xStart),
            str(vtx[1] + yStart),
            str(vtx[2] + zStart),
        ))
        vertex_count = vertex_count + 1

    for face in FV:
        fileFaces.write("f %i %i %i %i\n" % (
            old_vertex_count + face[0],
            old_vertex_count + face[1],
            old_vertex_count + face[3],
            old_vertex_count + face[2]
        ))

    return vertex_count, old_vertex_count


def writeToStlFilesOld(fileVertex, fileFaces, V, FV,
                       xStart, yStart, zStart,
                       vertex_count, old_vertex_count,
                       b2cells
                       ):
    for f in b2cells:
        old_vertex_count = vertex_count

        for vtx in FV[f]:
            fileVertex.write("v ")
            fileVertex.write(str(V[vtx][0] + xStart))
            fileVertex.write(" ")
            fileVertex.write(str(V[vtx][1] + yStart))
            fileVertex.write(" ")
            fileVertex.write(str(V[vtx][2] + zStart))
            fileVertex.write("\n")
            vertex_count = vertex_count + 1

        # fileFaces.write("f ")
        # fileFaces.write(str(old_vertex_count + 0))
        # fileFaces.write(" ")
        # fileFaces.write(str(old_vertex_count + 1))
        # fileFaces.write(" ")
        # fileFaces.write(str(old_vertex_count + 3))
        # fileFaces.write("\n")

        # fileFaces.write("f ")
        # fileFaces.write(str(old_vertex_count + 0))
        # fileFaces.write(" ")
        # fileFaces.write(str(old_vertex_count + 3))
        # fileFaces.write(" ")
        # fileFaces.write(str(old_vertex_count + 2))
        # fileFaces.write("\n")

        fileFaces.write("f %i %i %i %i\n" % (
            old_vertex_count + 0,
            old_vertex_count + 1,
            old_vertex_count + 3,
            old_vertex_count + 2
        ))
    return vertex_count, old_vertex_count


def readFile(V,FV,chunksize,inputFile,OUT_DIR): # outputVtx="outputVtx.obj",outputFaces="outputFaces.obj"):
    if not os.path.isfile(inputFile):
        print "File '%s' not found" % (inputFile)
        exit(-1)
    outputId = os.path.basename(inputFile).split('.')[0].split('-')[1]
    outputVtx=OUT_DIR+"/output-a-"+outputId+".stl"
    outputFaces=OUT_DIR+"/output-b-"+outputId+".stl"

    with open(inputFile, "rb") as file:
        with open(outputVtx, "w") as fileVertex:
            with open(outputFaces, "w") as fileFaces:

                vertex_count = 1
                old_vertex_count = vertex_count
                count = 0

                try:
                    while True:
                        count += 1

                        zStart = struct.unpack('>I', file.read(4))[0]
                        xStart = struct.unpack('>I', file.read(4))[0]
                        yStart = struct.unpack('>I', file.read(4))[0]

                        log(1, ["zStart, xStart, yStart = " + str(zStart) + "," + str(xStart) + "," + str(yStart)]);
                        #    zStart, xStart, yStart = LISTA_OFFSET[i].astype(float64)

                        LISTA_VETTORI2 = np.zeros(chunksize,dtype=int32);

                        # log(1, ["chunksize = " + str(chunksize)]);
                        temp = file.read(chunksize);

                        # log(1, ["chunksize = OK"]);

                        i = 0
                        timer_start("LISTA_VETTORI2 " + str(i));
                        while (i < chunksize):
                            if (temp[i] == '\x01'):
                                LISTA_VETTORI2[i] = 1;
                            elif (temp[i] == '\xff'):
                                LISTA_VETTORI2[i] = -1;
                            i = i + 1;
                        # TODO signum is wrong
                        lista = LISTA_VETTORI2
                        LISTA_VETTORI2 = np.abs(LISTA_VETTORI2)
                        timer_stop();
                        log(1, ["LISTA_VETTORI2[i] = " + str(i)]);

                        timer_start("objectBoundaryChain ");
                        l = len(LISTA_VETTORI2)
                        objectBoundaryChain = scipy.sparse.csr_matrix(LISTA_VETTORI2.reshape((l,1)))
                        timer_stop();

                        timer_start("csrChainToCellList " + str(i));
                        b2cells = csrChainToCellList(objectBoundaryChain)
# mjirik code used for debug. It is more efficient but not connected to file
# write
                        # FVbo = []
                        # for i, orientation in enumerate(lista):
                        #     [v1, v2, v3, v4] = FV[i]
                        #     face = [v1, v2, v4, v3]
                        #     if orientation > 0:
                        #         FVbo.append(face)
                        #     if orientation < 0:
                        #         FVbo.append(face[::-1])
                        # from py.computation.step_triangularmesh import triangulate_quads
                        # # for debug visualization i need make proper order
                        # FVbo3 = triangulate_quads(FVbo)
                        # # VIEW(EXPLODE(1.2, 1.2, 1.2)(MKPOLS((V, FVbo3))))
                        timer_stop();

                        timer_start("MKPOLS " + str(i));
                        # orient FV
                        FVn = []
                        for i, face in enumerate(FV):
                            [v1, v2, v3, v4] = FV[i]
                            # face = [v1, v2, v4, v3]
                            if lista[i] < 0:
                                FVn.append([v1, v3, v2, v4])
                            else:
                                FVn.append([v1, v2, v3, v4])

                        # import ipdb; ipdb.set_trace() #  noqa BREAKPOINT

# @TODO old way is not efficient. It generates too much vertexes
                        vertex_count, old_vertex_count = writeToStlFilesOld(
                            fileVertex, fileFaces,
                            V, FVn,
                            xStart, yStart, zStart,
                            vertex_count, old_vertex_count,
                            b2cells
                        )




                        fileVertex.flush()
                        fileFaces.flush()

                    timer_stop();
                except struct.error:
                    logger.debug('not importatnt reading error')
                except:
                    logger.debug('reading error')
                    traceback.print_exc()
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
                    log(1, [ "EOF or error: " + ''.join('!! ' + line for line in lines) ])

def make_obj(nx, ny, nz, FILE_IN, OUT_DIR):

    def ind(x,y,z): return x + (nx+1) * (y + (ny+1) * (z))

    chunksize = nx * ny + nx * nz + ny * nz + 3 * nx * ny * nz
    V = [[x,y,z] for z in xrange(nz+1) for y in xrange(ny+1) for x in xrange(nx+1) ]

    v2coords = invertIndex(nx,ny,nz)

    FV = []
    for h in xrange(len(V)):
        x,y,z = v2coords(h)
        if (x < nx) and (y < ny): FV.append([h,ind(x+1,y,z),ind(x,y+1,z),ind(x+1,y+1,z)])
        if (x < nx) and (z < nz): FV.append([h,ind(x+1,y,z),ind(x,y,z+1),ind(x+1,y,z+1)])
        if (y < ny) and (z < nz): FV.append([h,ind(x,y+1,z),ind(x,y,z+1),ind(x,y+1,z+1)])

    logger.debug('before readFile()')
    try:
        readFile(V,FV,chunksize,FILE_IN,OUT_DIR)
    except:
        import traceback
        traceback.print_exc()
        exc_type, exc_value, exc_traceback = sys.exc_info()
        lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
        log(1, [ "Error: " + ''.join('!! ' + line for line in lines) ])
        sys.exit(2)
    logger.debug('after readFile()')

def main(argv):
    ARGS_STRING = 'Args: -x <borderX> -y <borderY> -z <borderZ> -i <inputfile> -o <outdir>'

    try:
        opts, args = getopt.getopt(argv,"i:o:x:y:z:")
    except getopt.GetoptError:
        print ARGS_STRING
        sys.exit(2)

    nx = ny = nz = 64
    mandatory = 3
    #Files
    FILE_IN = ''
    OUT_DIR = ''

    for opt, arg in opts:
        if opt == '-x':
            nx = ny = nz = int(arg)
            mandatory = mandatory - 1
        elif opt == '-y':
            ny = nz = int(arg)
        elif opt == '-z':
            nz = int(arg)
        elif opt == '-i':
            FILE_IN = arg
            mandatory = mandatory - 1
        elif opt == '-o':
            OUT_DIR = arg
            mandatory = mandatory - 1

    if mandatory != 0:
        print 'Not all arguments where given'
        print ARGS_STRING
        sys.exit(2)

    make_obj(nx, ny, nz, FILE_IN, OUT_DIR)

if __name__ == "__main__":
    main(sys.argv[1:])
