#!/usr/bin/perl

use strict;
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 $pixmap;
my $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 $ntpd = $timezone::ntp . 'd';
my $ntpfile = '/etc/' . $timezone::ntp . '.conf';

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("GMT - DrakClock"), N("Is your hardware clock set to GMT?"), $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_ntpserver, $ntp);
my $mode = 0;

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('ntp') && !$in->do_pkgs->is_installed('chrony')) {
						install_ntp();
					    }
							     }),
					0, $hb_ntp = gtknew('HBox', border_width => 5, children => [
								0, gtknew('Label', text => N("Server:")),
								1, $combo_ntpserver = Gtk3::ComboBoxText->new
							    ])
				    ]))
			]),
		    0, gtknew('VBox', children => [
			    0, 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;
			any::disable_x_screensaver();
			if ($check_ntp->get_active) {
			    my $choosed_serv = $combo_ntpserver->entry->get_text;
			    $choosed_serv =~ s/^[^:]+: (.+)/$1/;
			    if (!$choosed_serv) {
				err_dialog(N("Error"), N("Please enter a valid NTP server address."));
				return;
			    }
			    timezone::set_ntp_server($choosed_serv);
			    system('systemctl', 'enable', "$ntpd.service");
			    system('systemctl', 'stop', "$ntpd.service");
			    #verify that we have a valid hostname (thx sam)
			    $choosed_serv =~ s/[^-a-zA-Z0-9.]//g;
			    my $sync_ok;
			    if ($ntpd eq "chronyd") {
				system('systemctl', 'start', "$ntpd.service");
				# Wait up to 30s for sync
				$sync_ok = !system('chronyc', 'waitsync', '30', '0.1');
				system('systemctl', 'stop', "$ntpd.service") if !$sync_ok;
			    } else {
				$sync_ok = !system('/usr/sbin/ntpdate', $choosed_serv);
				system('systemctl', 'start', "$ntpd.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.", $choosed_serv),
				    ok => N("Quit"),
				    cancel => N("Retry"),
						       }, []) or return;
			    }
			} else {
			    if (is_ntp_daemon_running()) {
				system('systemctl', 'stop', "$ntpd.service");
				system('systemctl', 'disable', "$ntpd.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($drawing_area, 1);
		    $button_reset->set_sensitive(0);
		    $its_reset = 0;
		  } ]
	    ),
	])
    );
$button_reset = $w->{buttons}{N("Reset")};

$time_box->set_direction('ltr');

my $servers = get_server();
$combo_ntpserver->set_popdown_strings(@$servers);
if (-e $ntpfile && is_ntp_daemon_running()) {
    $ntp = timezone::ntp_server();
    $ntp and ntp_widget_state(1);
    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_ntpserver->entry->set_text($fullntp);
} else { ntp_widget_state(0) }
  
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(realize => sub {
                                  my $window = $drawing_area->get_window;
                                  return; #FIXME
                                  $pixmap = Gtk3::Gdk::Pixmap->new($window, @image_size, $window->get_depth);
                              });

$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 ($midx, $midy); # FIXME = ($drawing_area->get_allocation->width/2, $drawing_area->get_allocation->height/2);
$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() {
    !system("systemctl is-active $ntpd.service >/dev/null");
}
sub get_server() {
    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());
    $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($drawing_area, 1);
}

sub cal_changed() {
    if (!$its_reset and $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);
    undef $timer;
    $button_reset->set_sensitive(1);
    time_to_rad($adjs->get_value, $adjm->get_value, $adjh->get_value);
    Repaint($drawing_area);
    0;
}

sub motion_event {
    my ($widget, $event) = @_;
    $pressed or return;
    if ($first) {
	Glib::Source->remove($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($widget);
    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) = @_;
    return; #FIXME
    my ($x, $y, $width, $height) = $event->area->get_values;
    $widget->get_window->draw_drawable($widget->get_style->fg_gc('normal'), $pixmap, $x, $y, $x, $y, $width, $height);
    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 ($drawing_area, $o_update_time) = @_;
    my ($sec, $min, $hour) = localtime(time());
    time_to_rad($sec, $min, $hour) if $o_update_time;
    return; #FIXME
    my ($width, $height) = ($drawing_area->get_allocation->width, $drawing_area->get_allocation->height);
    my $dRadians_hour_real = $dRadians_hour + $dRadians_min / 12;
    my $dRadians_min_real  = POSIX::floor($dRadians_min / $PI * 30) * $PI / 30;
    my $dRadians_sec_real  = $dRadians_sec;
    my $gc = $drawing_area->get_style->white_gc;
    # fix race on ugtk3->exit that causes a crash (#33894)
    return 0  if !$gc;
    $pixmap->draw_rectangle($drawing_area->get_style->white_gc, 1, 0, 0, $width, $height);
    my ($midx, $midy) = ($width / 2, $height / 2);
    $radius = ($midx < $midy ? $midx : $midy) - 10;

    my $gray_gc  = $drawing_area->get_style->bg_gc('normal');
    my $black_gc = $drawing_area->get_style->black_gc;
    foreach ([ $gray_gc, 5 ], [ $black_gc, 0 ]) {
      &DrawTickAt($pixmap,  $_->[0], $midx, $midy, $_->[1]);
      &DrawHour($pixmap, $_->[0], $midx, $midy, $dRadians_hour_real, $_->[1]);
      &DrawMin($pixmap,  $_->[0], $midx, $midy, $dRadians_min_real,  $_->[1]);
      &DrawSec($pixmap,  $_->[0], $midx, $midy, $dRadians_sec_real,  $_->[1]);
    }
    &DrawPointAt($pixmap, $black_gc, $_, $midx, $midy) foreach (1..60);
    $drawing_area->queue_draw;
    1;
}

sub DrawSec {
    my ($pixmap, $gc, $midx, $midy, $dRadians, $dec) = @_;
    $pixmap->draw_line($gc,
		       $midx+$dec, $midy+$dec,
		       $midx+$dec + (8/10 * $radius * sin($dRadians)),
		       $midy+$dec - (8/10 * $radius * cos($dRadians)));
}

sub DrawMin {
    my ($pixmap, $gc, $midx, $midy, $dRadians, $dec) = @_;
    $pixmap->draw_polygon($gc, 1, $midx+$dec - 3/100 * $radius * sin($dRadians),       $midy+$dec + 3/100 * $radius * cos($dRadians),
				    $midx+$dec - 3/100 * $radius * sin($dRadians+$PI/2), $midy+$dec + 3/100 * $radius * cos($dRadians+$PI/2),
				    $midx+$dec + 8/10  * $radius * sin($dRadians),       $midy+$dec - 8/10  * $radius * cos($dRadians),
				    $midx+$dec + 3/100 * $radius * sin($dRadians+$PI/2), $midy+$dec - 3/100 * $radius * cos($dRadians+$PI/2)
				    );
}

sub DrawHour {
    my ($pixmap, $gc, $midx, $midy, $dRadians, $dec) = @_;
    $pixmap->draw_polygon($gc, 1, $midx+$dec - 5/100 * $radius * sin($dRadians),       $midy+$dec + 5/100 * $radius * cos($dRadians),
				    $midx+$dec - 5/100 * $radius * sin($dRadians+$PI/2), $midy+$dec + 5/100 * $radius * cos($dRadians+$PI/2),
				    $midx+$dec + 6/10  * $radius * sin($dRadians),       $midy+$dec - 6/10  * $radius * cos($dRadians),
				    $midx+$dec + 5/100 * $radius * sin($dRadians+$PI/2), $midy+$dec - 5/100 * $radius * cos($dRadians+$PI/2)
				    );
}

sub DrawTickAt {
    my ($pixmap, $gc, $cx, $cy, $dec) = @_;
    foreach my $nHour (1..12) {
      my $dRadians = $nHour * $PI / 6.0;
      $pixmap->draw_line($gc,
			 $cx + $dec + 9/10 * $radius * sin($dRadians),
			 $cy + $dec - 9/10 * $radius * cos($dRadians),
			 $cx + $dec + 1    * $radius * sin($dRadians),
			 $cy + $dec - 1    * $radius * cos($dRadians));
    }
}

sub DrawPointAt {
    my ($pixmap, $black_gc, $nHour, $cx, $cy) = @_;
    my $dRadians = $nHour * $PI / 30;

    $pixmap->draw_points($black_gc,
			$cx + 95/100 * $radius * sin($dRadians),
			$cy - 95/100 * $radius * cos($dRadians));
}


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