#include <assert.h>
#include <err.h>
#include <math.h>
#include <stddef.h>
#include "calendar.h"
#include "basics.h"
#include "chinese.h"
#include "dates.h"
#include "days.h"
#include "ecclesiastical.h"
#include "gregorian.h"
#include "moon.h"
#include "nnames.h"
#include "parsedata.h"
#include "sun.h"
#include "utils.h"
static int find_days_yearly(int sday_id, int offset,
struct cal_day **dayp, char **edp);
static int find_days_moon(int sday_id, int offset,
struct cal_day **dayp, char **edp);
static int find_days_easter(int, struct cal_day **, char **);
static int find_days_paskha(int, struct cal_day **, char **);
static int find_days_advent(int, struct cal_day **, char **);
static int find_days_cny(int, struct cal_day **, char **);
static int find_days_cqingming(int, struct cal_day **, char **);
static int find_days_cjieqi(int, struct cal_day **, char **);
static int find_days_marequinox(int, struct cal_day **, char **);
static int find_days_sepequinox(int, struct cal_day **, char **);
static int find_days_junsolstice(int, struct cal_day **, char **);
static int find_days_decsolstice(int, struct cal_day **, char **);
static int find_days_newmoon(int, struct cal_day **, char **);
static int find_days_fullmoon(int, struct cal_day **, char **);
#define SPECIALDAY_INIT0 \
{ SD_NONE, NULL, 0, NULL, 0, NULL }
#define SPECIALDAY_INIT(id, name, func) \
{ (id), name, sizeof(name)-1, NULL, 0, func }
struct specialday specialdays[] = {
SPECIALDAY_INIT(SD_EASTER, "Easter", &find_days_easter),
SPECIALDAY_INIT(SD_PASKHA, "Paskha", &find_days_paskha),
SPECIALDAY_INIT(SD_ADVENT, "Advent", &find_days_advent),
SPECIALDAY_INIT(SD_CNY, "ChineseNewYear", &find_days_cny),
SPECIALDAY_INIT(SD_CQINGMING, "ChineseQingming", &find_days_cqingming),
SPECIALDAY_INIT(SD_CJIEQI, "ChineseJieqi", &find_days_cjieqi),
SPECIALDAY_INIT(SD_MAREQUINOX, "MarEquinox", &find_days_marequinox),
SPECIALDAY_INIT(SD_SEPEQUINOX, "SepEquinox", &find_days_sepequinox),
SPECIALDAY_INIT(SD_JUNSOLSTICE, "JunSolstice", &find_days_junsolstice),
SPECIALDAY_INIT(SD_DECSOLSTICE, "DecSolstice", &find_days_decsolstice),
SPECIALDAY_INIT(SD_NEWMOON, "NewMoon", &find_days_newmoon),
SPECIALDAY_INIT(SD_FULLMOON, "FullMoon", &find_days_fullmoon),
SPECIALDAY_INIT0,
};
static int
find_days_easter(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_EASTER, offset, dayp, edp);
}
static int
find_days_paskha(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_PASKHA, offset, dayp, edp);
}
static int
find_days_advent(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_ADVENT, offset, dayp, edp);
}
static int
find_days_cny(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_CNY, offset, dayp, edp);
}
static int
find_days_cqingming(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_CQINGMING, offset, dayp, edp);
}
static int
find_days_marequinox(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_MAREQUINOX, offset, dayp, edp);
}
static int
find_days_sepequinox(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_SEPEQUINOX, offset, dayp, edp);
}
static int
find_days_junsolstice(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_JUNSOLSTICE, offset, dayp, edp);
}
static int
find_days_decsolstice(int offset, struct cal_day **dayp, char **edp)
{
return find_days_yearly(SD_DECSOLSTICE, offset, dayp, edp);
}
static int
find_days_yearly(int sday_id, int offset, struct cal_day **dayp, char **edp)
{
struct cal_day *dp;
struct date date;
double t, longitude;
char buf[32];
int rd, approx, month;
int year1, year2;
int count = 0;
year1 = gregorian_year_from_fixed(Options.day_begin);
year2 = gregorian_year_from_fixed(Options.day_end);
for (int y = year1; y <= year2; y++) {
t = NAN;
switch (sday_id) {
case SD_EASTER:
rd = easter(y);
break;
case SD_PASKHA:
rd = orthodox_easter(y);
break;
case SD_ADVENT:
rd = advent(y);
break;
case SD_CNY:
rd = chinese_new_year(y);
break;
case SD_CQINGMING:
rd = chinese_qingming(y);
break;
case SD_MAREQUINOX:
case SD_JUNSOLSTICE:
case SD_SEPEQUINOX:
case SD_DECSOLSTICE:
if (sday_id == SD_MAREQUINOX) {
month = 3;
longitude = 0.0;
} else if (sday_id == SD_JUNSOLSTICE) {
month = 6;
longitude = 90.0;
} else if (sday_id == SD_SEPEQUINOX) {
month = 9;
longitude = 180.0;
} else {
month = 12;
longitude = 270.0;
}
date_set(&date, y, month, 1);
approx = fixed_from_gregorian(&date);
t = solar_longitude_atafter(longitude, approx);
t += Options.location->zone;
rd = floor(t);
break;
default:
errx(1, "%s: unknown special day: %d",
__func__, sday_id);
}
if ((dp = find_rd(rd, offset)) != NULL) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats", __func__);
return count;
}
if (!isnan(t)) {
format_time(buf, sizeof(buf), t);
edp[count] = xstrdup(buf);
}
dayp[count++] = dp;
}
}
return count;
}
static int
find_days_cjieqi(int offset, struct cal_day **dayp, char **edp)
{
const struct chinese_jieqi *jq;
struct cal_day *dp;
struct date date;
char buf[32];
int year1, year2;
int rd, rd_begin, rd_end;
int count = 0;
year1 = gregorian_year_from_fixed(Options.day_begin);
year2 = gregorian_year_from_fixed(Options.day_end);
for (int y = year1; y <= year2; y++) {
date_set(&date, y, 1, 1);
rd_begin = fixed_from_gregorian(&date);
date.year++;
rd_end = fixed_from_gregorian(&date);
if (rd_end > Options.day_end)
rd_end = Options.day_end;
for (rd = rd_begin; rd <= rd_end; rd += 14) {
rd = chinese_jieqi_onafter(rd, C_JIEQI_ALL, &jq);
if (rd > rd_end)
break;
if ((dp = find_rd(rd, offset)) != NULL) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats",
__func__);
return count;
}
snprintf(buf, sizeof(buf), "%s, %s",
jq->name, jq->zhname);
edp[count] = xstrdup(buf);
dayp[count++] = dp;
}
}
}
return count;
}
static int
find_days_newmoon(int offset, struct cal_day **dayp, char **edp)
{
return find_days_moon(SD_NEWMOON, offset, dayp, edp);
}
static int
find_days_fullmoon(int offset, struct cal_day **dayp, char **edp)
{
return find_days_moon(SD_FULLMOON, offset, dayp, edp);
}
static int
find_days_moon(int sday_id, int offset, struct cal_day **dayp, char **edp)
{
struct cal_day *dp;
struct date date;
double t, t_begin, t_end;
char buf[32];
int year1, year2;
int count = 0;
year1 = gregorian_year_from_fixed(Options.day_begin);
year2 = gregorian_year_from_fixed(Options.day_end);
for (int y = year1; y <= year2; y++) {
date_set(&date, y, 1, 1);
t_begin = fixed_from_gregorian(&date) - Options.location->zone;
date.year++;
t_end = fixed_from_gregorian(&date) - Options.location->zone;
if (t_end > Options.day_end + 1 - Options.location->zone)
t_end = Options.day_end + 1 - Options.location->zone;
for (t = t_begin; t <= t_end; ) {
switch (sday_id) {
case SD_NEWMOON:
t = new_moon_atafter(t);
break;
case SD_FULLMOON:
t = lunar_phase_atafter(180, t);
break;
default:
errx(1, "%s: unknown moon event: %d",
__func__, sday_id);
}
if (t > t_end)
break;
t += Options.location->zone;
if ((dp = find_rd(floor(t), offset)) != NULL) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats",
__func__);
return count;
}
format_time(buf, sizeof(buf), t);
edp[count] = xstrdup(buf);
dayp[count++] = dp;
}
}
}
return count;
}
int
find_days_ymd(int year, int month, int day,
struct cal_day **dayp, char **edp __unused)
{
struct cal_day *dp = NULL;
int count = 0;
while ((dp = loop_dates(dp)) != NULL) {
if (year >= 0 && year != dp->year)
continue;
if ((dp->month == month && dp->day == day) ||
(day == 0 && dp->last_dom && month == dp->month % 12 + 1)) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats", __func__);
return count;
}
dayp[count++] = dp;
}
}
return count;
}
int
find_days_dom(int dom, struct cal_day **dayp, char **edp __unused)
{
struct cal_day *dp = NULL;
int count = 0;
while ((dp = loop_dates(dp)) != NULL) {
if (dp->day == dom ||
(dom == 0 && dp->last_dom)) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats", __func__);
return count;
}
dayp[count++] = dp;
}
}
return count;
}
int
find_days_month(int month, struct cal_day **dayp, char **edp __unused)
{
struct cal_day *dp = NULL;
int count = 0;
while ((dp = loop_dates(dp)) != NULL) {
if (dp->month == month) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats", __func__);
return count;
}
dayp[count++] = dp;
}
}
return count;
}
int
find_days_mdow(int month, int dow, int index,
struct cal_day **dayp, char **edp __unused)
{
struct cal_day *dp = NULL;
int count = 0;
while ((dp = loop_dates(dp)) != NULL) {
if (month >= 0 && month != dp->month)
continue;
if (dp->dow[0] == dow) {
if (index != 0 &&
(index != dp->dow[1] && index != dp->dow[2])) {
continue;
}
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats", __func__);
return count;
}
dayp[count++] = dp;
}
}
return count;
}