# temporary imports for vedo_write:
from __future__ import division, print_function
import vtk
from vtk.util.numpy_support import vtk_to_numpy
import os
import glob
import numpy as np

import vedo.utils as utils
import vedo.colors as colors
from vedo.assembly import Assembly
from vedo.mesh import Mesh
from vedo.pointcloud import Points
from vedo.ugrid import UGrid
from vedo.tetmesh import TetMesh
from vedo.picture import Picture
from vedo.volume import Volume
import vedo.docs as docs
import vedo.settings as settings

# normal imports

from pathlib import Path
from vedo import load
import requests



def connected_to_internet(url="http://www.google.com/", timeout=5):
    """
    Check that there is an internet connection

    :param url: url to use for testing (Default value = 'http://www.google.com/')
    :param timeout:  timeout to wait for [in seconds] (Default value = 5)
    """

    try:
        _ = requests.get(url, timeout=timeout)
        return True
    except requests.ConnectionError:  # pragma: no cover
        print("No internet connection available.")  # pragma: no cover
    return False


def fail_on_no_connection(func):
    """
    Decorator that throws an error if no internet connection is available
    """
    if not connected_to_internet():  # pragma: no cover
        raise ConnectionError(
            "No internet connection found."
        )  # pragma: no cover

    def inner(*args, **kwargs):
        return func(*args, **kwargs)

    return inner


def request(url):
    """
    Sends a request to a url

    :param url:

    """
    if not connected_to_internet():  # pragma: no cover
        raise ConnectionError(
            "No internet connection found."
        )  # pragma: no cover

    response = requests.get(url)
    if response.ok:
        return response
    else:  # pragma: no cover
        exception_string = "URL request failed: {}".format(
            response.reason
        )  # pragma: no cover
    raise ValueError(exception_string)


def check_file_exists(func):  # pragma: no cover
    """
    Decorator that throws an error if a function;s first argument
    is not a path to an existing file.
    """

    def inner(*args, **kwargs):
        if not Path(args[0]).exists():
            raise FileNotFoundError(
                f"File {args[0]} not found"
            )  # pragma: no cover
        return func(*args, **kwargs)

    return inner


@check_file_exists
def load_mesh_from_file(filepath, color=None, alpha=None):
    """
    Load a a mesh or volume from files like .obj, .stl, ...

    :param filepath: path to file
    :param **kwargs:

    """
    actor = load(str(filepath))
    actor.c(color).alpha(alpha)
    return actor


def vedo_write(objct, fileoutput, binary=True):
    """
    this is a fixed version of the write function from the io module in vedo.
    credit to Marco Musy.
    https://github.com/marcomusy/vedo/issues/298

    Write 3D object to file. (same as `save()`).

    Possile extensions are:
        - vtk, vti, npy, npz, ply, obj, stl, byu, vtp, vti, mhd, xyz, tif, png, bmp.
    """

    obj = objct
    if isinstance(obj, Points): # picks transformation
        obj = objct.polydata(True)
    elif isinstance(obj, (vtk.vtkActor, vtk.vtkVolume)):
        obj = objct.GetMapper().GetInput()
    elif isinstance(obj, (vtk.vtkPolyData, vtk.vtkImageData)):
        obj = objct

    if hasattr(obj, 'filename'):
        obj.filename = fileoutput

    fr = fileoutput.lower()
    if   fr.endswith(".vtk"):
        writer = vtk.vtkDataSetWriter()
    elif fr.endswith(".ply"):
        writer = vtk.vtkPLYWriter()
        writer.AddComment("PLY file generated by vedo")
        lut = objct.GetMapper().GetLookupTable()
        if lut:
            pscal = obj.GetPointData().GetScalars()
            if not pscal:
                pscal = obj.GetCellData().GetScalars()
            if pscal and pscal.GetName():
                writer.SetArrayName(pscal.GetName())
            writer.SetLookupTable(lut)
    elif fr.endswith(".stl"):
        writer = vtk.vtkSTLWriter()
    elif fr.endswith(".vtp"):
        writer = vtk.vtkXMLPolyDataWriter()
    elif fr.endswith(".vtu"):
        writer = vtk.vtkXMLUnstructuredGridWriter()
    elif fr.endswith(".vtm"):
        g = vtk.vtkMultiBlockDataGroupFilter()
        for ob in objct:
            if isinstance(ob, Points): # picks transformation
                ob = ob.polydata(True)
                g.AddInputData(ob)
            elif isinstance(ob, Volume):
                ob = ob.imagedata()
                g.AddInputData(ob)
        g.Update()
        mb = g.GetOutputDataObject(0)
        wri = vtk.vtkXMLMultiBlockDataWriter()
        wri.SetInputData(mb)
        wri.SetFileName(fileoutput)
        wri.Write()
        return mb
    elif fr.endswith(".xyz"):
        writer = vtk.vtkSimplePointsWriter()
    elif fr.endswith(".facet"):
        writer = vtk.vtkFacetWriter()
    elif fr.endswith(".tif"):
        writer = vtk.vtkTIFFWriter()
        writer.SetFileDimensionality(len(obj.GetDimensions()))
    elif fr.endswith(".vti"):
        writer = vtk.vtkXMLImageDataWriter()
    elif fr.endswith(".mhd"):
        writer = vtk.vtkMetaImageWriter()
    elif fr.endswith(".nii"):
        writer = vtk.vtkNIFTIImageWriter()
    elif fr.endswith(".png"):
        writer = vtk.vtkPNGWriter()
    elif fr.endswith(".jpg"):
        writer = vtk.vtkJPEGWriter()
    elif fr.endswith(".bmp"):
        writer = vtk.vtkBMPWriter()
    elif fr.endswith(".npy") or fr.endswith(".npz"):
        if utils.isSequence(objct):
            objslist = objct
        else:
            objslist = [objct]
        dicts2save = []
        for obj in objslist:
            dicts2save.append( toNumpy(obj) )
        np.save(fileoutput, dicts2save)
        return dicts2save

    elif fr.endswith(".obj"):
        outF = open(fileoutput, "w")
        outF.write('# OBJ file format with ext .obj\n')
        outF.write('# File generated by vedo\n')

        for p in objct.points():
            outF.write("v {:.5g} {:.5g} {:.5g}\n".format(*p))

        # pdata = objct.polydata().GetPointData().GetScalars()
        # if pdata:
        #     ndata = vtk_to_numpy(pdata)
        #     for vd in ndata:
        #         outF.write('vp '+ str(vd) +'\n')

        #ptxt = objct.polydata().GetPointData().GetTCoords() # not working
        #if ptxt:
        #    ntxt = vtk_to_numpy(ptxt)
        #    print(len(objct.faces()), objct.points().shape, ntxt.shape)
        #    for vt in ntxt:
        #        outF.write('vt '+ str(vt[0]) +" "+ str(vt[1])+ ' 0\n')

        for i,f in enumerate(objct.faces()):
            fs = ''
            for fi in f:
                fs += " {:d}".format(fi+1)
            outF.write('f' + fs + '\n')

        for l in objct.lines():
            ls = ''
            for li in l:
                ls += str(li+1)+" "
            outF.write('l '+ ls + '\n')

        outF.close()
        return objct

    elif fr.endswith(".xml"):  # write tetrahedral dolfin xml
        vertices = objct.points().astype(str)
        faces = np.array(objct.faces()).astype(str)
        ncoords = vertices.shape[0]
        outF = open(fileoutput, "w")
        outF.write('<?xml version="1.0" encoding="UTF-8"?>\n')
        outF.write('<dolfin xmlns:dolfin="http://www.fenicsproject.org">\n')

        if len(faces[0]) == 4:# write tetrahedral mesh
            ntets = faces.shape[0]
            outF.write('  <mesh celltype="tetrahedron" dim="3">\n')
            outF.write('    <vertices size="' + str(ncoords) + '">\n')
            for i in range(ncoords):
                x, y, z = vertices[i]
                outF.write('      <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'" z="'+z+'"/>\n')
            outF.write('    </vertices>\n')
            outF.write('    <cells size="' + str(ntets) + '">\n')
            for i in range(ntets):
                v0, v1, v2, v3 = faces[i]
                outF.write('     <tetrahedron index="'+str(i)
                           + '" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'" v3="'+v3+'"/>\n')

        elif len(faces[0]) == 3:# write triangle mesh
            ntri = faces.shape[0]
            outF.write('  <mesh celltype="triangle" dim="2">\n')
            outF.write('    <vertices size="' + str(ncoords) + '">\n')
            for i in range(ncoords):
                x, y, dummy_z = vertices[i]
                outF.write('      <vertex index="'+str(i)+'" x="'+x+'" y="'+y+'"/>\n')
            outF.write('    </vertices>\n')
            outF.write('    <cells size="' + str(ntri) + '">\n')
            for i in range(ntri):
                v0, v1, v2 = faces[i]
                outF.write('     <triangle index="'+str(i)+'" v0="'+v0+'" v1="'+v1+'" v2="'+v2+'"/>\n')

        outF.write('    </cells>\n')
        outF.write("  </mesh>\n")
        outF.write("</dolfin>\n")
        outF.close()
        return objct

    else:
        colors.printc("\noentry Unknown format", fileoutput, "file not saved.", c="r")
        return objct

    try:
        if hasattr(writer, 'SetFileTypeToBinary'):
            if binary:
                writer.SetFileTypeToBinary()
            else:
                writer.SetFileTypeToASCII()
        writer.SetInputData(obj)
        writer.SetFileName(fileoutput)
        writer.Write()
    except Exception as e:
        colors.printc("\noentry Error saving: " + fileoutput, "\n", e, c="r")
    return objct