////////////////////////////////////////////////////////////////////////
//		THIS FILE IS THE INTERFACE FILE BETWEEN MATLAB AND C          //
////////////////////////////////////////////////////////////////////////
// Authors:   Matt Ueckermann, Themis Sapsis, 
//            Pierre Lermusiaux, Pat Haley for MIT course 2.29

////////////////////////////////////
//  DECLARE STANDARD HEADER FILES //
////////////////////////////////////

#include "math.h"
#include "mex.h"
#include <stdlib.h>
#include <string.h>


////////////////////////////
//  MASTER FUNCTION FILE  //
////////////////////////////


//////////////////////////////////////////
// MATLAB INTERFACE FUNCTION DEFINITION //
//////////////////////////////////////////

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    // Declare input variables
    double dx, dy, *ui, *vi, *uj, *vj;
    int Nx, Ny, *Nodeu, *Nodev, Nbcs, NbcsDu, NbcsDv;
    
    
    // Declare output variables
    double *Fu, *Fv;
    
    //Declare working variables
    int i, j;
    double fw, fe, fn, fs;
    
    // Some minor input/output error checking
    if (!(nrhs==13)) {
        mexErrMsgTxt("Need 13 input arguments");
    }
    if (!(nlhs==2)) {
        mexErrMsgTxt("Need 2 outputs arguments");
    }
    
    
    // Get access to input data from MATLAB
    // Pointers are used for large array
    // Scalar are re-created in memory
    
    /********!!!! WARNING !!!************/
    // WARNING !!!!  note that C is expecting to be pointing to type int32 from
    // Matlab.  Therefore, for this code to work, the C argument must be
    // int32.  This is done in matlab by int32(C);
    // The function is then called like [Bnew Cnew] = axB(5,B,int32(C));
    // Use double(Cnew) if you want to convert it back (this might slow things).
    /********!!!! WARNING !!!************/
    //[Fu Fv] = UVadvect_DO_CDS(Nx, Ny, dx, dy, ui, vi, uj, vj, Nodeu, Nodev, idsu, idsv)
    Nx      =   (int)     mxGetScalar( prhs[0]  );
    Ny      =   (int)     mxGetScalar( prhs[1]  );
    dx      =   (double)  mxGetScalar( prhs[2]  );
    dy      =   (double)  mxGetScalar( prhs[3]  );
    ui      =   (double*) mxGetPr(     prhs[4]  );
    vi      =   (double*) mxGetPr(     prhs[5]  );
    uj      =   (double*) mxGetPr(     prhs[6]  );
    vj      =   (double*) mxGetPr(     prhs[7]  );
    Nodeu   =   (int*)    mxGetPr(     prhs[8]  );
    Nodev   =   (int*)    mxGetPr(     prhs[9]  );
    Nbcs    =   (int)     mxGetScalar( prhs[10] );
    NbcsDu  =   (int)     mxGetScalar( prhs[11] );
    NbcsDv  =   (int)     mxGetScalar( prhs[12] );
//     idsu    =   (int*)    mxGetPr(     prhs[10] );
//     Nidsu   =             mxGetM (     prhs[10] );
//     idsv    =   (int*)    mxGetPr(     prhs[11] );
//     Nidsv   =             mxGetM (     prhs[11] );
    // Example of outputting text to MATLAB
//     if (time ==0)
//         mexPrintf("Welcome message for time %f\n",time);
    
    
    // ALLOCATE AND POINT TO OUTPUTS
    plhs[0] =   mxCreateDoubleMatrix((Nx-1)* Ny   , 1, mxREAL);
    plhs[1] =   mxCreateDoubleMatrix( Nx   *(Ny-1), 1, mxREAL);
    Fu      =   (double*)mxGetPr(plhs[0]);
    Fv      =   (double*)mxGetPr(plhs[1]);
    
    for(i=1;i<Nx;i++) {
        for(j=1;j<Ny+1;j++) {
            if (Nodeu[j+(i)*(Ny+2)]<=Nbcs) // don't do work
            {
                Fu[j-1+(i-1)*Ny] = 0.0;
            }
            else{ //do work
                // West Face
                if(Nodeu[j  +(i-1)*(Ny+2)]>Nbcs) {
                    fw = (0.25/dx)*( ui[Nodeu[j   +(i-1)*(Ny+2)]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                            *( uj[Nodeu[j   +(i-1)*(Ny+2)]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                }
                else //treat boundary condition
                {
                    if (Nodeu[j  +(i-1)*(Ny+2)]>NbcsDu) { //Neumann
                        fw = (1.0/dx)*( 0.5*ui[Nodeu[j   +(i-1)*(Ny+2)]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                                *( 0.5*uj[Nodeu[j   +(i-1)*(Ny+2)]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                    }
                    else { //same as before - Dirichlet
                        fw = (0.25/dx)*( ui[Nodeu[j   +(i-1)*(Ny+2)]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                                *( uj[Nodeu[j   +(i-1)*(Ny+2)]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                    }
                }
                
                //East face
                if (Nodeu[j  +(i+1)*(Ny+2)]>Nbcs) {
                    fe = (0.25/dx)*( ui[Nodeu[j   +(i+1)*(Ny+2)]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                            *( uj[Nodeu[j   +(i+1)*(Ny+2)]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                }
                else //treat boundary condition
                {
                    if (Nodeu[j  +(i+1)*(Ny+2)]>NbcsDu) { //Neumann
                        //mexPrintf("Neumann %d,%d, %f\n",Nodeu[j+(i+1)*(Ny+2)]-1,NbcsDu,uj[Nodeu[j   +(i+1)*(Ny+2)]-1] );
                        fe = (1.0/dx)*( 0.5*ui[Nodeu[j   +(i+1)*(Ny+2)]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                                *( 0.5*uj[Nodeu[j   +(i+1)*(Ny+2)]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                    }
                    else {//same as before - Dirichlet
                        //mexPrintf("Dirichlet %d,%d, %f\n",Nodeu[j+(i+1)*(Ny+2)]-1,NbcsDu,uj[Nodeu[j   +(i+1)*(Ny+2)]-1] );
                        fe = (0.25/dx)*( ui[Nodeu[j   +(i+1)*(Ny+2)]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                                *( uj[Nodeu[j   +(i+1)*(Ny+2)]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                    }
                }
                
                //South Face
                if (Nodeu[j+1+ i*(Ny+2)]>Nbcs) {
                    fs = (0.25/dy)*( ui[Nodeu[j+1 + i*(Ny+2)   ]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                            *( vj[Nodev[j   +(i+1)*(Ny+1)]-1] + vj[Nodev[j  +i*(Ny+1)]-1] );
                }
                else { //treat boundary conditions    
                    fs = 0.0;
                    if (Nodeu[j+1+(i)*(Ny+2)]>NbcsDu) { //Neumann on u
                        fs = 0.5*ui[Nodeu[j+1 + i*(Ny+2)   ]-1] + ui[Nodeu[j  +i*(Ny+2)]-1];    
                    }
                    else { //same as before on u - Dirichlet
                        fs = 0.5*(ui[Nodeu[j+1 + i*(Ny+2)   ]-1] + ui[Nodeu[j  +i*(Ny+2)]-1]);  
                    }
                    if (Nodev[j   +(i+1)*(Ny+1)]>NbcsDv && Nodev[j   +(i+1)*(Ny+1)]<=Nbcs) { //Neumann on v
                        fs = (0.5/dy)*fs*( vj[Nodev[j   +(i+1)*(Ny+1)]-1] + vj[Nodev[j  +i*(Ny+1)]-1]\
                                + vj[Nodev[j-1 +(i+1)*(Ny+1)]-1] + vj[Nodev[j-1 +i*(Ny+1)]-1]);
                    }
                    else { //same as before on v - Dirichlet
                        fs = (0.5/dy)*fs*( vj[Nodev[j   +(i+1)*(Ny+1)]-1] + vj[Nodev[j  +i*(Ny+1)]-1]);                      
                    }
                }
                
                //North Face
                if (Nodeu[j-1+ i*(Ny+2)]>Nbcs) {
                    fn = (0.25/dy)*( ui[Nodeu[j-1 + i*(Ny+2)   ]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] )\
                            *( vj[Nodev[j-1 +(i+1)*(Ny+1)]-1] + vj[Nodev[j-1+i*(Ny+1)]-1] );
                }
                else { //treat boundary conditions
                    fn = 0.0;
                    if (Nodeu[j-1+(i)*(Ny+2)]>NbcsDu) { //Neumann on u
                        fn = ( 0.5*ui[Nodeu[j-1 + i*(Ny+2)   ]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] );
                    }
                    else { //Dirchlet on u -- same as before
                        fn = 0.5*( ui[Nodeu[j-1 + i*(Ny+2)   ]-1] + ui[Nodeu[j  +i*(Ny+2)]-1] );
                    }
                    if (Nodev[j-1 +(i+1)*(Ny+1)]>NbcsDv && Nodev[j-1  +(i+1)*(Ny+1)]<=Nbcs) { //Neumann on v
                        fn = (0.5/dy)*fn*( vj[Nodev[j   +(i+1)*(Ny+1)]-1] + vj[Nodev[j  +i*(Ny+1)]-1]\
                                + vj[Nodev[j-1 +(i+1)*(Ny+1)]-1] + vj[Nodev[j-1+i*(Ny+1)]-1]);
                    }
                    else { //Dirichlet on v -- same as before
                        fn = (0.5/dy)*fn*( vj[Nodev[j-1 +(i+1)*(Ny+1)]-1] + vj[Nodev[j-1+i*(Ny+1)]-1]);
                    }
                }
                //U advection, central difference scheme
                Fu[j-1+(i-1)*Ny] = fw-fe+fs-fn;
            }
        }
    }
    for(i=1;i<Nx+1;i++) {
        for(j=1;j<Ny;j++) {
            if (Nodev[j  +(i)*(Ny+1)]<=Nbcs) // don't do work
            {
                Fv[j-1+(i-1)*(Ny-1)] = 0.0;
            }
            else{ //do work
                //V advection, central difference sceme
                if(Nodev[j  +(i-1)*(Ny+1)]>Nbcs) {
                    //West face
                    fw = (0.25/dx)*( vi[Nodev[j   +(i-1)*(Ny+1)]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                            *( uj[Nodeu[j+1 +(i-1)*(Ny+2)]-1] + uj[Nodeu[j +(i-1)*(Ny+2)]-1] );
                    
                }
                else //treat boundary condition
                {
                    fw=0.0;
                    //West face
                    if (Nodev[j  +(i-1)*(Ny+1)]-1<=NbcsDv) { //Dirchlet on v --same as before
                        fw = 0.5*( vi[Nodev[j   +(i-1)*(Ny+1)]-1] + vi[Nodev[j  +i*(Ny+1)]-1] );
                    }
                    else { //Neuman on v
                        fw = (0.5*vi[Nodev[j   +(i-1)*(Ny+1)]-1] + vi[Nodev[j  +i*(Ny+1)]-1] );
                    }
                    if (Nodeu[j+1 +(i-1)*(Ny+2)]<=NbcsDu || Nodeu[j+1 +(i-1)*(Ny+2)]>Nbcs) { //Dirchlet on u -- same as before
                        fw = (0.5/dx)*fw*( uj[Nodeu[j+1 +(i-1)*(Ny+2)]-1] + uj[Nodeu[j +(i-1)*(Ny+2)]-1] );
                    }
                    else { //Neuman on u
                        fw = (0.5/dx)*fw\
                                *( uj[Nodeu[j+1 +(i-1)*(Ny+2)]-1] + uj[Nodeu[j +(i-1)*(Ny+2)]-1]+ \
                                uj[Nodeu[j+1 +(i  )*(Ny+2)]-1] + uj[Nodeu[j +(i  )*(Ny+2)]-1]);
                    }
                }
                
                
                if (Nodev[j  +(i+1)*(Ny+1)]>Nbcs) {
                    //East face
                    fe = (0.25/dx)*( vi[Nodev[j   +(i+1)*(Ny+1)]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                            *( uj[Nodeu[j+1 + i*(Ny+2)   ]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                }
                else //treat boundary condition
                {
                    fe=0.0;
                    //East face
                    if (Nodev[j  +(i+1)*(Ny+1)]-1<=NbcsDv) { //Dirichlet on v --same as before
                        fe = 0.5*( vi[Nodev[j   +(i+1)*(Ny+1)]-1] + vi[Nodev[j  +i*(Ny+1)]-1]);
                        
                    }
                    else { //Neumann on v
                        fe = ( 0.5*vi[Nodev[j   +(i+1)*(Ny+1)]-1] + vi[Nodev[j  +i*(Ny+1)]-1]);
                    }
                    if (Nodeu[j+1 +(i)*(Ny+2)]<=NbcsDu || Nodeu[j+1 +(i)*(Ny+2)]>Nbcs) { //Dirchlet on u -- same as before
                        fe = (0.5/dx)*fe*( uj[Nodeu[j+1 + i*(Ny+2)   ]-1] + uj[Nodeu[j  +i*(Ny+2)]-1] );
                    }
                    else { //Neuman on u
                        fe = (0.5/dx)*fe\
                                *( uj[Nodeu[j+1+    i*(Ny+2)   ]-1] + uj[Nodeu[j+    i*(Ny+2)]-1] +\
                                uj[Nodeu[j+1+(i-1)*(Ny+2)   ]-1] + uj[Nodeu[j+(i-1)*(Ny+2)]-1]);
                    }
                }
                
                
                if (Nodev[j+1+ i*(Ny+1)]>Nbcs) {
                    //South Face
                    fs = (0.25/dy)*( vi[Nodev[j+1 + i*(Ny+1)   ]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                            *( vj[Nodev[j+1 + i*(Ny+1)   ]-1] + vj[Nodev[j  +i*(Ny+1)]-1] );
                }
                else { //Treat boundary condition
                    if (Nodev[j+1+(i)*(Ny+1)]-1<=NbcsDv) { //Dirchlet on v -- same as before
                        fs = (0.25/dy)*( vi[Nodev[j+1 + i*(Ny+1)   ]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                                *( vj[Nodev[j+1 + i*(Ny+1)   ]-1] + vj[Nodev[j  +i*(Ny+1)]-1] );
                    }
                    else { //Neuman on v
                        fs = (1.0/dy)*( 0.5*vi[Nodev[j+1 + i*(Ny+1)   ]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                                *( 0.5*vj[Nodev[j+1 + i*(Ny+1)   ]-1] + vj[Nodev[j  +i*(Ny+1)]-1] );
                    }
                }
                
                
                //North Face
                if (Nodev[j-1+ i*(Ny+1)]>Nbcs) {
                    fn = (0.25/dy)*( vi[Nodev[j-1 + i*(Ny+1)   ]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                            *( vj[Nodev[j-1 + i*(Ny+1)   ]-1] + vj[Nodev[j  +i*(Ny+1)]-1] );
                }
                else { //treat boundary condition
                    if (Nodev[j-1+(i)*(Ny+1)]-1<=NbcsDv) { //Dirichlet --same as before
                        fn = (0.25/dy)*( vi[Nodev[j-1 + i*(Ny+1)   ]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                                *( vj[Nodev[j-1 + i*(Ny+1)   ]-1] + vj[Nodev[j  +i*(Ny+1)]-1] );
                    }
                    else { //Neuman
                        fn = (1.0/dy)*( 0.5*vi[Nodev[j-1 + i*(Ny+1)   ]-1] + vi[Nodev[j  +i*(Ny+1)]-1] )\
                                *( 0.5*vj[Nodev[j-1 + i*(Ny+1)   ]-1] + vj[Nodev[j  +i*(Ny+1)]-1] );
                    }
                }
                Fv[j-1+(i-1)*(Ny-1)] = fw-fe+fs-fn;
            }
        }
    }
}