/**
 * \brief Implementation of the set command
 */
int	set_command(MountPtr mount, const RaDec& radec) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "ra = %s", radec.ra().hms().c_str());
	debug(LOG_DEBUG, DEBUG_LOG, 0, "dec = %s", radec.dec().dms().c_str());
	if (!dryrun) {
		mount->Goto(radec);
		return wait_command(mount, await_completion);
	}
	return get_command(mount);
}
bool	RaDec::operator>(const RaDec& other) const {
	if (dec() > other.dec()) {
		return true;
	}
	if (dec() < other.dec()) {
		return false;
	}
	return ra() > other.ra();
}
void	CelestronMount::Goto(const RaDec& radec) {
	std::string	cmd;
	if (version > 106) {
		cmd = stringprintf("r%08X,%08X",
			angle32(radec.ra()), angle32(radec.dec()));
	} else {
		cmd = stringprintf("R%04X,%04X",
			angle16(radec.ra()), angle16(radec.dec()));
	}
	debug(LOG_DEBUG, DEBUG_LOG, 0, "command sent: %s", cmd.c_str());
	write(cmd);
	getprompt();
}
int	main(int argc, char *argv[]) {
	int	c;
	while (EOF != (c = getopt(argc, argv, "d")))
		switch (c) {
		case 'd':
			debuglevel = LOG_DEBUG;
			break;
		}

	// create a Chart factory
	CatalogPtr 	catalog = CatalogFactory::get(CatalogFactory::Combined,
					"/usr/local/starcatalogs");
	TurbulencePointSpreadFunction   psf(2);
	ChartFactory    factory(catalog, psf, 14, 100);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "chart factory created");

	// create an Image Normalizer
	ImageNormalizer normalizer(factory);

	// prepare the initial transformation
	Projection      projection(M_PI * 162 / 180, Point(838, 182), 0.98);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "projection: %s",
	projection.toString().c_str());

	// get the image from the input file
	FITSin  in("andromeda-base.fits");
	ImagePtr        imageptr = in.read();

	// apply the normalizer to the 
	debug(LOG_DEBUG, DEBUG_LOG, 0, "apply normalizer");
	RaDec   center = normalizer(imageptr, projection);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "true center: %s",
	center.toString().c_str());
	debug(LOG_DEBUG, DEBUG_LOG, 0, "transformation: %s",
	projection.toString().c_str());

	return EXIT_SUCCESS;
}
/**
 * \brief Implementation of the get command
 */
int	get_command(MountPtr mount) {
	RaDec	radec = mount->getRaDec();
	if (decimal) {
		std::cout << radec.ra().hours();
		std::cout << " ";
		if (radec.dec() > Angle(M_PI)) {
			std::cout << (radec.dec() - Angle(2 * M_PI)).degrees();
		} else {
			std::cout << radec.dec().degrees();
		}
	} else {
		std::cout << radec.ra().hms();
		std::cout << " ";
		if (radec.dec() > Angle(M_PI)) {
			std::cout << (radec.dec() - Angle(2 * M_PI)).dms();
		} else {
			std::cout << radec.dec().dms();
		}
	}
	std::cout << " ";
	std::cout << state2string(mount->state());
	std::cout << std::endl;
	return EXIT_SUCCESS;
}
/**
 * \brief Main method
 */
int main(int argc, char *argv[]) {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "mount utility");
	int	c;
	int	longindex;
	putenv((char *)"POSIXLY_CORRECT=1");	// cast to silence compiler
	while (EOF != (c = getopt_long(argc, argv, "c:dh", longopts,
		&longindex))) {
		switch (c) {
		case 'c':
			Configuration::set_default(std::string(optarg));
			break;
		case 'd':
			debuglevel = LOG_DEBUG;
			break;
		case 'h':
			usage(argv[0]);
			return EXIT_SUCCESS;
		case 'n':
			dryrun = true;
			break;
		case 'f':
			decimal = true;
			break;
		case 'w':
			await_completion = true;
			break;
		case 1:
			switch (longindex) {
			}
			break;
		}
	}

	// next argument must be the command
	if (argc <= optind) {
		throw std::runtime_error("missing command argument");
	}
	std::string	command(argv[optind++]);

	// call the command specific functions
	if (command == "help") {
		return help_command();
	}

	// the other commands need a repository
	astro::module::Repository	repository;
	astro::module::Devices	devices(repository);

	// list command
	if (command == "list") {
		return list_command(devices);
	}

	// other commands need a mount url 
	if (argc <= optind) {
		throw std::runtime_error("missing mount URL");
	}
	DeviceName	mountname(argv[optind++]);
	if (!mountname.hasType(DeviceName::Mount)) {
		throw std::runtime_error("not a mount device name");
	}
	debug(LOG_DEBUG, DEBUG_LOG, 0, "mount device name: %s",
		mountname.toString().c_str());

	// use the Devices class to get the mount associated with this name
	MountPtr	mount = devices.getMount(mountname);
	if (command == "get") {
		return get_command(mount);
	}
	if (command == "cancel") {
		return cancel_command(mount);
	}
	if (command == "wait") {
		return wait_command(mount, true);
	}
	if (command == "set") {
		if (argc < optind + 2) {
			throw std::runtime_error("two angle arguments missing");
		}
		RaDec	radec;
		radec.ra() = Angle::hms_to_angle(argv[optind++]);
		radec.dec() = Angle::dms_to_angle(argv[optind++]);
		return set_command(mount, radec);
	}

	throw std::runtime_error("unknown command");
}
SphericalCoordinates::SphericalCoordinates(const RaDec& radec)
	: TwoAngles(radec.ra(), Angle(M_PI / 2) - radec.dec()) {
}
/**
 * \brief Compute proper motion corrected position of an object
 */
RaDec	CelestialObject::position(const double epoch) const {
	RaDec	result;
	result.ra() = ra() + pm().ra() * epoch;
	result.dec() = dec() + pm().dec() * epoch;
	return result;
}