#!/usr/bin/perl
use strict;
use warnings;
use utf8;                    # Allow Unicode in source code
use open ':std', ':encoding(UTF-8)';  # Set STDOUT to UTF-8
use Encode qw(decode_utf8);
use Math::Trig;
use DateTime;
use DateTime::Event::Easter;
use Astro::MoonPhase;
use Astro::Coord::ECI::Sun;
use Time::Local qw(timegm);

# Get year and latitude from command line or use defaults
my $year = $ARGV[0] || (localtime)[5] + 1900;
my $latitude = $ARGV[1] || 47.3;
my $longitude = 8.9; #only to shift lunar/solar year relative to noon
my $church_year = 1; # draw church holidays?
my $holidays = 1; # draw school holidays + weekends?

# A4 dimensions in mm
my $a4_width = 210;
my $a4_height = 297;

# Canvas dimensions (printable area with 5mm margins)
my $canvas_width = 200;
my $canvas_height = 287;

# Stroke color and width
my $stroke_width = 0.1;
my $fat_stroke_width = 0.2;
my $min_width = 0.02;
my $stroke_color = "black";
my $ink_color = "#202020";
my $red_color = "#C00000"; 
my $day_color = "#FFD700"; 
my $night_color = "#1E3A8A"; 
my $fullmoon_color = "floralwhite";
my $newmoon_color = "#1E3A8A";
my $easter_color = "#FF6060"; 
my $lent_color = "#101010";
my $fastfree_color = "gold";
my $maslenitsa_color = "gold";

# Calculate offsets to center canvas
my $offset_x = ($a4_width - $canvas_width) / 2;
my $offset_y = ($a4_height - $canvas_height) / 2;

my $center_x = $canvas_width/2 + $offset_x;
my $center_y = $canvas_height/2 + $offset_y;

my $outer_radius = $canvas_width/2;
my $day_night_radius = $outer_radius * 0.98;
my $inner_radius = $outer_radius * 0.88;     # Inner boundary of day/night ring
my $lunation_radius = $outer_radius * 0.72;
my $month_radius = $outer_radius * 0.53;     # Inner ring for months
my $week_radius = $outer_radius * 0.43;
my $smallest_radius = $outer_radius * 0.22;

#my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my @month_names = ("Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"); 
#= qw(Januar Februar März April Mai Juni Juli August September Oktober November Dezember);
my @zodiac = qw(♈︎ ♉︎ ♊︎ ♋︎ ♌︎ ♍︎ ♎︎ ♏︎ ♐︎ ♑︎ ♒︎ ♓︎);
my @lunar_names = qw(wintermanoth hornunc lenzinmanoth ostermanoth wunnimanoth brachmanoth hewimanoth aranmanoth witumanoth windumemanoth herbistmanoth heilagmanoth);

# Days in each month
my @days_in_month = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
$days_in_month[1] = 29 if is_leap_year($year);  # February in leap year

# Calculate moon phases for the year
my $start_dt = DateTime->new(year => $year, month => 1, day => 1, hour => 0, minute => 0, second => 0, time_zone => 'UTC');
my $start_time = $start_dt->epoch;
my $end_dt = DateTime->new(year => $year + 1, month => 1, day => 1, hour => 0, minute => 0, second => 0, time_zone => 'UTC');
my $end_time = $end_dt->epoch ;

my @new_moons = ();
my @full_moons = ();



my $time = $start_time; 
while ($time < ($end_time + 30*86400)) {
    my (@phasetimes) = phasehunt($time);
    # phasehunt returns: new, first quarter, full, last quarter, next new
    my $new_time = $phasetimes[0];
    my $full_time = $phasetimes[2];
    
    if ($new_time >= $start_time && $new_time < $end_time) {

        push @new_moons, (($new_time - $start_time)/86400 + $longitude/360);
        #print "<!-- new moon ".($new_moons[$#new_moons])."-->\n";
    }
    
    if ($full_time >= $start_time && $full_time < $end_time) {

        push @full_moons, (($full_time - $start_time)/86400 + $longitude/360);
        #print "<!-- full moon ".($full_moons[$#full_moons])."-->\n";
    }
    
    $time = $phasetimes[4] + 86400 * 28;  # Advance by ~half a lunar cycle to robustly skip potential stalls
}

# Check if leap year
sub is_leap_year {
    my $y = shift;
    return ($y % 4 == 0 && $y % 100 != 0) || ($y % 400 == 0);
}

# Calculate sunrise and sunset times for a given day of year
sub calculate_sun_times {
    my ($day_of_year, $year, $lat) = @_;
    
    # Convert latitude to radians
    my $lat_rad = deg2rad($lat);
    
    # Calculate solar declination angle (varies throughout the year)
    # Using the approximation: declination = 23.45 * sin(360 * (284 + day_of_year) / 365)
    my $declination_deg = 23.45 * sin(deg2rad(360 * (284 + $day_of_year) / 365));
    my $declination_rad = deg2rad($declination_deg);
    
    # Calculate hour angle at sunrise/sunset
    # cos(hour_angle) = -tan(latitude) * tan(declination)
    my $argument = -tan($lat_rad) * tan($declination_rad);
    
    # Handle polar day/night conditions
    if ($argument < -1) {
        return (0, 24);  # Polar day (sun never sets)
    }
    elsif ($argument > 1) {
        return (12, 12); # Polar night (sun never rises)
    }
    
    # Calculate hour angle and convert to hours
    my $hour_angle_rad = acos($argument);
    my $hour_angle_hours = $hour_angle_rad * 12 / pi;
    
    my $sunrise = 12 - $hour_angle_hours;
    my $sunset = 12 + $hour_angle_hours;
    
    return ($sunrise, $sunset);
}

my $days_in_year = is_leap_year($year) ? 366 : 365;
my $leap_adjust = is_leap_year($year) ? 1 : 0;

# Calculate solar quarters (fractional DOY)
#my @solar_quarters = (79 + $leap_adjust, 172 + $leap_adjust, 265 + $leap_adjust, 355 + $leap_adjust);

my $year_start = eval { timegm(0, 0, 0, 1, 0, $year - 1900) };
my $year_end = eval { timegm(0, 0, 0, 1, 0, $year + 1 - 1900) };
my $sun = Astro::Coord::ECI::Sun->new();
$sun->universal($year_start); 
my @events;

while (1) {
    my ($time, $quarter, $desc) = $sun->next_quarter();
    last if $time >= $year_end;
    next if $time < $year_start;
    push @events, [$time, $quarter, $desc];
}
@events = sort { $a->[0] <=> $b->[0] } @events;

my @solar_quarters = ();
foreach my $e (@events) {
    $solar_quarters[$#solar_quarters+1] = (($e->[0] - $year_start) / 86400) + $longitude/360;
    #print "<!-- solar quarter ".sprintf("%.4f",  $solar_quarters[$#solar_quarters])."-->\n"; 
}

sub format_time {
    my $time = shift;
    my $rounded_time = int($time + 0.5);
    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($rounded_time);
    $year += 1900;
    $mon += 1;
    return sprintf("%04d-%02d-%02d %02d:%02d:%02d UTC", $year, $mon, $mday, $hour, $min, $sec);
}

# Start SVG
print qq{<?xml version="1.0" encoding="UTF-8"?>\n<svg width="210mm" height="297mm" viewBox="0 0 $a4_width $a4_height" xmlns="http://www.w3.org/2000/svg">\n};
print qq{<defs>\n};
print qq{  <style>\n};
print qq{    .month-text { font-family: Liberation, serif; font-size: 5.0; text-anchor: middle; dominant-baseline: middle; fill: $ink_color; }\n};
print qq{    .lunation-text { font-family: Uncial, serif; font-size: 2.8; text-anchor: middle; dominant-baseline: middle; fill: $ink_color; }\n};
print qq{    .sunday-text { font-family: Arial, sans-serif; font-size: 1.8; text-anchor: middle; dominant-baseline: middle; fill: $red_color; }\n};
print qq{    .week-text { font-family: Arial, sans-serif; font-size: 2.2; text-anchor: middle; dominant-baseline: middle; fill: $ink_color; }\n};
print qq{    .year-text { font-family: Liberation, serif; font-size: 8.4; text-anchor: middle; alignment-baseline: middle; dominant-baseline: middle; fill: $ink_color; font-weight: bold; }\n};
print qq{    .feast-text { font-family: Oglavie, serif; font-size: 2.2; text-anchor: left; alignment-baseline: middle; dominant-baseline: central; fill: $red_color; }\n};
print qq{    .easter-text { font-family: Oglavie, serif; font-size: 2.9; text-anchor: left; alignment-baseline: middle; dominant-baseline: central; fill: $red_color; }\n};
print qq{  </style>\n};
print qq{</defs>\n};

# Draw colored segments for day/night
my $day_angle_step = 360 / $days_in_year;
for my $day (1 .. $days_in_year) {
    my ($sunrise, $sunset) = calculate_sun_times($day, $year, $latitude);
    
    # Calculate angles for this day
    my $day_start_angle = ($day - 1) * $day_angle_step - 90; # Start from top
    my $day_end_angle = $day * $day_angle_step - 90;
    
    # Map sunrise/sunset hours to angles within the day
    # Sunrise at 6am would be 0.25 through the day, sunset at 6pm would be 0.75
    my $sunrise_fraction = $sunrise / 24;
    my $sunset_fraction = $sunset / 24;
    
    my $day_angle_range = $day_end_angle - $day_start_angle;
    my $sunrise_angle = $day_start_angle + ($sunrise_fraction * $day_angle_range);
    my $sunset_angle = $day_start_angle + ($sunset_fraction * $day_angle_range);
    
    # Draw morning night segment (midnight to sunrise)
    if ($sunrise_fraction > 0) {
        my $path_data = create_arc_path($center_x, $center_y, $inner_radius, $day_night_radius, $day_start_angle, $sunrise_angle);
        print qq{<path d="$path_data" fill="$night_color" />\n};
    }
    
    # Draw day segment (sunrise to sunset)
    if ($sunset > $sunrise) {
        my $path_data = create_arc_path($center_x, $center_y, $inner_radius, $day_night_radius, $sunrise_angle, $sunset_angle);
        print qq{<path d="$path_data" fill="$day_color" />\n};
    }
    
    # Draw evening night segment (sunset to midnight)
    if ($sunset_fraction < 1) {
        my $path_data = create_arc_path($center_x, $center_y, $inner_radius, $day_night_radius, $sunset_angle, $day_end_angle);
        print qq{<path d="$path_data" fill="$night_color" />\n};
    }
    #draw spoke to midnight
    my $x1 = $center_x + $week_radius * cos(deg2rad($day_start_angle));
    my $y1 = $center_y + $week_radius * sin(deg2rad($day_start_angle));
    my $x2 = $center_x + $outer_radius * cos(deg2rad($day_start_angle));
    my $y2 = $center_y + $outer_radius * sin(deg2rad($day_start_angle));
#    print qq{<line x1="$x1" y1="$y1" x2="$x2" y2="$y2" stroke="$stroke_color" stroke-width="$min_width" />\n};
    print "<line x1=\"".sprintf("%.4f",$x1)."\" y1=\"".sprintf("%.4f",$y1)."\" x2=\"".sprintf("%.4f",$x2)."\" y2=\"".sprintf("%.4f",$y2)."\" stroke=\"$stroke_color\" stroke-width=\"$min_width\" />\n";

}

# Function to create arc path for SVG
sub create_arc_path {
    my ($cx, $cy, $inner_r, $outer_r, $start_angle, $end_angle) = @_;
    
    my $start_rad = deg2rad($start_angle);
    my $end_rad = deg2rad($end_angle);
    
    # Calculate points (unitless numbers for path d)
    my $x1 = $cx + $inner_r * cos($start_rad);
    my $y1 = $cy + $inner_r * sin($start_rad);
    my $x2 = $cx + $outer_r * cos($start_rad);
    my $y2 = $cy + $outer_r * sin($start_rad);
    my $x3 = $cx + $outer_r * cos($end_rad);
    my $y3 = $cy + $outer_r * sin($end_rad);
    my $x4 = $cx + $inner_r * cos($end_rad);
    my $y4 = $cy + $inner_r * sin($end_rad);
    
    my $large_arc = abs($end_angle - $start_angle) > 180 ? 1 : 0;
    
    return "M ".sprintf("%.4f",$x1)." ".sprintf("%.4f",$y1)." L ".sprintf("%.4f",$x2)." ".sprintf("%.4f",$y2)." A $outer_r $outer_r 0 $large_arc 1 ".sprintf("%.4f",$x3)." ".sprintf("%.4f",$y3)." L ".sprintf("%.4f",$x4)." ".sprintf("%.4f",$y4)." A $inner_r $inner_r 0 $large_arc 0 ".sprintf("%.4f",$x1)." ".sprintf("%.4f",$y1)." Z";
}

# Draw main circles
print qq{<circle r="$outer_radius" cx="$center_x" cy="$center_y" stroke="$stroke_color" fill="none" stroke-width="$stroke_width" />\n};
print qq{<circle r="$day_night_radius" cx="$center_x" cy="$center_y" stroke="$stroke_color" fill="none" stroke-width="$stroke_width" />\n};
print qq{<circle r="$inner_radius" cx="$center_x" cy="$center_y" stroke="$stroke_color" fill="none" stroke-width="$stroke_width" />\n};
print qq{<circle r="$lunation_radius" cx="$center_x" cy="$center_y" stroke="$stroke_color" fill="none" stroke-width="$stroke_width" />\n};
print qq{<circle r="$month_radius" cx="$center_x" cy="$center_y" stroke="$stroke_color" fill="none" stroke-width="$stroke_width" />\n};
print qq{<circle r="$week_radius" cx="$center_x" cy="$center_y" stroke="$stroke_color" fill="none" stroke-width="$stroke_width" />\n};

# Draw month segments and labels
my $cumulative_days = 0;
for my $month (0 .. 11) {
    # Calculate start angle for this month
    my $start_angle = ($cumulative_days * $day_angle_step) - 90;
    my $end_angle = (($cumulative_days + $days_in_month[$month]) * $day_angle_step) - 90;
    my $mid_angle = ($start_angle + $end_angle) / 2;
    
    my $start_rad = deg2rad($start_angle);
    my $mid_rad = deg2rad($mid_angle);
    
    # Draw month division line
    my $x1 = $center_x + $month_radius * cos($start_rad);
    my $y1 = $center_y + $month_radius * sin($start_rad);
    my $x2 = $center_x + $lunation_radius * cos($start_rad);
    my $y2 = $center_y + $lunation_radius * sin($start_rad);
    
    print qq{<line x1="$x1" y1="$y1" x2="$x2" y2="$y2" stroke="$stroke_color" stroke-width="$stroke_width" />\n};
    
    # Add month label
    my $label_radius = 0.99*($month_radius + $lunation_radius) / 2;
    my $text_x = $center_x + $label_radius * cos($mid_rad);
    my $text_y = $center_y + $label_radius * sin($mid_rad);
    my $month_rot = 15 + $month * 30;
    
    #print qq{<text x="$text_x" y="$text_y" class="month-text" transform="rotate($month_rot $text_x $text_y)">$month_names[$month]</text>\n};
    curvedtxt($center_x,$center_y, $label_radius, $mid_rad, $month_names[$month], 5.0, 0.9, "month-text" );
    
    $cumulative_days += $days_in_month[$month];
}

# Draw zodiac labels
for my $zod (0 .. 11) {
    my $angle = ($solar_quarters[0] + ($zod+0.5)*$days_in_year/12)/$days_in_year*360 -90;

    my $label_radius = $smallest_radius * 1.16;
    my $text_x = $center_x + $label_radius * cos(deg2rad($angle));
    my $text_y = $center_y + $label_radius * sin(deg2rad($angle));
    my $label_rot = $angle +90;
    
    print qq{<text x="$text_x" y="$text_y" class="month-text" transform="rotate($label_rot $text_x $text_y)">$zodiac[$zod]</text>\n};
    
}

# lunation division lines at new moons
foreach my $doy (@new_moons) {
    my $angle = ($doy * $day_angle_step) - 90;
    my $angle_rad = deg2rad($angle);
    my $x1 = $center_x + $lunation_radius * cos($angle_rad);
    my $y1 = $center_y + $lunation_radius * sin($angle_rad);
    my $x2 = $center_x + $inner_radius * cos($angle_rad);
    my $y2 = $center_y + $inner_radius * sin($angle_rad);
    print qq{<line x1="$x1" y1="$y1" x2="$x2" y2="$y2" stroke="$newmoon_color" stroke-width="$fat_stroke_width" />\n};
}
# lunation division lines at full moons
foreach my $doy (@full_moons) {
    my $angle = ($doy * $day_angle_step) - 90;
    my $angle_rad = deg2rad($angle);
    my $x1 = $center_x + $lunation_radius * cos($angle_rad);
    my $y1 = $center_y + $lunation_radius * sin($angle_rad);
    my $x2 = $center_x + $inner_radius * cos($angle_rad);
    my $y2 = $center_y + $inner_radius * sin($angle_rad);
    print qq{<line x1="$x1" y1="$y1" x2="$x2" y2="$y2" stroke="$newmoon_color" stroke-width="$fat_stroke_width" stroke-dasharray="0.4,0.2" />\n};
}

# lunation labels on full moons
#wintermanoth is 2nd full moon if 1st too early
my $label_index = 0;
if (@full_moons && $full_moons[0] <= 6) {
    $label_index = -1;
}

for my $i (0 .. $#full_moons) {
    if ($i >= 12) { last; }
    my $doy = $full_moons[$i];
    my $name = $lunar_names[($label_index%12)];
    $label_index++;
    my $angle = (($doy - 0.5) * $day_angle_step) - 90;
    my $angle_rad = deg2rad($angle);
    my $text_radius = ($lunation_radius + $inner_radius * 0.98) / 2;
    my $text_x = $center_x + $text_radius * cos($angle_rad);
    my $text_y = $center_y + $text_radius * sin($angle_rad);
    my $rot = 360 * $doy / $days_in_year;
    #print qq{<text x="$text_x" y="$text_y" class="lunation-text" transform="rotate($rot $text_x $text_y)" font-size="1.5em">$name</text>\n};
    curvedtxt($center_x,$center_y, $text_radius, $angle_rad, $name, 2.8, 1.2, "lunation-text" );
}

sub curvedtxt {
my ($cx, $cy, $r, $phi, $string, $fontsize, $width_scale, $class) = @_;
if ($class){$class = "class=\"$class\" "}

my %relative_widths = ();

if ($class =~/lunation/){
# Font width calibration for: Uncial
# Generated with test font-size: 48
# Date: Thu Aug 28 13:21:11 2025
# Reference 'M' width: 56 pixels at size 48

%relative_widths = (
    'a' => 0.643,
    'b' => 0.500,
    'c' => 0.571,
    'd' => 0.750,
    'e' => 0.589,
    'f' => 0.589,
    'g' => 0.589,
    'h' => 0.643,
    'i' => 0.250,
    'j' => 0.429,
    'k' => 0.571,
    'l' => 0.339,
    'm' => 1.000,
    'n' => 0.643,
    'o' => 0.625,
    'p' => 0.589,
    'q' => 0.786,
    'r' => 0.643,
    's' => 0.482,
    't' => 0.500,
    'u' => 0.643,
    'v' => 0.625,
    'w' => 1.000,
    'x' => 0.696,
    'y' => 0.661,
    'z' => 0.643,
    'A' => 0.643,
    'B' => 0.500,
    'C' => 0.571,
    'D' => 0.750,
    'E' => 0.589,
    'F' => 0.589,
    'G' => 0.589,
    'H' => 0.643,
    'I' => 0.250,
    'J' => 0.429,
    'K' => 0.571,
    'L' => 0.339,
    'M' => 1.000,
    'N' => 0.643,
    'O' => 0.625,
    'P' => 0.589,
    'Q' => 0.786,
    'R' => 0.643,
    'S' => 0.482,
    'T' => 0.500,
    'U' => 0.643,
    'V' => 0.625,
    'W' => 1.000,
    'X' => 0.696,
    'Y' => 0.661,
    'Z' => 0.643,
    ' ' => 0.286,
    '.' => 0.268,
    ',' => 0.446,
    '-' => 0.304
);
}
else{
# Font width calibration for: Liberation
# Generated with test font-size: 48
# Date: Thu Aug 28 13:20:33 2025
# Reference 'M' width: 42 pixels at size 48

%relative_widths = (
    'a' => 0.667,
    'b' => 0.690,
    'c' => 0.548,
    'd' => 0.714,
    'e' => 0.595,
    'f' => 0.810,
    'g' => 0.690,
    'h' => 0.690,
    'i' => 0.333,
    'j' => 0.667,
    'k' => 0.619,
    'l' => 0.333,
    'm' => 1.024,
    'n' => 0.690,
    'o' => 0.667,
    'p' => 0.786,
    'q' => 0.690,
    'r' => 0.524,
    's' => 0.524,
    't' => 0.429,
    'u' => 0.690,
    'v' => 0.619,
    'w' => 0.905,
    'x' => 0.738,
    'y' => 0.905,
    'z' => 0.619,
    'A' => 0.857,
    'B' => 0.714,
    'C' => 0.738,
    'D' => 0.786,
    'E' => 0.667,
    'F' => 0.667,
    'G' => 0.810,
    'H' => 0.810,
    'I' => 0.548,
    'J' => 0.44, #manual fix
    'K' => 0.762,
    'L' => 0.571,
    'M' => 1.000,
    'N' => 0.833,
    'O' => 0.857,
    'P' => 0.667,
    'Q' => 0.857,
    'R' => 0.690,
    'S' => 0.619,
    'T' => 0.690,
    'U' => 0.810,
    'V' => 0.762,
    'W' => 1.095,
    'X' => 0.857,
    'Y' => 0.714,
    'Z' => 0.738,
    '0' => 0.643,
    '1' => 0.643,
    '2' => 0.643,
    '3' => 0.643,
    '4' => 0.643,
    '5' => 0.643,
    '6' => 0.643,
    '7' => 0.667,
    '8' => 0.643,
    '9' => 0.643,
    ' ' => 0.286,
    '.' => 0.310,
    ',' => 0.452,
    '-' => 0.381,
    'ä' => 0.667,
    'ö' => 0.667,
    'ü' => 0.690
);

}

my $default_width = 0.50;  # Fallback for unknown characters
my $pi = 3.141592653589793;

# Calculate total angular width of the string
my $total_arc_length = 0;
foreach my $char (split //, $string) {
    my $rel_width = $relative_widths{$char} || $default_width;
    $total_arc_length += $fontsize * $rel_width * $width_scale;
}
my $total_angle = $total_arc_length / $r;

# Start at phi minus half the total angle to center the text
my $current_angle = $phi - $total_angle / 2;
    
foreach my $char (split //, $string) {
    my $rel_width = $relative_widths{$char} || $default_width;
    my $arc_length = $fontsize * $rel_width * $width_scale;
    my $angle_step_rad = $arc_length / $r;

    $current_angle += $angle_step_rad / 2;
    my $pos_x = $cx + $r * cos($current_angle);
    my $pos_y = $cy + $r * sin($current_angle);

    my $rot_rad = atan2(sin($current_angle), cos($current_angle)) + $pi/2; 
    my $rot_deg = $rot_rad * (180 / $pi);

    if ($char ne ' ') {  # Skip printing for spaces, but still advance
        print qq{<text x="$pos_x" y="$pos_y" font-size="$fontsize" $class text-anchor="middle" transform="rotate($rot_deg $pos_x $pos_y)">$char</text>\n};
    }
    $current_angle += $angle_step_rad / 2;
    }
}


if ($holidays){
#https://www.stadt-zuerich.ch/de/bildung/volksschule/schulferien.html
#Sportferien: Wochen 7 und 8
#Frühlingsferien: Wochen 17 und 18 (Wochen 16 und 17, wenn Ostermontag in die 16. Woche fällt)
#Sommerferien: Wochen 29 bis 33
#Herbstferien: Wochen 41 und 42
    my $first_dt = DateTime->new(year => $year, month => 1, day => 1, time_zone => 'UTC');
    my $easter_dt = DateTime::Event::Easter->new();
    my $gregorian_easter = $easter_dt->following($first_dt);
    #print $easter_sunday->ymd;
    my $frfe=(($gregorian_easter->clone->add(days => 1)->week)==16?16:17);

    my @hday_weeks = (7,8,$frfe,$frfe+1,29,30,31,32,33,41,42);

    my $greg_easter_doy = $gregorian_easter->day_of_year();
    my @hd_other=(($greg_easter_doy-2), $greg_easter_doy+1, $greg_easter_doy+39, $greg_easter_doy+40, $greg_easter_doy+50);
 
    #Weihnachtsferien: von 1. Montag vor 27.12. [20.-26.]  -- bis 1. Freitag nach 30.12. [31.-6.]
    my $fri=0;
    unless($first_dt->day_of_week == 6){
        foreach my $d (1..6){
            if($fri == 0 && ($first_dt->clone->add(days => $d-1))->day_of_week <=5){@hd_other[$#hd_other+1]=$d;}
            if(($first_dt->clone->add(days => $d-1))->day_of_week == 5){$fri=1;}
            }
        }
    my $mon=0;
    foreach my $d ($days_in_year-11..$days_in_year){
        if(($first_dt->clone->add(days => $d-1))->day_of_week ==1){$mon=1;}
        if($mon==1){@hd_other[$#hd_other+1]=$d;}
    }
#    print "\n<!-- debug hd_other @hd_other -->\n";

    for my $doy (1 .. $days_in_year) {
        my $dt = $first_dt->clone->add(days => $doy - 1);
        my $dow=$dt->day_of_week;
        my ($week_year, $week) = $dt->week;
        if ($dow == 6 || $dow == 7) { #Weekends
            draw_arc($doy-1, $doy, $month_radius, "blue", 2, 0.3);
        }
        else {
            foreach my $hdw (@hday_weeks){ #school holidays
                if($week==$hdw){
                    draw_arc($doy-1, $doy, $month_radius+.5, "blue", 1, 0.3);
                    #print "\n<!-- debug holiday week $week doy $doy-->\n";
                }
            }
            #Ostern, Auffahrt, Pfingsten, Weihnachtsferien
            foreach my $d (@hd_other){
                if ($d == $doy){
                    draw_arc($doy-1, $doy, $month_radius+.5, "blue", 1, 0.3);
                }
            }
        }                    
    }
    #Zuercher Feiertage:
    #1. Mai
    my $may_doy = DateTime->new(year => $year, month => 5, day => 1)->day_of_year;
    print "\n<!-- debug 1. Mai $may_doy-->\n";    
    draw_arc($may_doy-1, $may_doy, $month_radius+.5, "blue", 1, 0.3);    
    #Knabenschiessen
    # 1 Sept = doy 244 (+is_leap_year)
    my $sept3 = DateTime->new(year => $year, month => 9, day => 3);
    my $kn_doy = $sept3->add(days => (8 - $sept3->day_of_week()) % 7 + 7)->day_of_year;
    print "\n<!-- debug Knabenschiessen $kn_doy-->\n";    
    draw_arc($kn_doy-.5, $kn_doy, $month_radius+.5, "blue", 1, 0.3);
    #Sechseläuten
    my $sl_doy = sechselaeuten($year);
    print "\n<!-- debug Sechselaeuten $sl_doy-->\n"; 
    draw_arc($sl_doy-.5, $sl_doy, $month_radius+.5, "blue", 1, 0.3);
}


#Das Datum des Sechseläutens wurde vom Stadtrat auf den dritten Montag des Monats April festgelegt. Fällt dieser Tag in die Karwoche, so wird das Sechseläuten am zweiten Montag abgehalten. Fällt der dritte Montag im April auf den Ostermontag, so wird das Fest auf den vierten Montag verschoben (Stadtratsbeschluss Nr. 1214 vom 13. Juni 1952).
#20. April 2026
#19. April 2027
#24. April 2028
#16. April 2029
#8. April 2030
sub sechselaeuten {
    my $y = shift;
    my $first_dt = DateTime->new(year => $y, month => 1, day => 1, time_zone => 'UTC');
    my $easter_dt = DateTime::Event::Easter->new();
    my $gregorian_easter = $easter_dt->following($first_dt);
    my $greg_easter_doy = $gregorian_easter->day_of_year();
    my $april_dow = DateTime->new(year => $y, month => 4, day => 1, time_zone => 'UTC')->day_of_week;
    my $doy =  DateTime->new(year => $y, month => 4, day => (15+($april_dow==1?0:(8- $april_dow))), time_zone => 'UTC')->day_of_year; # doy of 3rd monday in sept
    if ($doy == $greg_easter_doy-6){$doy-=7;} #Karwoche
    elsif($doy == $greg_easter_doy+1){$doy+=7;} #Ostermontag
    return $doy;
}


############################################
# Church feasts
#4 movable:
#Palm Sunday 	-- green
#Easter		-- white
#Ascension	-- white
#Pentecost	-- green

#9 immovable:
#Nativity of the Theotokos, 21 September	-- blue
#Exaltation of the Cross, 27 September		-- red
#Presentation of the Theotokos, 4 December 	-- blue
#Christmas,   7 January 			-- white
#Theophany,   19 January			-- white
#Candlemas, 15 February				-- white
#Annunciation,   7 April			-- blue
#Transfiguration, 19 August			-- white
#Dormition,  28 August				-- blue

    my $easter_dt = easter_gauss($year);
    my $easter_doy = (($easter_dt->epoch - $start_time)/86400 +0.5);

    my @feasts_doy = ($easter_doy-6.5,$easter_doy,$easter_doy+39.5,$easter_doy+49.5,264+$leap_adjust, 270+$leap_adjust, 338+$leap_adjust, 6.5, 19, 46, 97+$leap_adjust, 231+$leap_adjust, 240+$leap_adjust);
#    my @feast_labels=("Palmsonntag", "Ostern","Auffahrt","Pfingsten","Mariä Geburt","Kreuzerhöhung","Darstellung Mariens","Weihnachten","Theophanie","Darstellung","Verkündigung","Verklärung","Entschlafung");
    my @feast_colors=("limegreen","floralwhite","floralwhite","limegreen","royalblue","purple","royalblue","floralwhite","floralwhite","floralwhite","royalblue","floralwhite","royalblue");
    my @feast_labels=(
    	"Вхо́дъ гдⷭ҇ень въ і҆ерⷭ҇ли́мъ", 
    	"Свѣтлое Христово Воскресеніе", # Пасха Господня
    	"Вознесе́ние гдⷭ҇а бг҃а и҆ сп҃са на́шегѡ і҆и҃са хрⷭ҇та̀",
    	"Недѣ́лѧ ст҃ы́ѧ пентико́стїи",
    	"Ржⷭ҇тво̀ прест҃ы́ѧ бцⷣы и҆ прⷭ҇нод҃вы мр҃і́и", # влⷣчицы на́шеѧ
    	"Всемі́рное воздви́женїе чⷭ҇тна́гѡ и животворѧ́шагѡ крⷭ҇та̀",
    	"Вхо́дъ во хра́мъ прест҃ы́ѧ бцⷣы и҆ приснодв҃ы мр҃і́и", # влⷣчцы на́шеѧ 
    	"Є҆́же по пло́ти ржⷭ҇тво̀ гдⷭ҇а бг҃а и҆ сп҃са на́шегѡ і҆и҃са хрⷭ҇та̀",
    	"Кр҃ще́нїе гдⷭ҇а бг҃а и҆ сп҃са на́шегѡ і҆и҃са хрⷭ҇та̀",
    	"Срѣтение гдⷭ҇а бг҃а и҆ сп҃са на́шегѡ і҆и҃са хрⷭ҇та̀", 
    	"Благовѣ́щенїе Пресвятой Богородицы",
    	"Преображеніе гдⷭ҇а бг҃а и҆ сп҃са на́шегѡ і҆и҃са хрⷭ҇та̀",
    	"Ѹ҆спе́нїе прест҃ы́ѧ влⷣчцы на́шеѧ бцⷣы и҆ прⷭ҇нод҃вы мр҃і́и"
    	);


#    my @feasts_doy = ($easter_doy,$easter_doy+50, 7, 84+$leap_adjust,217+$leap_adjust,226+$leap_adjust);
#    my @feast_labels=("Све́тлое Христо́во Воскресе́ние","Недѣ́лѧ ст҃ы́ѧ пентико́стїи","Є҆́же по пло́ти ржⷭ҇тво̀ гдⷭ҇а бг҃а и҆ сп҃са на́шегѡ і҆и҃са хрⷭ҇та̀","Благовѣ́щенїе Пресвятой Богородицы","Преображе́ние Госпо́дне","Ѹ҆спе́нїе прест҃ы́ѧ влⷣчцы на́шеѧ бцⷣы и҆ прⷭ҇нод҃вы мр҃і́и");

if ($church_year){
    # Great Lent
    draw_arc(($feasts_doy[1]-48.75), ($feasts_doy[1]-0.5), $lunation_radius, $lent_color, 4, 0.5);
    #Apostles fast (easter+57 - 11.7.)
    draw_arc(($feasts_doy[1]+56.25), (191.75+$leap_adjust), $lunation_radius, $lent_color, 3, 0.4);
    #Dormition fast (14.8. - 27.8.) 
    draw_arc((224.75+$leap_adjust), (238.75+$leap_adjust), $lunation_radius, $lent_color, 3, 0.4);
    #Nativity fast (28.11. - 6.1.) 
    draw_arc(($days_in_year-34.25), 6, $lunation_radius, $lent_color, 3, 0.4);

    #fast-free: 7.-17.1.
    draw_arc(6, 16.75, $lunation_radius, $fastfree_color, 3, 0.4);
    #fast-free: preparatory
    draw_arc(($feasts_doy[1]-69.75), ($feasts_doy[1]-63.75), $lunation_radius, $fastfree_color, 3, 0.4);
    #fast-free: maslenitsa
    draw_arc(($feasts_doy[1]-55.75), ($feasts_doy[1]-48.75), $lunation_radius, $maslenitsa_color, 3, 0.4);
    #fast-free: Holy Week
    draw_arc(($feasts_doy[1]), ($feasts_doy[1]+6.25), $lunation_radius, $fastfree_color, 4, 0.5);
    #fast-free: post-pentecost
    draw_arc(($feasts_doy[1]+49.25), ($feasts_doy[1]+55.25), $lunation_radius, $fastfree_color, 3, 0.4);


#addtl. fast days
    my $first_dt = DateTime->new(year => $year, month => 1, day => 1, time_zone => 'UTC');
    for my $doy (1 .. $days_in_year) {
        unless(($doy > 6 && $doy < 18)  ||		#exclude fast-free
               ($doy > ($feasts_doy[1]-69) && $doy < ($feasts_doy[1]-64))  ||
               ($doy > ($feasts_doy[1]-55) && $doy < ($feasts_doy[1]-50))  ||
               ($doy > ($feasts_doy[1]) && $doy < ($feasts_doy[1]+8))  ||
               ($doy > ($feasts_doy[1]+49) && $doy < ($feasts_doy[1]+56))
        ){ 
            my $dt = $first_dt->clone->add(days => $doy - 1);
            my $dow=$dt->day_of_week;
            if ($dow == 3 || $dow == 5) { 		#Wed/Fri
                draw_arc($doy-1.25, $doy-.25, $lunation_radius, $lent_color, 3, 0.4);
            }
            elsif($doy==18 || $doy == $days_in_year-111 || $doy == $days_in_year-95)  { #18.1. (epiphany eve), 11.9. (john), 27.9. (exalt.cross)
                draw_arc($doy-1.25, $doy-.25, $lunation_radius, $lent_color, 3, 0.4);
            }
        }
    }    
}

sub draw_arc {
    my ($doy1, $doy2, $radius, $col, $w, $opac) = @_;
    my $a1 = ($doy1 * $day_angle_step) - 90;
    my $x1 = $center_x + $radius * cos(deg2rad($a1));
    my $y1 = $center_y + $radius * sin(deg2rad($a1));
    my $a2 = ($doy2 * $day_angle_step) - 90;
    my $x2 = $center_x + $radius * cos(deg2rad($a2));
    my $y2 = $center_y + $radius * sin(deg2rad($a2));
    print qq{<path d="M $x1 $y1 A $radius $radius 360 0 1 $x2 $y2" stroke="$col" stroke-width="$w" fill="none" opacity="$opac"/>};

}
#########################

# Calendar weeks ring and day markers
my $first_dt = DateTime->new(year => $year, month => 1, day => 1, time_zone => 'UTC');
for my $doy (1 .. $days_in_year) {
    my $dt = $first_dt->clone->add(days => $doy - 1);
    if ($dt->day_of_week == 1) {  # Monday
        my $angle = (($doy - 1) * $day_angle_step) - 90;
        my $angle_rad = deg2rad($angle);
        my $x1 = $center_x + $week_radius * cos($angle_rad);
        my $y1 = $center_y + $week_radius * sin($angle_rad);
        my $x2 = $center_x + $month_radius * cos($angle_rad);
        my $y2 = $center_y + $month_radius * sin($angle_rad);
        print qq{<line x1="$x1" y1="$y1" x2="$x2" y2="$y2" stroke="$stroke_color" stroke-width="$stroke_width" />\n};
    }
    if ($dt->day_of_week == 4 && $dt->week_year == $year) {  # Thursday and week of this year
        my $week = $dt->week_number;
        my $angle = (($doy - 0.5 - ($week==53? 1.5:0)) * $day_angle_step) - 90;
        my $angle_rad = deg2rad($angle);
        my $text_radius = ($week_radius + $month_radius) / 2;
        my $text_x = $center_x + $text_radius * cos($angle_rad);
        my $text_y = $center_y + $text_radius * sin($angle_rad);
        my $rot = 360 * $doy / $days_in_year;
        print qq{<text x="$text_x" y="$text_y" class="week-text" transform="rotate($rot $text_x $text_y)">$week</text>\n};
    }

    if ($dt->day_of_week == 7) {  # Sunday

        my $angle = (($doy - 0.5) * $day_angle_step) - 90;
        my $angle_rad = deg2rad($angle);
        my $text_radius = $lunation_radius * 0.95;
        my $text_x = $center_x + $text_radius * cos($angle_rad);
        my $text_y = $center_y + $text_radius * sin($angle_rad);
        my $day_rot = 360 * $doy / $days_in_year;
        my $day = $dt->day;
            
        print qq{<text x="$text_x" y="$text_y" class="sunday-text" transform="rotate($day_rot $text_x $text_y)">$day</text>\n};
        }
}

# Draw compass rose for equinoxes and solstices

print "<circle r=\"".($smallest_radius+1)."\" cx=\"$center_x\" cy=\"$center_y\" stroke=\"$stroke_color\" fill=\"none\" stroke-width=\"$stroke_width\" />\n";

my $wedge_angle=5;
foreach my $doy (@solar_quarters) {
    foreach my $lr (0..1){
        my $angle = ($doy * $day_angle_step) - 90;
        my $x1 = $center_x + $inner_radius * cos(deg2rad($angle));
        my $y1 = $center_y + $inner_radius * sin(deg2rad($angle));
        my $x2 = $center_x + $smallest_radius * cos(deg2rad($angle));
        my $y2 = $center_y + $smallest_radius * sin(deg2rad($angle));
        my $x3 = $center_x + $smallest_radius * cos(deg2rad($angle+$wedge_angle*($lr?1:-1)));
        my $y3 = $center_y + $smallest_radius * sin(deg2rad($angle+$wedge_angle*($lr?1:-1)));
	my $wedge_color=(($doy>300 || ($lr && $doy>200) || (!$lr && $doy<100))? $night_color:$day_color);

        print qq{<polygon points="$x1,$y1 $x2,$y2 $x3,$y3" style="fill:$wedge_color;stroke:$stroke_color;stroke-width:$stroke_width" />\n};
    }
}

#Draw compass rose inner circle
my $angle = ($solar_quarters[0] * $day_angle_step) - 90;
my $angle_rad = deg2rad($angle);
my $x1 = $center_x + $smallest_radius * cos($angle_rad);
my $y1 = $center_y + $smallest_radius * sin($angle_rad);
$angle = ($solar_quarters[2] * $day_angle_step) - 90;
$angle_rad = deg2rad($angle);
my $x2 = $center_x + $smallest_radius * cos($angle_rad);
my $y2 = $center_y + $smallest_radius * sin($angle_rad);

print qq{<path d="M $x1 $y1 A $smallest_radius $smallest_radius 360 0 0 $x2 $y2"  stroke="$night_color" stroke-width="2" fill="none"/>};
print qq{<path d="M $x1 $y1 A $smallest_radius $smallest_radius 360 1 1 $x2 $y2"  stroke="$day_color" stroke-width="2" fill="none"/>};

print "<circle r=\"".($smallest_radius-1)."\" cx=\"$center_x\" cy=\"$center_y\" stroke=\"$stroke_color\" fill=\"none\" stroke-width=\"$stroke_width\" />\n";


# Draw lines for equinoxes and solstices
my $compass_width = $stroke_width * 1.5;
foreach my $doy (@solar_quarters) {
    my $angle = ($doy * $day_angle_step) - 90;
    my $angle_rad = deg2rad($angle);
    my $x1 = $center_x + $smallest_radius * cos($angle_rad);
    my $y1 = $center_y + $smallest_radius * sin($angle_rad);
    my $x2 = $center_x + $inner_radius * cos($angle_rad);
    my $y2 = $center_y + $inner_radius * sin($angle_rad);
    print qq{<line x1="$x1" y1="$y1" x2="$x2" y2="$y2" stroke="$stroke_color" stroke-width="$compass_width" />\n};
}

# Add year in the center
print qq{<text x="$center_x" y="$center_y" class="year-text">$year</text>\n};

# Add latitude info below year
#my $lat_text_y = $center_y + 8;
#print qq{<text x="$center_x" y="$lat_text_y" class="info-text">Latitude: ${latitude}°N</text>\n};


##############################
if ($church_year){
    # Add feast labels
    my $easter_size = 4.5;
    my $feast_size = 3.0;
    my $minor_size = 2.0;
    foreach my $feast (0..$#feasts_doy){
      my $label_radius = $smallest_radius * 1.33;
      my $feast_label = $feast_labels[$feast];
      if($feast==10 && (abs($feasts_doy[10] - $feasts_doy[0]) < 4 || abs($feasts_doy[10] - $feasts_doy[1]) < 4)){ #Annunciation too close to other feast for labelling
          $label_radius = $smallest_radius * 2.6;
          $feast_label =~s/ .+//;
      }
      
      my $angle=($feasts_doy[$feast]-0.5)/$days_in_year*360 -90;

      my $text_x = $center_x + $label_radius * cos(deg2rad($angle));
      my $text_y = $center_y + $label_radius * sin(deg2rad($angle));
      my $label_rot = $angle;
    
      print "<line x1=\"".sprintf("%.4f",($center_x + $smallest_radius * cos(deg2rad($angle))))."\" y1=\"".sprintf("%.4f",($center_y + $smallest_radius * sin(deg2rad($angle))))."\" x2=\"".sprintf("%.4f",($center_x + $inner_radius * cos(deg2rad($angle))))."\" y2=\"".sprintf("%.4f",($center_y + $inner_radius * sin(deg2rad($angle))))."\" stroke=\"".($feast==1? $easter_color : $red_color)."\" stroke-width=\"".($feast==1? ($stroke_width*2) : $stroke_width)."\" opacity=\"0.6\" />\n";
      
      if($feast == 1){
            print "<!-- debug Ostern: ".$feasts_doy[$feast]." -->\n";
            print qq{<text x="$text_x" y="$text_y" class="easter-text" transform="rotate($label_rot $text_x $text_y)">$feast_label</text>\n};
            draw_cross($easter_size, $lunation_radius, $angle, "floralwhite", "crimson");
            
      }
      else{
            print qq{<text x="$text_x" y="$text_y" class="feast-text" transform="rotate($label_rot $text_x $text_y)">$feast_label</text>\n};
            draw_cross($feast_size, $lunation_radius, $angle, $feast_colors[$feast], ($feast_colors[$feast]=~/white/?"gold":"floralwhite"));

      }

    }
#minor fixed feasts
#14.1. Beschn, 7.7. John, 12.7. Apostles, 11.9. John, 14.10. Pokrov
    draw_cross($minor_size, $lunation_radius, (13.5/$days_in_year*360-90), "white", "gold");
    draw_cross($minor_size, $lunation_radius, ((187.5+$leap_adjust)/$days_in_year*360-90), $red_color, "white");
    draw_cross($minor_size, $lunation_radius, ((192.5+$leap_adjust)/$days_in_year*360-90), $red_color, "white");
    draw_cross($minor_size, $lunation_radius, ((253.5+$leap_adjust)/$days_in_year*360-90), $red_color, "white");
    draw_cross($minor_size, $lunation_radius, ((286.5+$leap_adjust)/$days_in_year*360-90), "royalblue", "white");
#Tiro: 2.3., $easter_doy-43
    draw_cross($minor_size, $lunation_radius, ((60.5+$leap_adjust)/$days_in_year*360-90), $red_color, "white");
    draw_cross($minor_size, $lunation_radius, (($feasts_doy[1]-43)/$days_in_year*360-90), "purple", "white");
}

sub easter_gauss {
    my ($yr) = @_;

    my $g = $yr % 19;
    my $i = (19 * $g + 15) % 30;
    my $j = ($yr + int($yr / 4) + $i) % 7;
    my $e = 10;
    if ($yr > 1600) {
        my $cent = int($yr / 100);
        $e += $cent - 16 - int(($cent - 16) / 4);
    }
    my $p = $i - $j + $e;
    my $m = 3 + int(($p + 26) / 30);
    my $d = 1 + (($p + 27 + int(($p + 6) / 40)) % 31);
#    print "<!-- easter: $d $m $yr-->\n";
    my $easter_dt = DateTime->new(year => $yr, month => $m, day => $d, hour => 0, minute => 0, second => 0, time_zone => 'UTC');
    return $easter_dt;
}
sub draw_cross {
    my ($size, $radius, $angle, $fill_color, $bg_color) = @_;
    my $x = $center_x + $radius * cos(deg2rad($angle));
    my $y = $center_y + $radius * sin(deg2rad($angle));
    my $w = $size;
    my $h = $size/5;
    print "<circle cx=\"".($x)."\" cy=\"".($y)."\" r=\"".($w*0.51)."\"  fill=\"$bg_color\" />\n"; # opacity=\"0.75\"
    print "<rect x=\"".($x-$w/2)."\" y=\"".($y-$h/2)."\" width=\"$w\" height=\"$h\" fill=\"$fill_color\" transform=\"rotate($angle $x $y)\"/>\n";
    print "<rect x=\"".($x-$h/2)."\" y=\"".($y-$w/2)."\" width=\"$h\" height=\"$w\" fill=\"$fill_color\" transform=\"rotate($angle $x $y)\"/>\n";

}


#Draw 1 January divider
    my $top_y = $center_y-$outer_radius;
    my $inner_y =$center_y-(0.75*$smallest_radius);
    my $fat_width = $stroke_width * 3.5;
    print qq{<line x1="$center_x" y1="$inner_y" x2="$center_x" y2="$top_y" stroke="black" stroke-width="$fat_width" opacity="0.75" />\n};

# Add moon phase markers
my $marker_radius =  $inner_radius * 0.97;  
my $marker_size = 2; 

foreach my $doy (@new_moons) {
    my $angle = ($doy * $day_angle_step) - 90;  # Middle of the day segment
    my $angle_rad = deg2rad($angle);
    my $cx = $center_x + $marker_radius * cos($angle_rad);
    my $cy = $center_y + $marker_radius * sin($angle_rad);
    print qq{<circle cx="$cx" cy="$cy" r="$marker_size" fill="$newmoon_color" stroke="$stroke_color" stroke-width="$stroke_width" />\n};
}

foreach my $doy (@full_moons) {
    my $angle = ($doy  * $day_angle_step) - 90;  # Middle of the day segment
    my $angle_rad = deg2rad($angle);
    my $cx = $center_x + $marker_radius * cos($angle_rad);
    my $cy = $center_y + $marker_radius * sin($angle_rad);
    print qq{<circle cx="$cx" cy="$cy" r="$marker_size" fill="$fullmoon_color" stroke="$stroke_color" stroke-width="$stroke_width" />\n};
}

# End SVG
print "</svg>\n";
