#!/usr/bin/perl
#==========================================================================
# Auteur : djibril
# Date   : 03/07/2011 14:08:50
# But    : Script Perl/Tk utilisant des threads pour rechercher nos fichiers
#==========================================================================
use warnings;
use strict;

use Tk;    # Pour créer notre GUI
use Tk::LabFrame;
use File::Find;
use threads;                    # Pour créer nos threads
use threads::shared;            # Pour partager nos données entre threads
use Time::HiRes qw( sleep );    # Pour faire des sleeps < à une seconde

# Contient les fonctions à appeler dans le thread si besoin
my %fonctions_a_lancer_dans_thread = ( 'recherchez_fichier' => \&recherchez_fichier, );

#===================================
# Threads et variables partagées
#==================================
my $tuer_thread : shared;              # Permet de tuer le thread proprement
my $nom_fonction : shared;             # Contient le nom de la fonction à appeler
my $thread_travail : shared;           # Contient la valeur permettant au thread de lancer une procédure
my @arguments_thread : shared;         # Contient les arguments à passer à une éventuelle procédure
my @resultat_fonction : shared;        # Contient le résultat des fonctions lancées dans le thread
my $ref_resultat_fonction : shared;    # Contient le résultat des fonctions lancées dans le thread

$thread_travail = 0;                   # 0 : thread ne fait rien, 1 : il bosse
$tuer_thread    = 0;                   # 0 : thread en vie, 1 : thread se termine

# Création du thread
my $thread = threads->create( \&notre_processus_leger );

#===================================
# Debut du code principal Perl Tk
#==================================
my $fenetre = new MainWindow(
  -title      => 'Recherche de fichiers',
  -background => 'white',
);
$fenetre->protocol( "WM_DELETE_WINDOW", \&fermer_application );

# Affichage de l'heure
my $date_heure = date();
my $label_date = $fenetre->Label(
  -textvariable => \$date_heure,
  -background   => 'white',
  -font         => '{Arial} 16 {bold}',
)->pack(qw/ -pady 20 /);

# État de la recherche du fichier
my $etat_recherche = 'Aucune recherche en cours';
my $label_etat     = $fenetre->Label(
  -textvariable => \$etat_recherche,
  -background   => 'white',
  -foreground   => 'blue',
  -font         => '{Arial} 12 {bold}',
)->pack(qw/ -pady 20 /);

# Cadre de recherche
my $cadre = $fenetre->LabFrame(
  -label      => 'Cadre de recherche',
  -background => 'white',
)->pack(qw/ -pady 20 -padx 20 /);

my ( $motif_recherche, $repertoire_emplacement );
my $label1 = $cadre->Label( -text => 'Nom du fichier à trouver : ', -background => 'white' );
my $entry_nom_fichier = $cadre->Entry( -textvariable => \$motif_recherche );
my $label2 = $cadre->Label( -text => 'Emplacement : ', -background => 'white' );
my $entry_emplacement = $cadre->Entry( -textvariable => \$repertoire_emplacement );

my $bouton_emplacement = $cadre->Button(
  -text    => '...',
  -command => sub {
    $repertoire_emplacement = $cadre->chooseDirectory(
      -title     => 'Sélectionner un emplacement',
      -mustexist => 1,
    );
  },
);

# Affichage d'un bouton pour rechercher un fichier
my $bouton = $cadre->Button(
  -text    => 'Recherchez un fichier',
  -command => [ \&recherchez_fichier_tk ],
  -font    => '{Arial} 14 {bold}',
);

$label1->grid( $entry_nom_fichier, '-',                 -sticky => 'nw' );
$label2->grid( $entry_emplacement, $bouton_emplacement, -sticky => 'nw' );
$bouton->grid( '-',                '-',                 qw/ -padx 10 -pady 10 / );

# Centrer ma fenêtre
centrer_widget($fenetre);

# Toutes les secondes, la date et l'heure évoluent
$fenetre->repeat( 1000, sub { $date_heure = date(); } );

MainLoop;

#================================================
# notre_processus_leger
#================================================
sub notre_processus_leger {

  # Tourne en rond
  while (1) {

    # demande au thread de travailler
    if ( $thread_travail == 1 ) {

      # Lance la procédure
      my @resutat = $fonctions_a_lancer_dans_thread{$nom_fonction}->(@arguments_thread);
      $ref_resultat_fonction = shared_clone( \@resutat );

      # demande au thread de dormir
      $thread_travail = 0;
    }

    # Terminer le thread
    last if ( $tuer_thread == 1 );
    sleep 0.5;
  }
  return;
}

sub fermer_application {

  # Demande au thread de se terminer
  $tuer_thread = 1;

  # On attend que le thread se termine proprement
  $thread->detach();

  exit;
}

sub recherchez_fichier_tk {
  if ( not defined $motif_recherche or not defined $repertoire_emplacement or !-d $repertoire_emplacement ) {
    return;
  }

  # On désactive le bouton ListerFichiers
  $bouton->configure( -state => 'disabled' );

  $etat_recherche = "Liste des fichiers en cours";

  # On lui indique la procédure à appeler
  $nom_fonction = "recherchez_fichier";

  # On lui donne les arguments
  @arguments_thread = ( $motif_recherche, $repertoire_emplacement );

  # On va demander au thread de bosser
  $thread_travail = 1;

  my $id;
  $id = $fenetre->repeat(
    500,
    sub {
      if ( $thread_travail == 0 ) {

        # Thread terminé, on affiche le résultat
        my $nombre_fichier = $ref_resultat_fonction->[0];
        $etat_recherche = "$nombre_fichier fichier(s) trouvé(s)";
        $bouton->configure( -state => 'normal' );
        $id->cancel;
      }
    }
  );

  return;
}

sub recherchez_fichier {
  my ( $motif_recherche, $repertoire_emplacement ) = @_;

  # Recherchons le fichier
  my $fichier_trouve = 0;
  find(
    { wanted => sub {
        if ( $_ =~ m{$motif_recherche}i ) {
          $fichier_trouve++;
          print "$fichier_trouve- $File::Find::name\n";
        }
        }
    },
    $repertoire_emplacement
  );

  return $fichier_trouve;
}

#================================================
# But : Obtenir la date et l'heure
#================================================
sub date {
  my $time = shift || time;    #$time par defaut vaut le time actuel
  my ( $seconde, $minute, $heure, $jour, $mois, $annee, $jour_semaine, $jour_annee, $heure_hiver_ou_ete )
    = localtime($time);
  $mois  += 1;
  $annee += 1900;

  # On rajoute 0 si le chiffre est compris entre 1 et 9
  foreach ( $seconde, $minute, $heure, $jour, $mois, $annee ) { s/^(\d)$/0$1/; }
  return "$jour/$mois/$annee - $heure:$minute:$seconde";
}

#================================================
# But : Centrer un widget automatiquement
#================================================
sub centrer_widget {
  my ($widget) = @_;

  # Height and width of the screen
  my $largeur_ecran = $widget->screenwidth();
  my $hauteur_ecran = $widget->screenheight();

  # update le widget pour récupérer les vraies dimensions
  $widget->update;
  my $largeur_widget = $widget->width;
  my $hauteur_widget = $widget->height;

  # On centre le widget en fonction de la taille de l'écran
  my $nouvelle_largeur = int( ( $largeur_ecran - $largeur_widget ) / 2 );
  my $nouvelle_hauteur = int( ( $hauteur_ecran - $hauteur_widget ) / 2 );
  $widget->geometry( $largeur_widget . "x" . $hauteur_widget . "+$nouvelle_largeur+$nouvelle_hauteur" );

  $widget->update;

  return;
}