/*
** (c) 1996-2000 The Regents of the University of California (through
** E.O. Lawrence Berkeley National Laboratory), subject to approval by
** the U.S. Department of Energy.  Your use of this software is under
** license -- the license agreement is attached and included in the
** directory as license.txt or you may contact Berkeley Lab's Technology
** Transfer Department at TTD@lbl.gov.  NOTICE OF U.S. GOVERNMENT RIGHTS.
** The Software was developed under funding from the U.S. Government
** which consequently retains certain rights as follows: the
** U.S. Government has been granted for itself and others acting on its
** behalf a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, and perform publicly
** and display publicly.  Beginning five (5) years after the date
** permission to assert copyright is obtained from the U.S. Department of
** Energy, and subject to any subsequent five (5) year renewals, the
** U.S. Government is granted for itself and others acting on its behalf
** a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, distribute copies to
** the public, perform publicly and display publicly, and to permit
** others to do so.
*/


//
// $Id: AmrDeriveAvgPlots.cpp,v 1.13 2001/10/17 17:53:33 lijewski Exp $
//

#include <new>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>

#include <unistd.h>

#include "REAL.H"
#include "Box.H"
#include "FArrayBox.H"
#include "ParmParse.H"
#include "ParallelDescriptor.H"
#include "DataServices.H"
#include "Utility.H"
#include "VisMF.H"
#if (BL_SPACEDIM == 3)
#include "Derived.H"
#endif

#define VSHOWVAL(verbose, val) { if(verbose) { \
		 cout << #val << " = " << val << '\n'; } }

void PrintUsage(char *progName);


// ----------------------------------------------------------------------
int main(int argc, char *argv[]) {
    ParallelDescriptor::StartParallel(&argc, &argv);

    if(argc == 1) {
      PrintUsage(argv[0]);
    }

    int iPlot, iLevel, iComp;
    ParmParse pp(argc-1,argv+1);
    pp.dumpTable(cout);

    if(pp.contains("help")) {
      PrintUsage(argv[0]);
    }


    //
    // Make sure to catch new failures.
    //
    set_new_handler(Utility::OutOfMemory);

    FArrayBox::setFormat(FABio::FAB_IEEE_32);
    cout << endl;
    cout << "******* Using FABio::FAB_IEEE_32" << endl;
    cout << endl;

    //
    // Scan the arguments.
    //
    //int i;
    std::string iFileDir;
    std::string iFile;
    std::string oFile;
    std::string oFileDir;
    bool verbose(false);
    if(pp.contains("verbose") || (pp.contains("v"))) {
      verbose = true;
      AmrData::SetVerbose(true);
    }

    int nPlotFiles(pp.countval("infile"));
    if(nPlotFiles <= 0) {
      cerr << "Bad nPlotFiles:  " << nPlotFiles << endl;
      cerr << "Exiting." << endl;
      DataServices::Dispatch(DataServices::ExitRequest, NULL);
    }
    VSHOWVAL(verbose, nPlotFiles);
    Array<std::string> plotFileNames(nPlotFiles);
    for(iPlot = 0; iPlot < nPlotFiles; ++iPlot) {
      pp.get("infile", plotFileNames[iPlot], iPlot);
      VSHOWVAL(verbose, plotFileNames[iPlot]);
    }

    pp.get("outfile", oFile);
    VSHOWVAL(verbose, oFile);


    DataServices::SetBatchMode();
    FileType fileType(NEWPLT);
    
    Array<DataServices *> dataServicesPtrArray(nPlotFiles);
    Array<AmrData *> amrDataPtrArray(nPlotFiles);
    for(iPlot = 0; iPlot < nPlotFiles; ++iPlot) {
      dataServicesPtrArray[iPlot] =
		     new DataServices(plotFileNames[iPlot], fileType);
      if( ! dataServicesPtrArray[iPlot]->AmrDataOk()) {
        DataServices::Dispatch(DataServices::ExitRequest, NULL);
        // ^^^ this calls ParallelDescriptor::EndParallel() and exit()
      }
      amrDataPtrArray[iPlot] = &(dataServicesPtrArray[iPlot]->AmrDataRef());
    }


    // check that all the plotfiles have the same structure
    int nComp(amrDataPtrArray[0]->NComp());
    int finestLevel(amrDataPtrArray[0]->FinestLevel());
    Array<std::string> plotVarNames(amrDataPtrArray[0]->PlotVarNames());
    Array<BoxArray> boxArray(finestLevel + 1);
    for(iLevel = 0; iLevel <= finestLevel; ++iLevel) {
      boxArray[iLevel] = amrDataPtrArray[0]->boxArray(iLevel);
    }

    bool bPlotStructureMatches(true);
    for(iPlot = 0; iPlot < nPlotFiles; ++iPlot) {
      if(amrDataPtrArray[iPlot]->FinestLevel() != finestLevel) {
        bPlotStructureMatches = false;
      }
      if(amrDataPtrArray[iPlot]->NComp() != nComp) {
        bPlotStructureMatches = false;
      }
      for(iComp = 0; iComp < nComp; ++iComp) {
        if(amrDataPtrArray[iPlot]->PlotVarNames()[iComp] != plotVarNames[iComp]) {
          bPlotStructureMatches = false;
        }
      }
      for(iLevel = 0; iLevel <= finestLevel; ++iLevel) {
        if(amrDataPtrArray[iPlot]->boxArray(iLevel) != boxArray[iLevel]) {
          bPlotStructureMatches = false;
        }
      }
    }
    if( ! bPlotStructureMatches) {
      cerr << "Plot file structures do not match." << endl;
      cerr << "Exiting." << endl;
      DataServices::Dispatch(DataServices::ExitRequest, NULL);
    }



    // Make the result MultiFab's.
    Array<MultiFab *> mfout(finestLevel+1);
    Array<MultiFab *> mfin(nPlotFiles);

    // loop through the levels
    for(iLevel = 0; iLevel <= finestLevel; ++iLevel) {
      const BoxArray &ba = boxArray[iLevel];
      int nGhost(0);
      mfout[iLevel] = new MultiFab(ba, nComp, nGhost);
      MultiFab &mfo = *mfout[iLevel];
      mfo.setVal(0.0);

      // loop through the components
      for(int nc = 0; nc < nComp; ++nc) {

        for(iPlot = 0; iPlot < nPlotFiles; ++iPlot) {  // cache the grids
	  mfin[iPlot] = &(amrDataPtrArray[iPlot]->GetGrids(iLevel, nc));
        }

        // loop through the fabs (within the multifab) at this level
        for(MultiFabIterator mfomfi(mfo); mfomfi.isValid(); ++mfomfi) {
	  int iFab(mfomfi.index());

          // Do the averaging
          for(iPlot = 0; iPlot < nPlotFiles; ++iPlot) {
	    //mfo[iFab] += (*mfin[iPlot])[iFab];
	    mfo[iFab].plus((*mfin[iPlot])[iFab], 0, nc);  // 0 is the srcComp
	  }
	  mfo[iFab].divide((Real) nPlotFiles, nc);

        }  // end for(mfomfi...)
      }  // end for(nc...)
    }  // end for(iLevel...)



  // write the output plotfile

    if (ParallelDescriptor::IOProcessor())
        if (!Utility::UtilCreateDirectory(oFile, 0755))
            Utility::CreateDirectoryFailed(oFile);
    //
    // Force other processors to wait till directory is built.
    //
    ParallelDescriptor::Barrier();

    std::string oFileHeader(oFile);
    oFileHeader += "/Header";
  
    VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);

    ofstream os;
  
    os.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.length());

    int i;
    if(ParallelDescriptor::IOProcessor()) {
      if(verbose) {
	if(ParallelDescriptor::IOProcessor()) {
          cout << "Opening file = " << oFileHeader << '\n';
	}
      }

      os.open(oFileHeader.c_str(), ios::out|ios::binary);

      if(os.fail())
        Utility::FileOpenFailed(oFileHeader);
      //
      // Start writing plotfile.
      //
      os << amrDataPtrArray[0]->PlotFileVersion() << '\n';
      int n_var(nComp);
      os << n_var << '\n';
      for(int n = 0; n < nComp; n++) os << plotVarNames[n] << '\n';
      os << BL_SPACEDIM << '\n';
      os << amrDataPtrArray[0]->Time() << '\n';  // use first plotfile time
      os << finestLevel << '\n';
      for(i = 0; i < BL_SPACEDIM; i++) os << amrDataPtrArray[0]->ProbLo()[i] << ' ';
      os << '\n';
      for(i = 0; i < BL_SPACEDIM; i++) os << amrDataPtrArray[0]->ProbHi()[i] << ' ';
      os << '\n';
      for(i = 0; i < finestLevel; i++) os << amrDataPtrArray[0]->RefRatio()[i] << ' ';
      os << '\n';
      for(i = 0; i <= finestLevel; i++) os << amrDataPtrArray[0]->ProbDomain()[i] << ' ';
      os << '\n';
      for(i = 0; i <= finestLevel; i++) os << 0 << ' ';
      os << '\n';
      for(i = 0; i <= finestLevel; i++) {
        for(int k = 0; k < BL_SPACEDIM; k++) os << amrDataPtrArray[0]->DxLevel()[i][k] << ' ';
        os << '\n';
      }
      os << amrDataPtrArray[0]->CoordSys() << '\n';
      os << "0\n"; // The bndry data width.
    }  // end if(ioproc)

    //
    // Write out level by level.
    //
    for(iLevel = 0; iLevel <= finestLevel; ++iLevel) {   // Write state data.
      int nGrids(amrDataPtrArray[0]->boxArray(iLevel).length());
      char buf[64];
      sprintf(buf, "Level_%d", iLevel);
    
      if(ParallelDescriptor::IOProcessor()) {
        os << iLevel << ' ' << nGrids << ' ' << amrDataPtrArray[0]->Time() << '\n';
        os << 0 << '\n';
    
        for(i = 0; i < nGrids; ++i) {
          for(int n = 0; n < BL_SPACEDIM; n++) {
            os << amrDataPtrArray[0]->GridLocLo()[iLevel][i][n]
               << ' '
               << amrDataPtrArray[0]->GridLocHi()[iLevel][i][n]
               << '\n';
	  }
        }
        //
        // Build the directory to hold the MultiFabs at this level.
        //
        std::string Level(oFile);
        Level += '/';
        Level += buf;
    
        if (!Utility::UtilCreateDirectory(Level, 0755))
            Utility::CreateDirectoryFailed(Level);
      }  // end if(ioproc)
      //
      // Force other processors to wait till directory is built.
      //
      ParallelDescriptor::Barrier();


      // Now build the full relative pathname of the MultiFab.
      static const std::string MultiFabBaseName("/MultiFab");
    
      std::string PathName(oFile);
      PathName += '/';
      PathName += buf;
      PathName += MultiFabBaseName;
    
      if(ParallelDescriptor::IOProcessor()) {
        // The full name relative to the Header file.
        std::string RelativePathName(buf);
        RelativePathName += '/';
        RelativePathName += MultiFabBaseName;
        os << RelativePathName << '\n';
      }
      VisMF::Write(*mfout[iLevel], PathName, VisMF::OneFilePerCPU);
      delete mfout[iLevel];
    }  // end for(iLevel...)

    os.close();

    DataServices::Dispatch(DataServices::ExitRequest, NULL);
    // ^^^ this calls ParallelDescriptor::EndParallel() and exit()
}


// ----------------------------------------------------------------------
void PrintUsage(char *progName) {
  cout << '\n';
  cout << "Usage:" << '\n';
  cout << progName << '\n';
  cout << "    outfile = outputFileName" << '\n';
  cout << "    infile  = inputFileName(s)" << '\n';
  cout << "   [-help]" << '\n';
  cout << "   [-verbose]" << '\n';
  cout << '\n';

  cout << '\n';
  exit(-1);
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
