__all__ = [
    'ExtractTopography',
]

__displayname__ = 'Subsetting'

from datetime import datetime

import numpy as np
import vtk
from scipy.spatial import cKDTree
from vtk.numpy_interface import dataset_adapter as dsa

from .. import _helpers, interface
from ..base import FilterBase

# NOTE: internal import - from scipy.spatial import cKDTree



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

class ExtractTopography(FilterBase):
    """This filter takes two inputs: a gridded data set and a set of points for
    a Topography source. This will add a boolean data array to the cell data of
    the input grid on whether that cell should be active (under topographic
    layer).

    Note:
        This currenlty ignores time varying inputs. We can implement time 
        variance but need to think about how we would like to do that. Should
        the topography surface be static and the volumetric data have time
        variance?

    """
    __displayname__ = 'Extract Topography'
    __category__ = 'filter'
    def __init__(self):
        FilterBase.__init__(self,
            nInputPorts=2, inputType='vtkDataSet',
            nOutputPorts=1)
        self.__tolerance = 0.001

    # CRITICAL for multiple input ports
    def FillInputPortInformation(self, port, info):
        """This simply makes sure the user selects the correct inputs
        """
        typ = 'vtkDataSet'
        if port == 1:
            typ = 'vtkPolyData' # Make sure topography are poly data
        info.Set(self.INPUT_REQUIRED_DATA_TYPE(), typ)
        return 1

    # THIS IS CRUCIAL to preserve data type through filter
    def RequestDataObject(self, request, inInfo, outInfo):
        """Constructs the output data object based on the input data object
        """
        self.OutputType = self.GetInputData(inInfo, 0, 0).GetClassName()
        self.FillOutputPortInformation(0, outInfo.GetInformationObject(0))
        return 1


    @staticmethod
    def __GetVoxelCenter(voxel):
        """Returns tuple for center of Voxel

        Note: The Z-coordinate is at the top of the cell
        """
        bounds = voxel.GetBounds()
        x = (bounds[0]+bounds[1])/2.0
        y = (bounds[2]+bounds[3])/2.0
        z = bounds[5]#(bounds[4]+bounds[5])/2.0
        return (x, y, z)

    def RequestData(self, request, inInfo, outInfo):
        """Used by pipeline to generate output"""
        # Get input/output of Proxy
        igrid = self.GetInputData(inInfo, 0, 0) # Port 0: grid
        topo = self.GetInputData(inInfo, 1, 0) # Port 1: topography
        grid = self.GetOutputData(outInfo, 0)

        # Perfrom task
        grid.DeepCopy(igrid)
        ncells = grid.GetNumberOfCells()
        active = np.zeros((ncells), dtype=int)
        # Now iterate through the cells in the grid and test if they are beneath the topography
        wtopo = dsa.WrapDataObject(topo)
        topoPts = wtopo.Points

        filt = vtk.vtkCellCenters()
        filt.SetInputDataObject(igrid)
        filt.Update()
        datapts = dsa.WrapDataObject(filt.GetOutput(0)).Points

        tree = cKDTree(topoPts)
        i = tree.query(datapts)[1]
        comp = topoPts[i]
        active = np.array(datapts[:,2] < (comp[:,2] - self.__tolerance), dtype=int)

        # Now add cell data to output
        active = interface.convertArray(active, name='Active Topography')
        grid.GetCellData().AddArray(active)
        return 1

    def Apply(self, data, points):
        self.SetInputDataObject(0, data)
        self.SetInputDataObject(1, points)
        self.Update()
        return self.GetOutput()

    def SetTolerance(self, tol):
        if self.__tolerance != tol:
            self.__tolerance = tol
            self.Modified()

    def GetTolerance(self):
        return self.__tolerance
