//# CalTableDesc2.cc: Implementation of CalTableDesc2.h
//# 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
//#
//# $Id$
//----------------------------------------------------------------------------

#include <synthesis/CalTables/CalTableDesc2.h>
#include <msvis/MSVis/MSCalEnums.h>
#include <casacore/tables/Tables/ScaColDesc.h>
#include <casacore/tables/Tables/ArrColDesc.h>
#include <casacore/tables/Tables/SetupNewTab.h>
#include <casacore/tables/Tables/TableRecord.h>
#include <casacore/measures/TableMeasures/TableMeasRefDesc.h>
#include <casacore/measures/TableMeasures/TableMeasValueDesc.h>
#include <casacore/measures/TableMeasures/TableMeasDesc.h>
#include <casacore/measures/TableMeasures/TableQuantumDesc.h>
#include <casacore/measures/Measures/MEpoch.h>
#include <casacore/measures/Measures/MFrequency.h>
#include <casacore/measures/Measures/MDirection.h>
#include <casacore/casa/Containers/Record.h>
#include <casacore/casa/Arrays/IPosition.h>

using namespace casacore;
namespace casa { //# NAMESPACE CASA - BEGIN
  
  //----------------------------------------------------------------------------
  /*
  CalTableDesc2::CalTableDesc2() : itsCalMainDesc(defaultCalMain("",VisCalParType::COMPLEX)), 
				 itsCalHistoryDesc(defaultCalHistory()),
				 itsCalDescDesc(defaultCalDesc())
  {
    // Default null constructor for calibration table description (v2.0)
    // Output to private data:
    //    itsCalMainDesc      TableDesc        Table descriptor (cal_main)
    //    itsCalHistoryDesc   TableDesc        Table descriptor (cal_history)
    //    itsCalDescDesc      TableDesc        Table descriptor (cal_desc)
    //
  };
  */
  /*
  CalTableDesc2::CalTableDesc2 (const String& jonesType, const Int& parType) : 
    itsCalMainDesc(defaultCalMain(jonesType, parType)), 
    itsCalHistoryDesc(defaultCalHistory()), itsCalDescDesc(defaultCalDesc())
  {
    // Constructor for calibration table description (v2.0)
    // Inputs:
    //    type                const String&    Cal table type (eg. "P Jones")
    // Output to private data:
    //    itsCalMainDesc      TableDesc        Table descriptor (cal_main)
    //    itsCalHistoryDesc   TableDesc        Table descriptor (cal_history)
    //    itsCalDescDesc      TableDesc        Table descriptor (cal_desc)
    //
    parType_ = parType;
  };
  */
  
  CalTableDesc2::CalTableDesc2() : itsCalMainDesc(""), 
				 itsCalHistoryDesc(""),
				 itsCalDescDesc("")
  {
    // Default null constructor for calibration table description (v2.0)
    // Output to private data:
    //    itsCalMainDesc      TableDesc        Table descriptor (cal_main)
    //    itsCalHistoryDesc   TableDesc        Table descriptor (cal_history)
    //    itsCalDescDesc      TableDesc        Table descriptor (cal_desc)
    //
  };
  //----------------------------------------------------------------------------
  
  CalTableDesc2::CalTableDesc2 (const String& jonesType, const Int& parType) : 
    itsCalMainDesc(defaultCalMain(jonesType, parType)), 
    itsCalHistoryDesc(defaultCalHistory()), 
    itsCalDescDesc(defaultCalDesc())
  {
    // Constructor for calibration table description (v2.0)
    // Inputs:
    //    type                const String&    Cal table type (eg. "P Jones")
    // Output to private data:
    //    itsCalMainDesc      TableDesc        Table descriptor (cal_main)
    //    itsCalHistoryDesc   TableDesc        Table descriptor (cal_history)
    //    itsCalDescDesc      TableDesc        Table descriptor (cal_desc)
    //
    //    init(jonesType, parType);
    parType_ = parType;
  };
  
  //----------------------------------------------------------------------------
  
  void CalTableDesc2::init(const String& jonesType, const Int& parType)
  {
    itsCalMainDesc.add(defaultCalMain(jonesType, parType));
    itsCalHistoryDesc.add(defaultCalHistory());
    itsCalDescDesc.add(defaultCalDesc());
  };

  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::defaultCalHistory()
  {
    // Generate the default table descriptor for the Cal History sub-table
    // Output:
    //    defaultCalHistory     TableDesc     Default Cal History descriptor
    //
    TableDesc td ("Cal History", "2.0", TableDesc::Scratch);
    td.comment() = "Calibration history sub-table";
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::CAL_PARMS),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::CAL_TABLES),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::CAL_SELECT),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::CAL_NOTES),
					     ColumnDesc::Direct));
    return td;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::defaultCalDesc()
  {
    // Generate the default table descriptor for the Cal Desc sub-table
    // Output:
    //    defaultCalDesc        TableDesc       Default Cal Desc descriptor
    //
    TableDesc td ("Cal Desc", "1.0", TableDesc::Scratch);
    td.comment() = "Calibration description sub-table";
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::NUM_SPW),
					  ColumnDesc::Direct));
    td.addColumn (ArrayColumnDesc <Int> (MSC::fieldName (MSC::NUM_CHAN)));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::NUM_RECEPTORS),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::N_JONES),
					  ColumnDesc::Direct));
    td.addColumn 
      (ArrayColumnDesc <Int> (MSC::fieldName (MSC::SPECTRAL_WINDOW_ID)));
    td.addColumn (ArrayColumnDesc <Double> (MSC::fieldName (MSC::CHAN_FREQ)));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::MEAS_FREQ_REF),
					  ColumnDesc::Direct));
    td.addColumn (ArrayColumnDesc <Double> (MSC::fieldName (MSC::CHAN_WIDTH)));
    td.addColumn (ArrayColumnDesc <Int> (MSC::fieldName (MSC::CHAN_RANGE)));
    td.addColumn (ArrayColumnDesc <String> 
		  (MSC::fieldName (MSC::POLARIZATION_TYPE)));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::JONES_TYPE),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::MS_NAME),
					     ColumnDesc::Direct));
    
    // Add TableMeasures information for Measures/Quanta columns
    //
    // CHAN_FREQ
    TableMeasValueDesc chanFreqMeasVal (td, MSC::fieldName(MSC::CHAN_FREQ));
    TableMeasRefDesc chanFreqMeasRef (td, MSC::fieldName (MSC::MEAS_FREQ_REF));
    TableMeasDesc<MFrequency> chanFreqMeasCol (chanFreqMeasVal, chanFreqMeasRef);
    chanFreqMeasCol.write (td);
    
    // CHAN_WIDTH
    TableQuantumDesc chanWidthQuantDesc (td, MSC::fieldName (MSC::CHAN_WIDTH),
					 Unit ("Hz"));
    chanWidthQuantDesc.write (td);
    
    return td;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::defaultCalMain (const String& jonesType, const Int& parType)
  {
    // Generate the default table descriptor for the Cal Main sub-table
    // Input:
    //    type             const String&      Cal table type (eg. "P Jones")
    // Output:
    //    defaultCalMain   TableDesc          Default Cal Main descriptor
    //
    // Set up table descriptor and add comment field
    TableDesc td (jonesType, "1.0", TableDesc::Scratch);
    td.comment() = jonesType + " calibration table";
    
    // Define keywords
    Record keyWordRec;
    // Cal_desc and cal_history indices
    keyWordRec.define (MSC::fieldName (MSC::CAL_DESC_ID), 0);
    keyWordRec.define (MSC::fieldName (MSC::CAL_HISTORY_ID), 0);
    // Add to table descriptor
    td.rwKeywordSet().assign (keyWordRec);
    
    // Cal Main columns (MS Main indices)
    td.addColumn (ScalarColumnDesc <Double> (MSC::fieldName (MSC::TIME),
					     ColumnDesc::Direct));
    td.addColumn 
      (ScalarColumnDesc <Double> (MSC::fieldName (MSC::TIME_EXTRA_PREC),
				  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Double> (MSC::fieldName (MSC::INTERVAL),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::ANTENNA1),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::FEED1),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::FIELD_ID),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::ARRAY_ID),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::OBSERVATION_ID),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::SCAN_NUMBER),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::PROCESSOR_ID),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::STATE_ID),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::PHASE_ID),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::PULSAR_BIN),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::PULSAR_GATE_ID),
					  ColumnDesc::Direct));
    
    // Secondary MS indices (from MS sub-tables)
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::FREQ_GROUP),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc<String> (MSC::fieldName(MSC::FREQ_GROUP_NAME),
					    ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::FIELD_NAME),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::FIELD_CODE),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::SOURCE_NAME),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <String> (MSC::fieldName (MSC::SOURCE_CODE),
					     ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName(MSC::CALIBRATION_GROUP),
					  ColumnDesc::Direct));
    
    // Gain values
    if (parType == VisCalEnum::COMPLEX)
      td.addColumn (ArrayColumnDesc <Complex> (MSC::fieldName (MSC::GAIN)));
    else
      //      td.addColumn (ArrayColumnDesc <Float> (MSC::fieldName (MSC::SOLVE_PAR)));
      td.addColumn (ArrayColumnDesc <Float> (MSC::fieldName (MSC::GAIN)));
    
    // Reference frame for antenna-based corrections
    td.addColumn (ArrayColumnDesc <Int> (MSC::fieldName (MSC::REF_ANT)));
    td.addColumn (ArrayColumnDesc <Int> (MSC::fieldName (MSC::REF_FEED)));
    td.addColumn (ArrayColumnDesc <Int> (MSC::fieldName (MSC::REF_RECEPTOR)));
    td.addColumn (ArrayColumnDesc <Double> (MSC::fieldName(MSC::REF_FREQUENCY)));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::MEAS_FREQ_REF),
					  ColumnDesc::Direct));
    td.addColumn (ArrayColumnDesc <Double> (MSC::fieldName(MSC::REF_DIRECTION)));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::MEAS_DIR_REF),
					  ColumnDesc::Direct));
    
    // Pointers to Cal_Desc and Cal_History sub-tables
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::CAL_DESC_ID),
					  ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Int> (MSC::fieldName (MSC::CAL_HISTORY_ID),
					  ColumnDesc::Direct));
    
    // Add TableMeasures information for designated Measures/Quanta columns
    // 
    // TIME
    TableMeasValueDesc timeMeasVal (td, MSC::fieldName (MSC::TIME));
    TableMeasRefDesc timeMeasRef (MEpoch::DEFAULT);
    TableMeasDesc<MEpoch> timeMeasCol (timeMeasVal, timeMeasRef);
    timeMeasCol.write (td);
    
    // TIME fix Unit
    TableQuantumDesc timeQuantDesc (td, MSC::fieldName (MSC::TIME),
				    Unit ("s"));
    timeQuantDesc.write (td);
    
    // TIME_EXTRA_PREC
    TableQuantumDesc timeEPQuantDesc (td, MSC::fieldName (MSC::TIME_EXTRA_PREC),
				      Unit ("s"));
    timeEPQuantDesc.write (td);
    
    // INTERVAL
    TableQuantumDesc intervalQuantDesc (td, MSC::fieldName(MSC::INTERVAL),
					Unit ("s"));
    intervalQuantDesc.write (td);
    
    // REF_FREQUENCY
    TableMeasValueDesc refFreqMeasVal (td, MSC::fieldName (MSC::REF_FREQUENCY));
    TableMeasRefDesc refFreqMeasRef (td, MSC::fieldName (MSC::MEAS_FREQ_REF));
    TableMeasDesc<MFrequency> refFreqMeasCol (refFreqMeasVal, refFreqMeasRef);
    refFreqMeasCol.write (td);
    
    // REF_DIRECTION
    TableMeasValueDesc refDirMeasVal (td, MSC::fieldName (MSC::REF_DIRECTION));
    TableMeasRefDesc refDirMeasRef (td, MSC::fieldName (MSC::MEAS_DIR_REF));
    TableMeasDesc<MDirection> refDirMeasCol (refDirMeasVal, refDirMeasRef);
    refDirMeasCol.write (td);
    
    return td;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc& CalTableDesc2::calMainDesc()
  {
    // Return the main calibration table descriptor
    // Output:
    //    calMainDesc     TableDesc        Table descriptor (cal_main)
    //
    return itsCalMainDesc;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::calHistoryDesc()
  {
    // Return the calibration history sub-table descriptor
    // Output:
    //    calHistoryDesc  TableDesc        Table descriptor (cal_history)
    //
    return itsCalHistoryDesc;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::calDescDesc()
  {
    // Return the calibration description sub-table descriptor
    // Output:
    //    calDescDesc     TableDesc        Table descriptor (cal_desc)
    //
    return itsCalDescDesc;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::defaultFitDesc()
  {
    // Generate the default table descriptor for fit parameters
    // Output:
    //    defaultFitDesc      TableDesc        Table descriptor (fit parameters)
    // 
    TableDesc td;
    td.addColumn (ScalarColumnDesc <Bool> 
		  (MSC::fieldName (MSC::TOTAL_SOLUTION_OK), ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Float> (MSC::fieldName (MSC::TOTAL_FIT),
					    ColumnDesc::Direct));
    td.addColumn (ScalarColumnDesc <Float> 
		  (MSC::fieldName (MSC::TOTAL_FIT_WEIGHT), ColumnDesc::Direct));
    td.addColumn (ArrayColumnDesc <Bool> (MSC::fieldName (MSC::SOLUTION_OK)));
    td.addColumn (ArrayColumnDesc <Float> (MSC::fieldName (MSC::FIT)));
    td.addColumn (ArrayColumnDesc <Float> (MSC::fieldName (MSC::FIT_WEIGHT)));
    td.addColumn (ArrayColumnDesc <Bool> (MSC::fieldName (MSC::FLAG)));
    td.addColumn (ArrayColumnDesc <Float> (MSC::fieldName (MSC::SNR)));
    
    return td;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::defaultPolyDesc()
  {
    // Generate the default table descriptor for general polynomial parameters
    // Output:
    //    defaultPolyDesc      TableDesc        Table descriptor (poly parameters)
    // 
    TableDesc td;
    
    // Additional parameters required for general polynomials
    // POLY_TYPE              - polynomial type (e.g. Chebyshev or spline)
    // POLY_MODE              - polynomial y-value (e.g. A&P)
    // SCALE_FACTOR           - overall polynomial scale factor
    // VALID_DOMAIN           - valid polynomial domain [x_0, x_1]
    // N_POLY_AMP             - polynomial degree for amplitude
    // N_POLY_PHASE           - polynomial degree for phase
    // POLY_COEFF_AMP         - polynomial coefficients for amplitude
    // POLY_COEFF_PHASE       - polynomial coefficients for phase
    // PHASE_UNITS            - units for the phase polynomial
    //
    td.addColumn(ScalarColumnDesc<String>(MSC::fieldName(MSC::POLY_TYPE)));
    td.addColumn(ScalarColumnDesc<String>(MSC::fieldName(MSC::POLY_MODE)));
    td.addColumn(ScalarColumnDesc<Complex>(MSC::fieldName(MSC::SCALE_FACTOR)));
    td.addColumn(ArrayColumnDesc<Double>(MSC::fieldName(MSC::VALID_DOMAIN)));
    td.addColumn(ScalarColumnDesc<Int>(MSC::fieldName(MSC::N_POLY_AMP)));
    td.addColumn(ScalarColumnDesc<Int>(MSC::fieldName(MSC::N_POLY_PHASE)));
    td.addColumn(ArrayColumnDesc<Double>(MSC::fieldName(MSC::POLY_COEFF_AMP)));
    td.addColumn(ArrayColumnDesc<Double>(MSC::fieldName(MSC::POLY_COEFF_PHASE)));
    td.addColumn(ScalarColumnDesc<String>(MSC::fieldName(MSC::PHASE_UNITS)));
    
    return td;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::defaultSplineDesc()
  {
    // Generate the default table descriptor for spline polynomial parameters
    // Output:
    //    defaultSplineDesc      TableDesc     Table descriptor (spline parameters)
    // 
    TableDesc td;
    
    // Additional parameters required for spline polynomials.
    // N_KNOTS_AMP            - number of spline knots in amplitude
    // N_KNOTS_PHASE          - number of spline knots in phase
    // SPLINE_KNOTS_AMP       - spline knot positions for amplitude
    // SPLINE_KNOTS_PHASE     - spline knot positions for phase
    //
    td.addColumn(ScalarColumnDesc<Int>(MSC::fieldName(MSC::N_KNOTS_AMP)));
    td.addColumn(ScalarColumnDesc<Int>(MSC::fieldName(MSC::N_KNOTS_PHASE)));
    td.addColumn(ArrayColumnDesc<Double>(MSC::fieldName(MSC::SPLINE_KNOTS_AMP)));
    td.addColumn(ArrayColumnDesc<Double>
		 (MSC::fieldName(MSC::SPLINE_KNOTS_PHASE)));
    
    return td;
  };
  
  //----------------------------------------------------------------------------
  
  TableDesc CalTableDesc2::insertDesc (const TableDesc& tableDesc, 
				      const TableDesc& insert, 
				      const String& insertAfter)
  {
    // Insert one table descriptor into another after a specified
    // column name.
    // Input:
    //    tableDesc       const TableDesc&      Input table descriptor
    //    insert          const TableDesc&      Table descriptor to be inserted
    //    insertAfter     const String&         Column name to insert after
    // Output:
    //    insertDesc      TableDesc             Output table descriptor
    //
    TableDesc tdout;
    ColumnDesc* colDesc;
    Int ncol = tableDesc.ncolumn();
    Int jcol;
    
    // Loop over the number of columns in the input table descriptor
    for (jcol = 0; jcol < ncol; jcol++) {
      colDesc = new ColumnDesc (tableDesc.columnDesc (jcol));
      
      // Add column to output descriptor
      tdout.addColumn (*colDesc);
      
      // Insert second descriptor if appropriate
      if (colDesc->name() == insertAfter) {
	tdout.add (insert);
      };
      delete colDesc;
    };
    
    return tdout;
  };
  
  //----------------------------------------------------------------------------
  
  void CalTableDesc2::addDesc (const TableDesc& tableDesc, TableDesc& addTo)
  {
    addTo.add(tableDesc);
  }
  
  //----------------------------------------------------------------------------
  
  
} //# NAMESPACE CASA - END