#!/usr/bin/perl


use lib qw(/usr/lib/libDrakX);
use POSIX;
use common;
# i18n : IMPORTANT to get correct namespace (drakconf instead of libDrakX)
BEGIN { unshift @::textdomains, 'drakconf' }
use mygtk3 qw(gtknew);
use ugtk3 qw(:all);
use interactive;
use standalone;
use timezone;

my $in = interactive->vnew('su');
my $cairo;
my ($midx, $midy, $radius);
my ($dRadians_hour, $dRadians_min, $dRadians_sec);
my $Radian;
my $timer;
my $first = 1;
my $its_reset = 0;

$ugtk3::wm_icon = "/usr/share/mcc/themes/default/time-mdk.png";

my $my_win = ugtk3->new(N("Date, Clock & Time Zone Settings"));

$my_win->{window}->signal_connect(delete_event => sub { ugtk3->exit(0) });

my $calendar = gtknew('Calendar');
$calendar->signal_connect($_ => \&cal_changed) foreach 'month-changed', 'day-selected', 'day-selected-double-click', 'prev-month', 'next-month', 'prev-year', 'next-year';

my $timezone = timezone::read();

my $label_timezone = gtknew('Label', text => $timezone->{timezone}  || N("not defined"));

my $button_time = gtknew('Button', text => N("Change Time Zone"));
my @timezones = eval { timezone::getTimeZones() };
my $err = $@;
$button_time->signal_connect(clicked => sub {
    local $::isEmbedded = 0; # to prevent sub window embedding
    if (my $tz = $in->ask_from_treelist(N("Timezone - DrakClock"), N("Which is your timezone?"), '/', \@timezones, $timezone->{timezone})) {
	$timezone->{timezone} = $tz;
	$timezone->{UTC} = $in->ask_yesorno(N("UTC - DrakClock"), N("Is your hardware clock set to UTC?"), $timezone->{UTC});
	timezone::write($timezone);
	$label_timezone->set_text($timezone->{timezone});
    }
			     });
if (!@timezones) {
    warn "Failed to retrieve timezone list: $err\n";
    $button_time->set_sensitive(0);
}

my $drawing_area;

my $adjh = Gtk3::Adjustment->new(0.0, 0.0, 23.0, 1.0, 5.0, 0.0);
my $adjm = Gtk3::Adjustment->new(0.0, 0.0, 59.0, 1.0, 5.0, 0.0);
my $adjs = Gtk3::Adjustment->new(0.0, 0.0, 59.0, 1.0, 5.0, 0.0);

my ($button_reset, $check_ntp, $hb_ntp, $combo_ntp_pool, $ntp);
my $mode = is_ntp_daemon_running();

my (undef, undef, $h_old, $old_day, $old_month, $old_year) = localtime(time());

my @image_size = (200, 200);

$my_win->{window}->add(
    gtknew('VBox', border_width => $::isEmbedded ? 0 : 5, children => [
	    1, gtknew('HBox', children => [
		    1, gtknew('VBox', children => [
			    0, $calendar,
			    1, gtknew('Frame', text => N("Network Time Protocol"), shadow_type => 'etched_in', child => 
				gtknew('VBox', border_width => 5, children => [
					0, gtknew('Label', text => N("Your computer can synchronize its clock\n with a remote time server using NTP")),
					0, gtksignal_connect(gtkset_active($check_ntp = gtknew('CheckButton', text => N("Enable Network Time Protocol")), $mode),  clicked => sub { 
					    $mode = !$mode; 
					    $hb_ntp->set_sensitive($mode);
					    if ($mode == 1 && !$in->do_pkgs->is_installed('ntpsec') && !$in->do_pkgs->is_installed('chrony')) {
						install_ntp();
                                                show_current_pool();
					    }
							     }),
					0, $hb_ntp = gtknew('HBox', border_width => 5, children => [
								0, gtknew('Label', text => N("Server:")),
								1, $combo_ntp_pool = Gtk3::ComboBoxText->new
							    ])
				    ]))
			]),
		    0, gtknew('VBox', children => [
			    0, my $clock = gtknew('HBox', children => [
					  0, $drawing_area = gtkset_size_request(Gtk3::DrawingArea->new, @image_size),
				      ]),
			    0, my $time_box = gtknew('HBox', homogenous => 1, children => [
							 0, my $spinner_h = Gtk3::SpinButton->new($adjh, 0, 0),
							 0, my $spinner_m = Gtk3::SpinButton->new($adjm, 0, 0),
							 0, my $spinner_s = Gtk3::SpinButton->new($adjs, 0, 0),
						     ]),
			    1, gtknew('Frame', text => N("Timezone"), shadow_type => 'etched_in', child => 
				      gtknew('VBox', border_width => 5, children_tight => [
						 $label_timezone,
						 $button_time ])),
			]),
		]),
	    0, create_okcancel(
		my $w =
		{
		    cancel_clicked => sub { ugtk3->exit(0) },
		    ok_clicked => sub {
			my $need_date = 1;
                        my $ntp_service = timezone::ntpd() . '.service';
			any::disable_x_screensaver();
			if ($check_ntp->get_active) {
			    my $chosen_pool = $combo_ntp_pool->entry->get_text;
			    $chosen_pool =~ s/^[^:]+: (.+)/$1/;
			    if (!$chosen_pool) {
				err_dialog(N("Error"), N("Please enter a valid NTP server pool."));
				return;
			    }
			    timezone::set_ntp_pool($chosen_pool);
			    system('systemctl', 'enable', $ntp_service);
			    system('systemctl', 'stop', $ntp_service);
			    #verify that we have a valid hostname (thx sam)
			    $chosen_pool =~ s/[^-a-zA-Z0-9.]//g;
			    my $sync_ok;
			    if ($ntp_service eq 'chronyd.service') {
				system('systemctl', 'start', $ntp_service);
				# Wait up to 30s for sync
				$sync_ok = !system('chronyc', 'waitsync', '30', '0.1');
				system('systemctl', 'stop', $ntp_service) if !$sync_ok;
			    } else {
				$sync_ok = !system('/usr/sbin/ntpd', '-gqc', '/dev/null', $chosen_pool);
				system('systemctl', 'start', $ntp_service) if $sync_ok;
			    }
			    if ($sync_ok) {
				update_time(); #- get the new time before updating the hwclock
				$need_date = 0;
			    } else {
				$in->ask_from_no_check({
				    title => N("Error"),
				    messages => N("Could not synchronize with %s.", $chosen_pool),
				    ok => N("Quit"),
				    cancel => N("Retry"),
						       }, []) or return;
			    }
			} else {
			    if (is_ntp_daemon_running()) {
				system('systemctl', 'stop', $ntp_service);
				system('systemctl', 'disable', $ntp_service);
			    }
			}
			if ($need_date) {
			    my ($year, $month, $day) = $calendar->get_date;
			    $month++;
			    my ($hour, $min, $sec) = ($adjh->get_value, $adjm->get_value, $adjs->get_value);
			    system("date " .
				   join('', map { print_it0($_) } ($month, $day, $hour, $min, $year)) . '.' . print_it0($sec));
			}
			-e '/sbin/hwclock' and system('/sbin/hwclock', '--systohc');
			any::enable_x_screensaver();
			system(qw(dcop kicker Panel restart)) if $ENV{DESKTOP} eq 'kde';
			ugtk3->exit(0);
		    },
		},
		undef, undef, '',
		[ N("Reset"), sub {
		    $its_reset = 1;
		    $timer = Glib::Timeout->add(120, \&update_time);
		    Repaint(1);
		    $button_reset->set_sensitive(0);
		    $its_reset = 0;
		  } ]
	    ),
	])
    );
$button_reset = $w->{buttons}{N("Reset")};

$time_box->set_direction('ltr');

my $servers = get_servers();
$combo_ntp_pool->set_popdown_strings(@$servers);
show_current_pool();
if (-e timezone::ntp_conf() && is_ntp_daemon_running()) {
    ntp_widget_state(1);
} else {
    ntp_widget_state(0);
}

sub show_current_pool() {
    $ntp = timezone::ntp_pool() || 'pool.ntp.org';
    my $fullntp = $ntp;
    my $short_ntp = $ntp;
    #- strip digits from \d+.foo.pool.ntp.org
    $short_ntp =~ s/^\d+\.//;
    foreach (@$servers) {
	/^[^:]+: \Q$short_ntp\E$/ and $fullntp = $_, last;
    }
    $combo_ntp_pool->entry->set_text($fullntp);
}

my $pressed;
$drawing_area->set_events(${ Gtk3::Gdk::EventMask->new([ 'button_press_mask', 'button_release_mask', "pointer_motion_mask" ]) });
$drawing_area->signal_connect(draw => \&draw);

$drawing_area->signal_connect(button_press_event   => sub { $pressed = 1 });
$drawing_area->signal_connect(button_release_event => sub { $first = 1; $pressed = 0 });
$drawing_area->signal_connect(motion_notify_event  => \&motion_event);

$spinner_h->set_wrap(1);
$spinner_h->signal_connect(activate => \&spinned);
$spinner_h->signal_connect(button_release_event => \&spinned);
$spinner_h->signal_connect(scroll_event => \&spinned);
$spinner_h->signal_connect(changed => \&changed);

$spinner_m->set_wrap(1);
$spinner_m->signal_connect(activate => \&spinned);
$spinner_m->signal_connect(scroll_event => \&spinned);
$spinner_m->signal_connect(button_release_event => \&spinned);

$spinner_s->set_wrap(1);
$spinner_s->signal_connect(activate => \&spinned);
$spinner_s->signal_connect(scroll_event => \&spinned);
$spinner_s->signal_connect(button_release_event => \&spinned);

gtkflush();

my $is24 = $h_old > 12;
$old_year += 1900;
$calendar->select_month($old_month, $old_year);
$calendar->select_day($old_day);
$button_reset->set_sensitive(0);
$timer = Glib::Timeout->add(120, \&update_time);

$drawing_area->show;
$my_win->{window}->show_all;
$my_win->main;
ugtk3->exit(0);

sub ntp_widget_state {
    my ($state) = @_;
    $check_ntp->set_active($state);  
    $hb_ntp->set_sensitive($state);
    $mode = $state;
}
sub install_ntp() {
    $my_win->{window}->set_sensitive(0);
    $in->do_pkgs->ensure_is_installed('chrony') or ntp_widget_state(0);
    $my_win->{window}->set_sensitive(1);
}
sub is_ntp_daemon_running() {
    my $ntp_service = timezone::ntpd() . '.service';
    !system("systemctl is-active $ntp_service >/dev/null");
}
sub get_servers() {
    my $servs = timezone::ntp_servers();
    [ map { "$servs->{$_}: $_" } sort { $servs->{$a} cmp $servs->{$b} || $a cmp $b } keys %$servs ];
}
sub update_time() {
    my (undef, undef, undef, $mday, $mon, $year) = localtime(time());
    return 1 if !$cairo;
    $year += 1900;
    my $old_its_reset = $its_reset;
    $its_reset = 1;
    $calendar->select_day($mday);
    $calendar->select_month($mon, $year);
    $its_reset = $old_its_reset;
    Repaint(1);
}

sub cal_changed() {
    if (!$its_reset && $timer) {
	Glib::Source->remove($timer);
	undef $timer;
    }
    $button_reset->set_sensitive(1);
}

sub changed() {
  my $val   = $adjh->get_value;
  my $limit = ($is24 ? 18 : 6);
  if (($limit > $val  &&  $h_old > $limit  &&  $h_old < ($is24 ? 24 : 12)) ||
      ($limit < $val  &&  $h_old < $limit  &&  $val-$h_old != 12)) {
    $is24 = !$is24;
  }
  $h_old = $val;
}

sub spinned() {
    Glib::Source->remove($timer) if $timer;
    undef $timer;
    $button_reset->set_sensitive(1);
    time_to_rad($adjs->get_value, $adjm->get_value, $adjh->get_value);
    Repaint();
    0;
}

sub motion_event {
    my ($widget, $event) = @_;
    $pressed or return;
    if ($first) {
	Glib::Source->remove($timer) if $timer;
	undef $timer;
	$Radian = determine_radian($event->x, $event->y);
	$button_reset->set_sensitive(1);
    }

    $$Radian = -atan2($event->x - $midx, $event->y - $midy) + $PI;

    Repaint();
    rad_to_time();
    $first = 0;
}

sub determine_radian {
    my ($x, $y) = @_;

    my $res;
    my $r;
    foreach (\$dRadians_hour, \$dRadians_min, \$dRadians_sec) {
	my $d = sqrt(($x - ($midx + 7/10 * $radius * sin($$_)))**2 + ($y - ($midy - 7/10 * $radius * cos($$_)))**2);
	$res or $res = $d, $r = $_;
	$d < $res and $res = $d, $r = $_;
    }
    $r;
}

sub draw {
    my ($widget, $event) = @_;
    $cairo = $event;
    Repaint(undef, 1);
    0;
}

sub rad_to_time() {
    $adjh->set_value(POSIX::floor($dRadians_hour * 6 / $PI) + ($is24 ? 12 : 0));
    $adjm->set_value(POSIX::floor($dRadians_min*30/$PI));
    $adjs->set_value(POSIX::floor($dRadians_sec*30/$PI));
}

sub time_to_rad {
    my ($sec, $min, $hour) = @_;
    $dRadians_hour = $hour % 12 * $PI / 6;
    $dRadians_min = $min * $PI / 30;
    $dRadians_sec = $sec * $PI / 30;
    $adjh->set_value($hour);
    $adjm->set_value($min);
    $adjs->set_value($sec);
}

sub Repaint {
    my ($o_update_time, $skip_redraw) = @_;
    my ($sec, $min, $hour) = localtime(time());
    time_to_rad($sec, $min, $hour) if $o_update_time;
    ($midx, $midy) = ($drawing_area->get_allocated_width/2, $drawing_area->get_allocated_height/2);
    $radius = ($midx < $midy ? $midx : $midy) - 10;
    $drawing_area->set_margin_start($clock->get_allocated_width/2 - $midx);

    $cairo->set_antialias('default');
    DrawTicks();
    DrawPoints();
    DrawHour();
    DrawMin();
    DrawSec();
    # Only redraw the clock if there are changes.
    # This highly decreases the CPU usage.
    $drawing_area->queue_draw unless $skip_redraw;
    1;
}

sub DrawSec() {
    my $dRadians = $dRadians_sec;
    $cairo->set_source_rgb(0.9, 0, 0);
    $cairo->move_to($midx, $midy);
    $cairo->line_to($midx + 85/100 * $radius * sin($dRadians), $midy - 85/100 * $radius * cos($dRadians));
    $cairo->stroke;
}

sub DrawMin() {
    my $dRadians = $dRadians_min;
    $cairo->set_source_rgb(0.2, 0.2, 0.2);
    $cairo->move_to($midx - 3/100 * $radius * sin($dRadians),       $midy + 3/100 * $radius * cos($dRadians));
    $cairo->line_to($midx - 3/100 * $radius * sin($dRadians+$PI/2), $midy + 3/100 * $radius * cos($dRadians+$PI/2));
    $cairo->line_to($midx + 7/10  * $radius * sin($dRadians-0.03),  $midy - 7/10  * $radius * cos($dRadians-0.03));
    $cairo->line_to($midx + 8/10  * $radius * sin($dRadians),       $midy - 8/10  * $radius * cos($dRadians));
    $cairo->line_to($midx + 7/10  * $radius * sin($dRadians+0.03),  $midy - 7/10  * $radius * cos($dRadians+0.03));
    $cairo->line_to($midx + 3/100 * $radius * sin($dRadians+$PI/2), $midy - 3/100 * $radius * cos($dRadians+$PI/2));
    $cairo->fill;
}

sub DrawHour() {
    my $dRadians = $dRadians_hour + $dRadians_min / 12;
    $cairo->set_source_rgb(0.2, 0.2, 0.2);
    $cairo->move_to($midx - 4/100 * $radius * sin($dRadians),       $midy + 4/100 * $radius * cos($dRadians));
    $cairo->line_to($midx - 4/100 * $radius * sin($dRadians+$PI/2), $midy + 4/100 * $radius * cos($dRadians+$PI/2));
    $cairo->line_to($midx + 5/10  * $radius * sin($dRadians-0.05),  $midy - 5/10  * $radius * cos($dRadians-0.05));
    $cairo->line_to($midx + 6/10  * $radius * sin($dRadians),       $midy - 6/10  * $radius * cos($dRadians));
    $cairo->line_to($midx + 5/10  * $radius * sin($dRadians+0.05),  $midy - 5/10  * $radius * cos($dRadians+0.05));
    $cairo->line_to($midx + 4/100 * $radius * sin($dRadians+$PI/2), $midy - 4/100 * $radius * cos($dRadians+$PI/2));
    $cairo->fill;
}

sub DrawTicks() {
    $cairo->set_source_rgb(1, 1, 1);
    $cairo->new_sub_path;
    $cairo->arc($midx, $midy, $radius, 0, 2 * $PI);
    $cairo->fill_preserve;
    $cairo->set_source_rgb(0, 0, 0);
    $cairo->stroke;

    foreach my $nHour (1..12) {
        my $dRadians = $nHour * $PI / 6.0;
        $cairo->move_to($midx + 9/10 * $radius * sin($dRadians),
                        $midy - 9/10 * $radius * cos($dRadians));
        $cairo->line_to($midx + 1    * $radius * sin($dRadians),
                        $midy - 1    * $radius * cos($dRadians));
    }
    $cairo->stroke;
}

sub DrawPoints() {
    $cairo->set_source_rgb(0, 0, 0);
    foreach my $nMin (1..60) {
        my $dRadians = $nMin * $PI / 30;
        $cairo->move_to($midx + 95/100 * $radius * sin($dRadians), $midy - 95/100 * $radius * cos($dRadians));
        $cairo->line_to($midx + 90/100 * $radius * sin($dRadians), $midy - 90/100 * $radius * cos($dRadians));
    }
    $cairo->stroke;
}


sub print_it0 { sprintf("%02d", $_[0]) }
