import argparse
import os
from concurrent import futures
from glob import glob

import h5py
import vigra
import z5py
from tqdm import tqdm

#
# A conda environment with all necessary dependencies can be installed with the following
# command:
# conda create -c conda-forge -n env-name h5py z5py vigra tqdm
#


N_SLICES = 11416
H5_EXTS = ('.h5', '.hdf5', '.hdf')
N5_EXTS = ('.n5',)


def open_file(path, mode='r'):
    ext = os.path.splitext(path)[1]
    if ext in H5_EXTS:
        return h5py.File(path, mode)
    elif ext in N5_EXTS:
        return z5py.File(path, mode)
    else:
        raise ValueError("Invalid file extension %s" % ext)


def to_key(path):
    ext = os.path.splitext(path)[1]
    key = 'setup0/timepoint0/s0' if ext in N5_EXTS else 't00000/s00/0/cells'
    return key


def to_tiff(in_path, out_folder, in_key=None, n_threads=16):
    """ Convert input hdf5 or n5 file to tiff.
    """

    os.makedirs(out_folder, exist_ok=True)

    with open_file(in_path, 'r') as f:

        # if the in_key is None, decuce it from the extension
        if in_key is None:
            in_key = to_key(in_path)

        ds = f[in_key]
        n_slices = ds.shape[0]

        def save_slice(z):
            im = ds[z]
            out_path = os.path.join(out_folder, 'z%05i.tif' % z)
            vigra.impex.writeImage(im, out_path, compression="DEFLATE")

        with futures.ThreadPoolExecutor(n_threads) as tp:
            list(tqdm(tp.map(save_slice, range(n_slices))))


def to_container(in_folder, out_path, out_key=None, n_threads=16, chunk_size=(32, 128, 128)):
    """ Convert input folder with tifs to hdf5 or n5 container.
    """
    if not os.path.isdir(in_folder):
        raise ValueError("Input needs to be a folder with tiff files")
    n_files = glob(os.path.join(in_folder, 'z*.tif'))
    if len(n_files) != N_SLICES:
        raise ValueError("Only found %i slices, expect %i" % (n_files, N_SLICES))

    def load_im(z):
        pass

    im0 = load_im(0)
    shape = (N_SLICES,) + im0.shape

    if out_key is None:
        out_key = to_key(out_path)

    with open_file(out_path, 'a') as f:
        ds = f.create_dataset(out_key, shape=shape, compression='gzip', dtype='uint8',
                              chunks=chunk_size)

        def write_slice(im, z):
            if (im != 0).sum() == 0:
                return
            ds[z] = im

        write_slice(im0, 0)

        def save_slice(z):
            im = load_im(z)
            write_slice(im, z)

        with futures.ThreadPoolExecutor(n_threads) as tp:
            list(tqdm(tp.map(save_slice, range(N_SLICES))))


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('input', type=str)
    parser.add_argument('output', type=str)
    args = parser.parse_args()

    in_ext = os.path.split(args.input)
    if in_ext in N5_EXTS or in_ext in H5_EXTS:
        to_tiff(args.input, args.output)
    else:
        to_container(args.input, args.output)
