/*
* MPIA - GEIRS project
*
* $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/iif/Interface/Ltcs/Iif/Iif2fitsKw.cc 374 2018-01-11 11:43:51Z depa $
*
*/
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iomanip>

#include "Ltcs/Iif/Iif2fitsKw.h"

using namespace std ;

#ifndef DR2D
/** conversion factor radians to degrees.
* Equals 180/Pi, see extern/sofa/sofa/.../c/src/sofam.h
*/
#define DR2D 57.29577951308232087679815
#endif

/*!**************************************** 
* @brief Constructor given an array of windows.
* @param dd IIF dictionary name
* @param typ FITS type
* @param key FITS keyword
* @param un Physical unit for use in the FITS comment.
* @param comm The human readable FITS comment.
* @param bits Use flags. LSB indicates with 0 that this is a standard FITS card and with 1 that this is a FITS COMMENT.
* @since 2016-10-11
* @since 2016-11-03 Additional arguments bits
* @author Richard J. Mathar
*/
Ltcs::Iif2fitsKw::Iif2fitsKw(const string dd, const string typ, const string key, const string un , const string comm, const short bits)
	: ddName(dd), unit(un), type(typ), comment(comm), keyw(key), val("NOTSPECIFIED"), updt(0), flags(bits)
{
} /* Liif2fits::Liif2fits */

/*!**************************************** 
* @brief Represent the keyword and value as a FITS card
* @since 2016-10-12
* @author Richard J. Mathar
*/
string Ltcs::Iif2fitsKw::toString() const
{
	string card = (flags & 1) ? "COMMENT " : "" ;
	card += keyw;

	if ( type == "bool")
	{
		if ( val.substr(0,1) == "1" )
			card += " = T / " ;
		else
			card += " = F / " ;
	}
	else if ( type == "string")
	{
		/* add tic marks for strings, also for NOTSPECIFIED */
		card += " = '" + val + "' / " ;
		if ( unit.length() > 0 )
			card += "[" + unit + "] " ;
	}
	else
	{
		/* type=int, long, float, double.
		* Keyword value provided in radians by IIF and printed here in degrees
		*/
		const double valD = atof(val.c_str())*DR2D ;
		ostringstream valS ;
		valS << std::setprecision(14) << valD ;
		if ( keyw == "DEC" )
		{
			if ( val != "NOTSPECIFIED") 
				card = "DEC = " + valS.str() + " / [deg] " ;
			else
				// card = "DEC = / [deg] " ;
				return string() ;
		}
		else if ( keyw == "RA" )
		{
			if ( val != "NOTSPECIFIED") 
				card = "RA = " + valS.str() + " / [deg] " ;
			else
				//card = "RA = / [deg] " ;
				return string() ;
		}
		else if ( ddName.substr(1) == "_AchievedALT" )
		{
			if ( val != "NOTSPECIFIED") 
				card = "ALT = " + valS.str() + " / [deg] " ;
			else
				//card = "ALT = / [deg] " ;
				return string() ;
		}
		else if ( ddName.substr(1) == "_AchievedAZ" )
		{
			if ( val != "NOTSPECIFIED") 
				card = "AZ = " + valS.str() + " / [deg] " ;
			else
				//card = "AZ = / [deg] " ;
				return string() ;
		}
		else if ( ddName == "MountAchievedHA" )
		{
			if ( val != "NOTSPECIFIED") 
				card = "HA = " + valS.str() + " / [deg] " ;
			else
				//card = "HA = / [deg] " ;
				return string() ;
		}

		else if ( val == "NOTSPECIFIED" || val == "nan" )
		{
			if ( type == "int" || type == "long")
				card += " = -9999 / [" + unit + "] " ;
			else
				card += " = -9999.0 / [" + unit + "] " ;
		}
		else
		{
			card += " = " + val + " / " ;
			if ( unit.length() > 0 )
				card += "[" + unit + "] " ;
		}
	}

	/* omit the leading two digits (century) of the year to save that space
	*/
	card += ddName + " " + tstamp().substr(2) + " " + comment ;

	return card ;
} /* Iif2fitsKw::toString */

/*!**************************************** 
* @brief Represent the keyword and value side by side
* @return A string which is very similar to a data dictionary dump; 
*   keyword and value separted by a blank.
* @since 2017-05-22
* @author Richard J. Mathar
*/
string Ltcs::Iif2fitsKw::toKVPairString() const
{
	ostringstream card ;
	card << ddName <<" " << val; 

	return card.str() ;
} /* Iif2fitsKw::toKVPairString */

/*!****************
* @brief Convert into a FTIS card of basda-vino.
* @return Keyword name, value, unit and comment converted to a VinoFitsCard.
* @since 2017-05-22
*/
Basda::VinoFitsCard Ltcs::Iif2fitsKw::getFits() const
{
	Basda::VinoFitsCard card ;
	ostringstream cmt ;

	if ( flags & 1)
	{
		/* this is a pure comment card */
		cmt << ddName << " = " << val << " / [" << unit << "] " << tstamp() ;
		card.set(std::string(), 0, cmt.str() ) ;
	}
	else if ( type == "bool")
	{
		cmt << ddName << " " <<  tstamp().substr(2) << " " << comment ;
		if ( val.substr(0,1) == "1" )
			card.set(keyw, true, cmt.str() ) ;
		else
			card.set(keyw, false, cmt.str() ) ;
	}
	else if ( type == "string")
	{
		if ( unit.length() > 0 )
			cmt << "[" << unit << "] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
		else
			cmt << ddName << " " <<  tstamp().substr(2) << " " << comment ;

		card.set(keyw, val, cmt.str() ) ;
	}
	else
	{
		/* type=int, long, float, double.
		* Keyword value provided in radians by IIF and printed here in degrees
		*/
		const double valD = atof(val.c_str())*DR2D ;

		if ( keyw == "DEC" )
		{
			cmt << "[deg] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
			if ( val != "NOTSPECIFIED") 
				card.set(keyw, valD, cmt.str() ) ;
		}
		else if ( keyw == "RA" )
		{
			cmt << "[deg] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
			if ( val != "NOTSPECIFIED") 
				card.set(keyw, valD, cmt.str() ) ;
		}
		else if ( ddName.substr(1) == "_AchievedALT" )
		{
			cmt << "[deg] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
			if ( val != "NOTSPECIFIED") 
				card.set("ALT", valD, cmt.str() ) ;
		}
		else if ( ddName.substr(1) == "_AchievedAZ" )
		{
			cmt << "[deg] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
			if ( val != "NOTSPECIFIED") 
				card.set("AZ", valD, cmt.str() ) ;
		}
		else if ( ddName == "MountAchievedHA" )
		{
			cmt << "[deg] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
			if ( val != "NOTSPECIFIED") 
				card.set("HA", valD, cmt.str() ) ;
		}
		else if ( val == "NOTSPECIFIED" || val == "nan" )
		{
			cmt << "[" << unit << "] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
			if ( type == "int" || type == "long")
				card.set(keyw, -9999, cmt.str() ) ;
			else
				card.set(keyw, -9999.0, cmt.str() ) ;
		}
		else
		{
			if ( unit.length() > 0 )
				cmt << "[" << unit << "] " << ddName << " " <<  tstamp().substr(2) << " " << comment ;
			else
				cmt << ddName << " " <<  tstamp().substr(2) << " " << comment ;

			if ( type == "int" || type == "long" )
			{
				const int vali = atoi(val.c_str()) ;
				card.set(keyw, vali, cmt.str() ) ;
			}
			else if ( type == "float" )
			{
				const float valf = atof(val.c_str()) ;
				card.set(keyw, valf, cmt.str() ) ;
			}
			else
			{
				const double vald = atof(val.c_str()) ;
				card.set(keyw, vald, cmt.str() ) ;
			}
		}
	}
	return card ;
} /* getFits */


/*!****************************
* @brief Convert the time stamp to an ISO string.
* @return A string of the format YYYY-MM-DD HH:MM::SS
*/
string Ltcs::Iif2fitsKw::tstamp() const
{
	/* place for 4 digits of year, 2digits of month, 2 digits of day
	* and two dashes and \0
	*/
	char tstr[80] ;
	struct tm tm;
	struct tm * ltime = gmtime_r(&updt, &tm);

	strftime(tstr, sizeof(tstr), "%F", ltime);
	tstr[10] = ' ' ;
	tstr[11] = '\0' ;
	strftime(tstr+strlen(tstr), sizeof(tstr)-strlen(tstr), "%T", ltime);

        return string(tstr) ;

} /* tstamp */

/*!************************************************
* @brief Create a FITS template line
* @param[in,out] os The stream to be filled.
* @param[in] rl The range list to be printed.
* @return  The stream itself.
* @since 2016-10-11
*/
ostream & operator<<(ostream &os, const Ltcs::Iif2fitsKw & k)
{
	os << k.toString() ;
	return os ;
}
