#!/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 mygtk2 qw(gtknew);
use ugtk2 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;

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

my $ntpfile = '/etc/ntp.conf';
my $ntpdlock = '/var/lock/subsys/ntpd';
my $ntpdsystemdlock = '/sys/fs/cgroup/systemd/system/ntpd.service';

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

$my_win->{window}->signal_connect(delete_event => sub { ugtk2->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 ($timezone->{timezone} = $in->ask_from_treelist(N("Timezone - DrakClock"), N("Which is your timezone?"), '/', \@timezones, $timezone->{timezone})) {
                                       $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 = Gtk2::Adjustment->new(0.0, 0.0, 23.0, 1.0, 5.0, 0.0);
my $adjm = Gtk2::Adjustment->new(0.0, 0.0, 59.0, 1.0, 5.0, 0.0);
my $adjs = Gtk2::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')) {
                                                                                                        install_ntp();
                                                                                                    }
                                                                                                }),
                                                                           0, $hb_ntp = gtknew('HBox', border_width => 5, children => [
                                                                                       0, gtknew('Label', text => N("Server:")),
                                                                                       1, $combo_ntpserver = Gtk2::Combo->new
                                                                                      ])
                                                                          ]))
                                                       ]),
                                            0, gtknew('VBox', children => [
                                                        0, gtknew('HBox', children => [
                                                                    0, $drawing_area = gtkset_size_request(Gtk2::DrawingArea->new, @image_size),
                                                                   ]),
                                                        0, my $time_box = gtknew('HBox', homogenous => 1, children => [
                                                                    0, my $spinner_h = Gtk2::SpinButton->new($adjh, 0, 0),
                                                                    0, my $spinner_m = Gtk2::SpinButton->new($adjm, 0, 0),
                                                                    0, my $spinner_s = Gtk2::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 { ugtk2->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(qw(/sbin/chkconfig --level 35 ntpd on));
                                                            # FIXME: Change to use systemctl when will not support old iniscript anymore
                                                            system(qw(service ntpd stop));
                                                            #verify that we have a valid hostname (thx sam)
                                                            $choosed_serv =~ s/[^-a-zA-Z0-9.]//g;
							    if (!system("/usr/sbin/ntpdate", $choosed_serv)) {
								update_time(); #- get the new time before updating the hwclock
                                # FIXME: Change to use systemctl when will not support old iniscript anymore
								system(qw(service ntpd start));
								$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 (-e $ntpdlock) { 
								system(qw(service ntpd stop));
								system(qw(/sbin/chkconfig --level 35 ntpd off));
							    }
							    elsif (-e $ntpdsystemdlock) {
								system(qw(systemctl stop ntpd.service));
								system(qw(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';
                                                        ugtk2->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 && (-e $ntpdsystemdlock || -e $ntpdlock)) {
    $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([ 'button_press_mask', 'button_release_mask', "pointer_motion_mask" ]);
$drawing_area->signal_connect(expose_event => \&expose_event);
$drawing_area->signal_connect(realize => sub {
                                  my $window = $drawing_area->window;
                                  $pixmap = Gtk2::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) = ($drawing_area->allocation->width/2, $drawing_area->allocation->height/2);
$my_win->main;
ugtk2->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('ntp') or ntp_widget_state(0);
    $my_win->{window}->set_sensitive(1);
}
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() {
    !$its_reset and $timer and Glib::Source->remove($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);
    $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);
	$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 expose_event {
    my ($widget, $event) = @_;
    my ($x, $y, $width, $height) = $event->area->values;
    $widget->window->draw_drawable($widget->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;
    my ($width, $height) = ($drawing_area->allocation->width, $drawing_area->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->style->white_gc;
    # fix race on ugtk2->exit that causes a crash (#33894)
    return 0  if !$gc;
    $pixmap->draw_rectangle($drawing_area->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->style->bg_gc('normal');
    my $black_gc = $drawing_area->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]) }