from SimPEG import *
import BaseGrav as GRAV
import re

class GravityIntegral(Problem.BaseProblem):
    
    #surveyPair = Survey.LinearSurvey
    
    storeG = True #: Store the forward matrix by default, otherwise just compute d
    actInd = None #: Active cell indices provided

    def __init__(self, mesh, mapping=None, **kwargs):
        Problem.BaseProblem.__init__(self, mesh, mapping=mapping, **kwargs)


    def fwr_op(self):
        # Add forward function
        # kappa = self.curModel.kappa TODO
        sus = self.mapping*self.curModel
        return self.G.dot(sus)


    def fields(self, m):
        self.curModel = m
        total = np.zeros(self.survey.nRx)
        induced = self.fwr_op()    
        # rem = self.rem

        if induced is not None:
            total += induced

        return total 


        # return self.G.dot(self.mapping*(m))
            
    def Jvec(self, m, v, f=None):
        dmudm = self.mapping.deriv(m)
        return self.G.dot(dmudm*v)

    def Jtvec(self, m, v, f=None):
        dmudm = self.mapping.deriv(m)
        return dmudm.T * (self.G.T.dot(v))

    @property
    def G(self):
        if not self.ispaired:
            raise Exception('Need to pair!')

        if getattr(self,'_G', None) is None:
           self._G = self.Intrgl_Fwr_Op( 'z' )

            
        return self._G


    def Intrgl_Fwr_Op(self, flag):

        """ 

        Gravity forward operator in integral form

        flag        = 'z' | 'xyz'
        
        Return
        _G        = Linear forward modeling operation

        Created on March, 15th 2016

        @author: dominiquef

         """    
        # Find non-zero cells
        #inds = np.nonzero(actv)[0]
        if getattr(self, 'actInd', None) is not None:
            if self.actInd.dtype=='bool':
                inds = np.asarray([inds for inds, elem in enumerate(self.actInd, 1) if elem], dtype = int) - 1
            else:
                inds = self.actInd



        else:

            inds = np.asarray(range(self.mesh.nC))
        
        nC = len(inds)

        # Create active cell projector
        P = sp.csr_matrix((np.ones(nC),(inds, range(nC))),
            shape=(self.mesh.nC, nC))
        
        

            
        # Create vectors of nodal location (lower and upper coners for each cell)
        xn = self.mesh.vectorNx;
        yn = self.mesh.vectorNy;
        zn = self.mesh.vectorNz;
        
        yn2,xn2,zn2 = np.meshgrid(yn[1:], xn[1:], zn[1:])
        yn1,xn1,zn1 = np.meshgrid(yn[0:-1], xn[0:-1], zn[0:-1])
        
        Yn = P.T*np.c_[mkvc(yn1), mkvc(yn2)]
        Xn = P.T*np.c_[mkvc(xn1), mkvc(xn2)]
        Zn = P.T*np.c_[mkvc(zn1), mkvc(zn2)]
           
        rxLoc = self.survey.srcField.rxList[0].locs   
        ndata = rxLoc.shape[0]    

        # Pre-allocate space and create magnetization matrix if required
        # Pre-allocate space
        if flag == 'z':
            G = np.zeros((ndata, nC))


        elif flag == 'xyz':

            G = np.zeros((int(3*ndata), nC))
            
            

        else:
            print """Flag must be either 'z' | 'xyz', please revised"""
            return


        # Loop through all observations and create forward operator (ndata-by-nC)
        print "Begin calculation of forward operator: " + flag

        # Add counter to dsiplay progress. Good for large problems
        count = -1;
        for ii in range(ndata):
        
            if flag=='z':
                tt = get_T_mat(Xn,Yn,Zn,rxLoc[ii,:]) 
                G[ii,:] = tt

            elif flag == 'xyz':
                print "Sorry 3-component not implemented yet"


        # Display progress
            count = progress(ii,count,ndata)

        print "Done 100% ...forward operator completed!!\n"
    
        return G


def get_T_mat(Xn,Yn,Zn,rxLoc):
    """
    Load in the active nodes of a tensor mesh and computes the gravity tensor
    for a given observation location rxLoc[obsx, obsy, obsz]

    INPUT:
    Xn, Yn, Zn: Node location matrix for the lower and upper most corners of
                all cells in the mesh shape[nC,2]
    M
    OUTPUT:
    Tx = [Txx Txy Txz]
    Ty = [Tyx Tyy Tyz]
    Tz = [Tzx Tzy Tzz]

    where each elements have dimension 1-by-nC.
    Only the upper half 5 elements have to be computed since symetric.
    Currently done as for-loops but will eventually be changed to vector
    indexing, once the topography has been figured out.

    Created on Oct, 20th 2015

    @author: dominiquef

     """ 
    NewtG=6.6738e-3
    eps = 1e-10 # add a small value to the locations to avoid /0
    
    nC = Xn.shape[0]

    # Pre-allocate space for 1D array
    T = np.zeros((1,nC))
    
    dz = rxLoc[2] - Zn + eps
         
    dy = Yn - rxLoc[1] + eps
 
    dx = Xn - rxLoc[0] + eps
    

    # Compute contribution from each corners
    for aa in range(2):
        for bb in range(2):
            for cc in range(2):

                r = (dx[:,aa] ** 2 + dy[:,bb] ** 2 + dz[:,cc] ** 2) ** (0.50)
                
                T = T - NewtG * (-1) ** aa * (-1) ** bb * (-1) ** cc * (dx[:,aa] * np.log ( dy[:,bb] + r ) + dy[:,bb] * np.log ( dx[:,aa] + r ) - dz[:,cc] * np.arctan ( dx[:,aa] * dy[:,bb] / ( dz[:,cc] * r )))

    return T

def progress(iter,prog,final):
    """
    progress(iter,prog,final)

    Function measuring the progress of a process and print to screen the %.
    Useful to estimate the remaining runtime of a large problem.

    Created on Dec, 20th 2015

    @author: dominiquef
    """
    arg = np.floor(float(iter)/float(final)*10.);

    if  arg > prog:

        strg = "Done " + str(arg*10) + " %"
        print strg
        prog = arg;

    return prog



def writeUBCobs(filename,survey,d):
    """
    writeUBCobs(filename,survey,d)

    Function writing an observation file in UBC-GRAV3D format.

    INPUT
    filename    : Name of out file including directory
    survey
    flag          : dobs | dpred

    OUTPUT
    Obsfile

    Created on Dec, 27th 2015

    @author: dominiquef
    """

    rxLoc = survey.srcField.rxList[0].locs

    wd = survey.std

    data = np.c_[rxLoc , d , wd]

    with file(filename,'w') as fid:
        fid.write('%i\n' %len(d) )
        np.savetxt(fid, data, fmt='%e',delimiter=' ',newline='\n')


    print "Observation file saved to: " + filename

def getActiveTopo(mesh,topo,flag):
    """
    getActiveTopo(mesh,topo)

    Function creates an active cell model from topography

    INPUT
    mesh        : Mesh in SimPEG format
    topo        : Scatter points defining topography [x,y,z]

    OUTPUT
    actv        : Active cell model

    Created on Dec, 27th 2015

    @founrdo
    """
    import scipy.interpolate as interpolation

    if (flag=='N'):
        Zn      = np.zeros((mesh.nNx,mesh.nNy))
        # wght    = np.zeros((mesh.nNx,mesh.nNy))
        cx      = mesh.vectorNx
        cy      = mesh.vectorNy

    F = interpolation.NearestNDInterpolator(topo[:,0:2],topo[:,2])
    [Y,X] = np.meshgrid(cy,cx)

    Zn = F(X,Y)

    actv = np.zeros((mesh.nCx, mesh.nCy, mesh.nCz))

    if (flag=='N'):
        Nz = mesh.vectorNz[1:]


    for jj in range(mesh.nCy):

        for ii in range(mesh.nCx):

            temp = [kk for kk in range(len(Nz)) if np.all(Zn[ii:(ii+2),jj:(jj+2)] > Nz[kk]) ]
            actv[ii,jj,temp] = 1


    actv = mkvc(actv==1)
    
    inds = np.asarray([inds for inds, elem in enumerate(actv, 1) if elem], dtype = int) - 1
        
    return inds

def plot_obs_2D(survey,varstr):   
    """ Function plot_obs(rxLoc,d,wd)
    Generate a 2d interpolated plot from scatter points of data
    
    INPUT
    rxLoc       : Observation locations [x,y,z]
    d           : Data vector
    wd          : Uncertainty vector

    OUTPUT
    figure()

    Created on Dec, 27th 2015

    @author: dominiquef
    
    """ 
        
    from scipy.interpolate import griddata
    import pylab as plt
    
    rxLoc   = survey.srcField.rxList[0].locs
    d       = survey.dobs
    wd      = survey.std

    # Create grid of points
    x = np.linspace(rxLoc[:,0].min(), rxLoc[:,0].max(), 100)
    y = np.linspace(rxLoc[:,1].min(), rxLoc[:,1].max(), 100)
    
    X, Y = np.meshgrid(x,y)
    
    # Interpolate
    d_grid = griddata(rxLoc[:,0:2],d,(X,Y), method ='linear')
    
    # Plot result
    plt.figure()
    plt.subplot()
    plt.imshow(d_grid, extent=[x.min(), x.max(), y.min(), y.max()],origin = 'lower')
    plt.colorbar(fraction=0.02)
    plt.contour(X,Y, d_grid,10)
    plt.scatter(rxLoc[:,0],rxLoc[:,1], c=d, s=20)
    plt.title(varstr)
    plt.gca().set_aspect('equal', adjustable='box')

def readUBCgravObs(obs_file):
    
    """
    Read UBC grav file format
    
    INPUT:
    :param fileName, path to the UBC obs grav file
    
    OUTPUT:
    :param survey
    
    """

    fid = open(obs_file,'r') 

    # First line has the number of rows
    line = fid.readline()
    ndat = np.array(line.split(),dtype=int)

    # Pre-allocate space for obsx, obsy, obsz, data, uncert
    line = fid.readline()
    temp = np.array(line.split(),dtype=float) 
        
    d  = np.zeros(ndat, dtype=float)
    wd = np.zeros(ndat, dtype=float)
    locXYZ = np.zeros( (ndat,3), dtype=float)
    
    for ii in range(ndat):
        
        temp = np.array(line.split(),dtype=float) 
        locXYZ[ii,:] = temp[:3]
        d[ii] = temp[3]
        wd[ii] = temp[4]
        line = fid.readline()
    
    rxLoc = GRAV.RxObs(locXYZ)
    srcField = GRAV.SrcField([rxLoc])
    survey = GRAV.LinearSurvey(srcField) 
    survey.dobs =  d
    survey.std =  wd
    return survey


def read_GRAVinv_inp(input_file):
    """Read input files for forward modeling MAG data with integral form
    INPUT:
    input_file: File name containing the forward parameter
    
    OUTPUT:
    mshfile
    obsfile
    topofile
    start model
    ref model
    weightfile
    chi_target
    as, ax ,ay, az
    upper, lower bounds
    lp, lqx, lqy, lqz

    # All files should be in the working directory, otherwise the path must
    # be specified.

    Created on Dec 21th, 2015
    
    @author: dominiquef
    """

    
    fid = open(input_file,'r')
    
    # Line 1
    line = fid.readline()
    l_input  = line.split('!')
    mshfile = l_input[0].rstrip()
    
    # Line 2
    line = fid.readline()
    l_input  = line.split('!')
    obsfile = l_input[0].rstrip()       
    
    # Line 3  
    line = fid.readline()
    l_input = re.split('[!\s]',line)
    if l_input=='null':
        topofile = []
        
    else:
        topofile = l_input[0].rstrip()


    # Line 4
    line = fid.readline()
    l_input = re.split('[!\s]',line) 
    if l_input[0]=='VALUE':
        mstart = float(l_input[1])
        
    else:
        mstart = l_input[0].rstrip()

    # Line 5
    line = fid.readline()
    l_input = re.split('[!\s]',line) 
    if l_input[0]=='VALUE':
        mref = float(l_input[1])
        
    else:
        mref = l_input[0].rstrip()
    

    # Line 7
    line = fid.readline()
    l_input = re.split('[!\s]',line) 
    if l_input=='DEFAULT':
        wgtfile = []
        
    else:
        wgtfile = l_input[0].rstrip()

    # Line 8
    line = fid.readline()
    l_input = re.split('[!\s]',line) 
    chi = float(l_input[0])

    # Line 9
    line = fid.readline()
    l_input = re.split('[!\s]',line) 
    val = np.array(l_input[0:4])
    alphas = val.astype(np.float)

    # Line 10
    line = fid.readline()
    l_input = re.split('[!\s]',line) 
    if l_input[0]=='VALUE':
        val   = np.array(l_input[1:3])
        bounds = val.astype(np.float)
        
    else:
        bounds = l_input[0].rstrip()

    # Line 11
    line = fid.readline()
    l_input = re.split('[!\s]',line) 
    if l_input[0]=='VALUE':
        val   = np.array(l_input[1:6])
        lpnorms = val.astype(np.float)
        
    else:
        lpnorms = l_input[0].rstrip()

    return mshfile, obsfile, topofile, mstart, mref, wgtfile, chi, alphas, bounds, lpnorms

