from contextlib import contextmanager
from timeit import default_timer
from pathlib import Path
import cProfile
import functools
import pstats


def profile(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        profiler = cProfile.Profile()
        profiler.enable()
        try:
            retval = func(*args, **kwargs)
        finally:
            profiler.disable()
            with open("profile.out", "w") as profile_file:
                stats = pstats.Stats(profiler, stream=profile_file)
                stats.print_stats()
        return retval

    return inner


spl = lambda y: [int(w) for w in y]


def minmax(l):
    return min(l), max(l)


def load_rows(day):
    return [row for row in load(day)]


def load(day):
    path = Path(get_fname(day))
    return path.read_text().rstrip().split("\n")


def get_fname(day: int) -> str:
    import sys

    if sys.argv[-1] == "--sample":
        return f"../samples/day{day:02}.txt"
    else:
        return f"../full/day{day:02}.txt"


#############
def load_char_matrix(f):
    my_file = []
    for line in f:
        my_file.append(line.rstrip())
    return [list(x) for x in my_file]


def load_file_char_matrix(name):
    with open(name, "r") as f:
        return load_char_matrix(f)


def load_int_matrix(f):
    my_file = []
    for line in f:
        my_file.append(line.rstrip())
    return [list(map(int, x)) for x in my_file]


def load_file_int_matrix(name):
    with open(name, "r") as f:
        return load_int_matrix(f)


def load_word_matrix(f):
    my_file = []
    for line in f:
        my_file.append(line.rstrip())
    return [x.split(" ") for x in my_file]


def load_file_word_matrix(name):
    with open(name, "r") as f:
        return load_word_matrix(f)


#############


def rotate(WHAT, times=1):
    what = WHAT
    for x in range(times):
        what = list(zip(*what[::-1]))
    return what


@contextmanager
def elapsed_timer():
    start = default_timer()
    elapser = lambda: default_timer() - start
    yield lambda: elapser()
    end = default_timer()
    elapser = lambda: end - start




def render_cubes(maxX,maxY,maxZ, my_cubes):
    from mpl_toolkits.mplot3d import Axes3D
    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d.art3d import Poly3DCollection
    
    def cuboid_data(o, size=(1,1,1)):
        X = [[[0, 1, 0], [0, 0, 0], [1, 0, 0], [1, 1, 0]],
             [[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]],
             [[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]],
             [[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]],
             [[0, 1, 0], [0, 1, 1], [1, 1, 1], [1, 1, 0]],
             [[0, 1, 1], [0, 0, 1], [1, 0, 1], [1, 1, 1]]]
        X = np.array(X).astype(float)
        for i in range(3):
            X[:,:,i] *= size[i]
        X += np.array(o)
        return X
    
    def plotCubeAt(positions,sizes=None,colors=None, **kwargs):
        if not isinstance(colors,(list,np.ndarray)): colors=["C0"]*len(positions)
        if not isinstance(sizes,(list,np.ndarray)): sizes=[(1,1,1)]*len(positions)
        g = []
        for p,s,c in zip(positions,sizes,colors):
            g.append( cuboid_data(p, size=s) )
        return Poly3DCollection(np.concatenate(g),  
                                facecolors=np.repeat(colors,6, axis=0), **kwargs)
    
    N1 = maxX
    N2 = maxY
    N3 = maxZ
    ma = np.random.choice([0,1], size=(N1,N2,N3), p=[0.99, 0.01])
    x,y,z = np.indices((N1,N2,N3))-.5
    #positions = np.c_[x[ma==1],y[ma==1],z[ma==1]]
    positions = np.c_[my_cubes]
    colors= np.random.rand(len(positions),3)
    
    fig = plt.figure()
    ax = fig.add_subplot(projection='3d')
    ax.set_aspect('equal')
    
    pc = plotCubeAt(positions, colors=colors,edgecolor="k")
    ax.add_collection3d(pc)
    
    ax.set_xlim([0,maxX])
    ax.set_ylim([0,maxY])
    ax.set_zlim([0,maxZ])
    #plotMatrix(ax, ma)
    #ax.voxels(ma, edgecolor="k")
    
    plt.show()


