#!/bin/sh
# Ce qui suit redémarre le script en utilisant wish \
# si possible wish8.5 ou supérieur \
wish=`which wish8.5` || wish=wish; exec "$wish" "$0" "$@"

global t_connexion t_usage
set t_connexion 0	 ;# temps de connexion
set t_usage 0            ;# durée d'utilisation réelle

# arguments:
# u=hh:mm:ss, u=mm:ss, u=ss => démarre le timer "usage" à une autre valeur que 0s
# c=hh:mm:ss, c=mm:ss, c=ss => démarre le timer "connexion" à une autre valeur que 0s
foreach arg $argv {
	set ok 0
	if [set n [scan $arg "u=%d:%d:%d" v1 v2 v3]] { set ok 1 }
	if {$n == 3} {
		set t_usage [expr $v3 + 60*($v2 + 60*$v1)]
	} elseif {$n == 2} {
		set t_usage [expr $v2 + 60*$v1]
	} elseif {$n == 1} {
		set t_usage $v1
	}
	if [set n [scan $arg "c=%d:%d:%d" v1 v2 v3]] { set ok 1 }
	if {$n == 3} {
		set t_connexion [expr $v3 + 60*($v2 + 60*$v1)]
	} elseif {$n == 2} {
		set t_connexion [expr $v2 + 60*$v1]
	} elseif {$n == 1} {
		set t_connexion $v1
	}
	if [expr !$ok] {
		puts stderr "$argv0: paramètre $arg incorrect"
		puts stderr "paramètres possibles:"
		puts stderr "u=hh:mm:ss, u=mm:ss, u=ss => valeur initiale du timer \"usage\""
		puts stderr "c=hh:mm:ss, c=mm:ss, c=ss => valeur initiale du timer \"connexion\""
		flush stderr
		update
		after 1000 {exit 1}
		return
	}
}

#construction de la fenêtre
checkbutton .chk -text "Alertes" -command update -variable alertes
.chk select
label .etiq -text " 00:00:00 "
label .dontusage -text " dont"
label .usage -text "00:00:00"
set couleur_fond_inactif [.usage cget -bg]
set couleur_fond_actif "pink"
pack .chk .etiq .dontusage .usage -side left -ipadx 0 -ipady 0

#configuration
set titre "Chronomètre"  ;# titre de la fenêtre
set posx -100            ;# position X de la fenêtre ("-" = depuis la droite)
set posy 0               ;# position Y de la fenêtre
set resolution_usage 45  ;# temps après lequel on est considéré inactif (s)
			 ;# moins de 50s, car vlc se déclare actif toutes les 50s

#initialisation de variables
set t1 [expr [clock seconds] - $t_connexion]  ;# heure au début de la session
#set t_connexion 0	 ;# temps de connexion
#set t_usage 0            ;# durée d'utilisation réelle
set dernier_usage 0      ;# heure de la dernière utilisation
set vivant_usage 0       ;# l'ordinateur est-il actuellement utilisé?
set alerte_passee 0      ;# numéro de la dernière alerte affichée (ou ignorée)
set souris_pos {0 0}     ;# position de la souris (pour détecter l'activité)
set decx 0               ;# décalage X entre la position demandée et la position fournie par le gestionnaire de fenêtre
set decy 0               ;# décalage Y, idem
set fixe_position 2      ;# indique que (posx, posy) est une position obligatoire (vaut 2 au début)

# affiche ou cache la bordure (les décorations) de la fenêtre;
#    fixe (ou corrige) la position de la fenêtre
#    fait quelques bidouilles pour que le gestionnaire de fenêtres réagisse
#    définit le titre de la fenêtre
#    installe la fenêtre en premier plan
proc bordure arg {
  global tk_version
  global posx posy decx decy fixe_position
  global titre
  if {[scan [winfo geometry .] "%dx%d+%d+%d" tx ty posx1 posy1] < 4} {
	tk_messageBox -message {"Erreur timer.tcl: géométrie illisible: " [winfo geometry .]}
  }
  if {$posx<0 && $fixe_position==2} {set posx [expr [winfo screenwidth .]+$posx-$tx]}
  if {$posy<0 && $fixe_position==2} {set posy [expr [winfo screenheight .]+$posy-$ty]}
  if {!$fixe_position} {
    set posx [expr $posx1 - $decx]
    set posy [expr $posy1 - $decy]
    # vérifier que cette nouvelle position est bien appliquée (supprimé car la nouvelle position peut être inatteignable si hors limites écran)
    #set fixe_position 1
    #incr posx [expr -$decx]
    #incr posy [expr -$decy]
    # évite de retrouver la fenêtre en bas de l'écran!
    if {$posy < 0} {set posy 0}
  }
  if {$tk_version >= "8.5"} {
	wm attributes . -topmost [expr !$arg]
  }
  wm title . "$titre"
  wm overrideredirect . [expr !$arg]
  wm withdraw .
# semble nécessaire avec Xubuntu 12.10, sinon affiche un cadre vide
update
after 100
  wm deiconify .
  wm geometry . ""
  wm geometry . [format "+%d+%d" $posx $posy]
  tkwait visibility .
#  update
#  after 100
  update
  scan [winfo geometry .] "%dx%d+%d+%d" tx ty posx2 posy2
  if {$posx2 >= $posx && $posx2-$posx < 20 && $posy2 >= $posy && $posy2-$posy < 50} { # géométrie prise en compte (le WM peut ajuster la position)
	set decx [expr $posx2-$posx]
	set decy [expr $posy2-$posy]
	# plus besoin d'indiquer la position
	#puts [format "Tout va bien, demandée=%+d%+d obtenue=%+d%+d fixe_position %d=>0" $posx $posy $posx2 $posy2 $fixe_position]
	set fixe_position 0
  } else {
	puts [format "pas bon, demandée=%+d%+d obtenue=%+d%+d fixe_position=%d" $posx $posy $posx2 $posy2 $fixe_position]
  }
  update
#  after idle {wm deiconify .}
#  wm resizable . 0 0
#  wm resizable . 1 0
  wm resizable . 1 1
}

# permet de savoir si l'ordinateur est actuellement utilisé
# (jusqu'à v8.4: juste à partir des mouvements de la souris,
# à partir de v8.5: réagit aussi au clavier)
proc souris_vivante {} {
  global tk_version
  if {$tk_version >= "8.5"} {
	# mieux que la simple détection de la souris: ça détecte aussi l'activité du clavier
	# à partir du temps d'inactivité de l'ordinateur (uniquement à partir de tcl/tk v8.5
	set inactive [tk inactive]
	return [expr $inactive < 1000]
  }
  global souris_temps souris_pos
  set t [clock seconds]
  set pos [winfo pointerxy .]
  if {"$pos" != "$souris_pos"} {
    set souris_temps $t
    set souris_pos "$pos"
    return 1
  }
  return 0
}

# calcule et affiche le temps d'utilisation de l'ordinateur
proc verifie_usage {} {
  global t2 t_usage dernier_usage resolution_usage vivant_usage
  global couleur_fond_actif couleur_fond_inactif
#  .debug configure -text "t=$t dernier=$dernier_usage"
#  update
  if {[souris_vivante] || $dernier_usage == 0} {
	if {$dernier_usage == 0} {set dernier_usage $t2}
	set old_m [expr $t_usage/60]
	if {$t2 - $dernier_usage <= $resolution_usage} {
		# ne pas incrémenter le temps d'utilisation, même sans période d'inactivité, 
		# si la dernière période d'activité remonte à plus d'une minute (correspond à une mise en veille)
		set t_usage [expr $t_usage + $t2 - $dernier_usage]
	}
	set new_m [expr $t_usage/60]
	set dernier_usage $t2
	set vivant_usage 1
        set s [expr $t_usage%60]
        set tt [expr $t_usage/60]
        set m [expr $tt%60]
        set h [expr $tt/60]
        set usage [format "%02d:%02d:%02d" $h $m $s]
        .usage configure -text "$usage"
	.dontusage configure -bg $couleur_fond_actif
	.usage configure -bg $couleur_fond_actif
	# réafficher en 1er plan si passage d'une minute de temps d'utilisation et fenêtre sans bord affiché
	# (à cause d'un bug: parfois la fenêtre disparaît totalement: impossible à retrouver)
	#global bord
	#if {$old_m != $new_m && $bord == 0} {bordure $bord}
         update
  } elseif {$t2 - $dernier_usage > $resolution_usage} {
	set vivant_usage 0
	.dontusage configure -bg $couleur_fond_inactif
	.usage configure -bg $couleur_fond_inactif
	update
  }
  # réafficher en 1er plan si la position n'est pas encore prise en compte
  global fixe_position bord
  if {$fixe_position} {bordure $bord}

  if {$vivant_usage == 0} {
	set dernier_usage $t2
  }
}

# affiche un message d'avertissement au milieu de l'écran
#   ne fait rien si la case "alertes" est décochée
#   ferme la fenêtre précédente si elle est encore là
proc alerte_affiche message {
  global alertes
  if [winfo exists .dial] {destroy .dial}
  if $alertes {
  toplevel .dial
  label .dial.etiq -text "Ça fait $message que tu utilises cet ordinateur !"
  button .dial.but -text "Fermer" -default active -command { destroy .dial }
  pack .dial.etiq .dial.but -ipadx 10 -ipady 5
  wm title .dial "Temps de connexion"

#Centrer la fenêtre au milieu de l'écran
#wm withdraw .dial
update
set x [expr {([winfo screenwidth .]-[winfo width .dial])/2}]
set y [expr {([winfo screenheight .]-[winfo height .dial])/2}]
wm geometry  .dial +$x+$y
wm transient .dial .
wm deiconify .dial
  }
}

# vérifie s'il faut afficher l'un des messages d'avertissement
# la fonction doit d'avoir être lancée avec hh=mm=ss=0 pour initialiser
proc alerte {hh mm ss message} {
  global alerte_examinee
  set lim [expr $ss+60*($mm+60*$hh)]
  if { $lim == 0 } {
    set alerte_examinee 0
  } else {
global t1 t2 t_usage
# afficher les alertes en fonction du temps de connexion?
#set t [expr $t2-$t1]
# afficher les alertes en fonction du temps réel d'utilisation?
set t $t_usage
     if { $t >= $lim } {
	incr alerte_examinee
	global alerte_passee
	if {$alerte_examinee > $alerte_passee} {
		set alerte_passee $alerte_examinee
		alerte_affiche $message
	}
      }
  }
}

# fonction appelée à chaque seconde pour afficher le temps de connexion
proc montimer {} {
  global t_connexion t1 t2 resolution_usage
  set t2 [clock seconds]  ;# heure actuelle
  if {$t2 - $t1 - $t_connexion > $resolution_usage} {
	# ne pas incrémenter le temps de connexion si pause supérieure à 1 minute (correspond à une mise en veille)
	set t1 [expr $t2-$t_connexion]	;# permet de faire comme si le temps de connexion n'avait pas évolué depuis la dernière fois
  }
  set t_connexion [expr $t2-$t1]    ;# temps de connexion
  set s [expr $t_connexion%60]
  set t [expr $t_connexion/60]
  set m [expr $t%60]
  set t [expr $t/60]
  set h [expr $t%60]

  set hh [format " %02d:%02d:%02d " $h $m $s]
  .etiq configure -text "$hh"
  update

  # parfois, la fenêtre est accidentellement minimisée (raison inconnue, mais solution facile; uniquement si affiché sans bordure)
  global bord
  if {[wm state .] == "iconic" && $bord == 0} {wm deiconify .}

  #en cours d'utilisation?
  verifie_usage

  #affichage des messages d'alerte (le premier sert à initialiser)
  alerte 0 0 0 ""

  if 0 {
    #message rapides pour déboguer  (ne fait rien si condition "if 0")
    alerte 0 0 5 "5 minutes"
    alerte 0 0 10 "10 minutes"
    alerte 0 0 15 "un quart d'heure"
    alerte 0 0 20 "20 minutes"
    alerte 0 0 30 "une demie-heure"
    alerte 0 0 45 "trois quarts d'heure"
  }

  # messages normaux
  alerte 0 5 0 "5 minutes"
  alerte 0 10 0 "10 minutes"
  alerte 0 15 0 "un quart d'heure"
  alerte 0 20 0 "20 minutes"
  alerte 0 30 0 "une demie-heure"
  alerte 0 45 0 "trois quarts d'heure"
  alerte 1 0 0 "une heure"
  alerte 1 30 0 "une heure et demie"
  for {set i 2} {$i <= 24} {incr i} {
    alerte $i 0 0 "$i heures"
    alerte $i 30 0 "$i heures et demie"
  }

  # dans 1 seconde, on affiche l'heure à nouveau
  after 1000 montimer
}

# agrandit ou diminue la fenêtre (selon que la souris est dedans; 0=petit 1=grand)
proc agrandit arg {
  wm geometry . ""
  pack propagate . 1
  if $arg {
	pack forget .chk .etiq .dontusage .usage
	pack .chk .etiq .dontusage .usage -side left -ipadx 0 -ipady 0
  } else {
	pack forget .chk .etiq .dontusage
  }
  update
  pack propagate . 0
}

# main

#dire qu'au départ on ne souhaite pas avoir la bordure
set bord 0

# lancer le timer pour la première fois (ensuite il se relance)
montimer

# afficher la fenêtre sans ses décorations
bordure $bord
agrandit 0

# agrandit ou retrécir la fenêtre, si entrée ou sortie de la fenêtre principale
bind . <Enter> {if {%i == [winfo id .]} {agrandit 1}}
bind . <Leave> {if {%i == [winfo id .]} {agrandit 0}}
# double-clic, clic droit ou clic central dans la fenêtre => inverser l'affichage des décorations
event add <<change-bord>> <Double-Button-1> <Button-3> <Button-2>
bind . <<change-bord>> {global bord fixe_position;set bord [expr !$bord];bordure $bord}

# une fois que la fenêtre est bien affichée, on ne propage plus la géométrie
# (évite que la fenêtre ne se déforme en fonction de la taille des chiffres)
update
pack propagate . 0

