//# CalSet.cc: Implementation of Calibration parameter cache
//# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//#
//# This library is distributed in the hope that it will be useful, but WITHOUT
//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: aips2-request@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA
//#

#include <synthesis/CalTables/CTGlobals.h>
#include <synthesis/CalTables/NewCalTable.h>
#include <synthesis/CalTables/CTIter.h>
#include <synthesis/CalTables/RIorAParray.h>

#include <casacore/casa/Arrays.h>
#include <casacore/casa/BasicSL/String.h>
#include <casacore/casa/Utilities/GenSort.h>
#include <casacore/casa/Exceptions/Error.h>
#include <casacore/tables/Tables/Table.h>
#include <casacore/tables/Tables/TableIter.h>
#include <casacore/tables/Tables/TableVector.h>

#include <sstream>

#include <casacore/casa/Logging/LogMessage.h>
#include <casacore/casa/Logging/LogSink.h>

using namespace casacore;
namespace casa { //# NAMESPACE CASA - BEGIN


void smoothCT(NewCalTable ct,
	      const String& smtype,
	      const Double& smtime,
	      Vector<Int> selfields) {

  // Complex parameters?
  Bool cmplx=ct.isComplex();

  // half-width
  Double thw(smtime/2.0);

  // Workspace
  Vector<Double> times;
  Vector<Float> p,newp;
  Vector<Bool> pOK, newpOK;

  Cube<Float> fpar;
  Cube<Bool> fparok,newfparok;

  Vector<Bool> mask;

  IPosition blc(3,0,0,0), fblc(3,0,0,0);
  IPosition trc(3,0,0,0), ftrc(3,0,0,0);
  IPosition vec(1,0);

  Block<String> cols(4);
  cols[0]="SPECTRAL_WINDOW_ID";
  cols[1]="FIELD_ID";
  cols[2]="ANTENNA1";
  cols[3]="ANTENNA2";
  CTIter ctiter(ct,cols);

  while (!ctiter.pastEnd()) {

    Int nSlot=ctiter.nrow();
    Int ifld=ctiter.thisField();

    // Only if more than one slot in this spw _AND_
    //  field is among those requested (if any)
    if (nSlot>1 && 
	(selfields.nelements()<1 || anyEQ(selfields,ifld))) {

      //UNUSED: Int ispw=ctiter.thisSpw();
      
      vec(0)=nSlot;
      trc(2)=ftrc(2)=nSlot-1;

      times.assign(ctiter.time());

      // Extract Float info
      if (cmplx)
        fpar.assign(ctiter.casfparam("AP"));
      else
        fpar.assign(ctiter.fparam());

      fparok.assign(!ctiter.flag());
      newfparok.assign(fparok);
      IPosition fsh(fpar.shape());

      // For each channel
      for (int ichan=0;ichan<fsh(1);++ichan) {
	blc(1)=trc(1)=fblc(1)=ftrc(1)=ichan;
	// For each param (pol)
	for (Int ipar=0;ipar<fsh(0);++ipar) {
	  blc(0)=trc(0)=ipar;
	  fblc(0)=ftrc(0)=ipar/(cmplx?2:1);
	  
	  // Reference slices of par/parOK
	  p.reference(fpar(blc,trc).reform(vec));
	  newp.assign(p);
	  pOK.reference(fparok(fblc,ftrc).reform(vec));
	  newpOK.reference(newfparok(fblc,ftrc).reform(vec));

       /*
	    cout << ispw << " "
		 << ichan << " "
		 << ipar << " "
		 << "p.shape() = " << p.shape() << " "
		 << "pOK.shape() = " << pOK.shape() << " "
		 << endl;
       */

	    
	  Vector<Bool> mask;
	  for (Int i=0;i<nSlot;++i) {
	    // Make mask
	    mask = pOK;
	    mask = (mask && ( (times >  (times(i)-thw)) && 
			      (times <= (times(i)+thw)) ) );

	    // Avoid explicit zeros, for now
	    //	    mask = (mask && amp>=FLT_MIN);


	    //cout << "    " << ifld << " " << i << " " << idx(i) << " ";
	    //for (Int j=0;j<mask.nelements();++j)
	    //  cout << mask(j);
	    //cout << endl;
	    
	    if (ntrue(mask)>0) {
	      if (smtype=="mean") {
		newp(i)=mean(p(mask));
	      }
	      else if (smtype=="median") {
		newp(i)= median(p(mask),false);
	      }
	      newpOK(i)=true;
	    }
	    else 
	      newpOK(i)=false;
	    
	  } // i
	  // keep new ok info
	  p=newp;
	} // ipar
      } // ichan

      // Put info back
      if (cmplx)
        ctiter.setcparam(RIorAPArray(fpar).c());
      else
        ctiter.setfparam(fpar);

      ctiter.setflag(!newfparok);

    } // nSlot>1

    ctiter.next();
  } // ispw
	
}

void assignCTScanField(NewCalTable& ct, String msName, 
		       Bool doField, Bool doScan, Bool doObs) {

  // TBD: verify msName is present and is an MS

  // Arrange to iterate only on SCAN  (and SPW?)
  Table mstab(msName,Table::Old);

  // How many scans in total?
  TableVector<Int> allscansTV(mstab,"SCAN_NUMBER");
  Vector<Int> allscans=allscansTV.makeVector();
  Int nScan=genSort(allscans,Sort::Ascending,(Sort::QuickSort | Sort::NoDuplicates));

  //  cout << "Found " << nScan << " scans in " << msName << "." << endl;

  // Workspace
  Vector<Int> scanlist(nScan,-1);
  Vector<Double> timelo(nScan,DBL_MIN);
  Vector<Double> timehi(nScan,DBL_MAX);
  Vector<Int> fieldlist(nScan,-1);
  Vector<Int> obslist(nScan,-1);
  Vector<uInt> ord;
  {
    Block<String> cols(1);
    cols[0]="SCAN_NUMBER";
    TableIterator mstiter(mstab,cols);
    
    // Get time boundares and fields for each scan
    Int iscan(0);
    while (!mstiter.pastEnd()) {
      Table thistab(mstiter.table());
      
      Int scan=TableVector<Int>(thistab,"SCAN_NUMBER")(0);
      scanlist(iscan)=scan;
      
      fieldlist(iscan)=TableVector<Int>(thistab,"FIELD_ID")(0);
      obslist(iscan)=TableVector<Int>(thistab,"OBSERVATION_ID")(0);
      
      Vector<Double> times=TableVector<Double>(thistab,"TIME").makeVector();
      timelo(iscan)=min(times)-1e-5;
      timehi(iscan)=max(times)+1e-5;
      
      mstiter.next();
      ++iscan;
    }


    // Ensure time orderliness
    genSort(ord,timehi);

    /*
    cout << "scanlist=" << scanlist << endl;
    cout << "fieldlist=" << fieldlist << endl;
    cout << "timelo=" << timelo-timelo(0) << endl;
    cout << "timehi=" << timehi-timelo(0) << endl;
    cout << "ord.nelements() = " << ord.nelements() << endl;
    cout << "ord = " << ord << endl;
    */

  }

  //UNUSED: Double rTime=timelo(ord(0));

  // Now iterate throught the NCT and set field and scan according to time
  Block<String> cols(1);
  cols[0]="TIME";
  CTIter ctiter(ct,cols);

  Int itime(0);
  Int thisObs(0);
  Int thisScan(0);
  Int thisField(0);
  while (!ctiter.pastEnd()) {
    Double thisTime=ctiter.thisTime();

    //    cout.precision(12);
    //    cout << "thisTime = " << thisTime-rTime << endl;

    // If time before first MS time, just use first
    if (thisTime<timehi(ord(0))) {
      itime=0;
      //      cout << " Pre: ";
    }
    // If time after last MS time, use last
    else if (thisTime>timelo(ord(nScan-1))) {
      itime=nScan-1;
      //      cout << " Post: ";
    }
    else if (thisTime>timehi(ord(itime))) {

      // Isolate correct time index
      while (thisTime>timehi(ord(itime))&& itime<nScan) {
	//	cout << itime << " " << thisTime-rTime << ">" << timehi(ord(itime))-rTime << endl;
	++itime;
      }
      //      cout << " Found: ";
    }
    //else 
      //      cout << " Still: ";

    thisObs=obslist(ord(itime));
    thisScan=scanlist(ord(itime));
    thisField=fieldlist(ord(itime));

    /*
    cout << " itime=" << itime << " "
	 << timelo(ord(itime))-rTime << " < "
	 << thisTime-rTime << " < "
	 << timehi(ord(itime))-rTime
	 << " s=" << thisScan << " f=" << thisField << endl;
    */

    // Set the field and scan
    if (doField) 
      ctiter.setfield(thisField);
    if (doScan) 
      ctiter.setscan(thisScan);
    if (doObs) 
      ctiter.setobs(thisObs);
    
    ctiter.next();
  }

}

} //# NAMESPACE CASA - END