package AmphetaDesk::OS::Windows;
###############################################################################
# This package handles all the GUI routines for the Windows operating system. #
# It requires various modules to be compiled into the runtime wrapper script  #
# - these modules are assumed to be there. If not, oopsy, we got problems.    #
#                                                                             #
# LIST OF ROUTINES BELOW:                                                     #
#   gui_init - start the gui and display the window.                          #
#   gui_listen - listen for window events for our gui.                        #
#   gui_note - send a note to our gui window.                                 #
#   open_url - open a url in the system's default browser.                    #
#   _things - various routines that control the gui and should be private.    #
###############################################################################

use strict; $|++;
use AmphetaDesk::Settings;
use AmphetaDesk::Utilities;
use Win32::API;
use Win32::GUI;
use Win32::TieRegistry;
require Exporter;
use vars qw( @ISA @EXPORT );
@ISA = qw( Exporter );
@EXPORT = qw( gui_init gui_listen gui_note open_url );

# these variables are global for
# the Windows gui - they just keep
# track of various things that are important.
my ($hwnd, $icon, $hwnd_class, $window, $menu_bar);
my ($img, $systray_icon, $systray_menu, $font, $logbox);

# used to circumvent a bug where the
# popup menu functions get called twice.
my $already_called = 0;

# remember the process id.
my $fork_id_path;

###############################################################################
# gui_init - start the gui and display the window.                            #
###############################################################################
# USAGE:                                                                      #
#   gui_init;                                                                 #
#                                                                             #
# NOTES:                                                                      #
#   This routine loads specific libraries specific to the OS and attempts to  #
#   do everything necessary to start up a GUI window and start listening for  #
#   events. Further down in this file, you should see supplementary GUI       #
#   routines (starting with _) that are part of the GUI happenings this       #
#   routine inits.                                                            #
#                                                                             #
#   Most of the code within this routine is thanks to David Berube.           #
#   NOTE: All of this code was written for Win32::GUI v0.5xx, which means     #
#   that deprecated v0.6xx things like -style and -exstyle still exist. The   #
#   reason is twofold: one, we still need to use v0.5xx under Windows 95,     #
#   and two, we're too lazy to find out the right equivalents under v0.6xx.   #
#                                                                             #
# RETURNS:                                                                    #
#   1; this routine always returns happily.                                   #
###############################################################################

sub gui_init {

   # hwnd is a handle to a window - basically, window's
   # way of keeping track of it's program windows...
   $hwnd = GUI::GetPerlWindow();

   # comment this to see error messages in a dos window
   # otherwise, this will hide the blasted thing...
   # GUI::Hide($hwnd);

   # get the width and height of the user's system.
   my $screen_width  = Win32::GUI::GetSystemMetrics(0);
   my $screen_height = Win32::GUI::GetSystemMetrics(1);

   # create the icon handler.
   $icon = new Win32::GUI::Icon( get_setting("files_gui_win_icon") );

   # create a window class for our window.
   $hwnd_class = new Win32::GUI::Class( -name => "AmphetaDesk Class",
                                        -icon => $icon
                                      );

   # set up our menu bar. these point to various
   # routines defined later on in this file, as 
   # well as set up key commands for the items.
   $menu_bar = new Win32::GUI::Menu( "&File"                 => "AmphetaDesk::OS::Windows::File",
                                     " > E&xit"              => "AmphetaDesk::OS::Windows::_FileExit",
                                     "&Edit"                 => "AmphetaDesk::OS::Windows::Edit",
                                     " > &Copy Ctrl+C"       => "AmphetaDesk::OS::Windows::_EditCopy",
                                     " > Select &All Ctrl+A" => "AmphetaDesk::OS::Windows::_EditSelectAll",
                                     "&Tools"                => "AmphetaDesk::OS::Windows::Tools",
                                     " > &Open Browser"      => "AmphetaDesk::OS::Windows::_OpenBrowser",
                                     " > &Refresh Channels"  => "AmphetaDesk::OS::Windows::_RefreshChannels"
                                   );

   # creates the main window. notice that we use a generic
   # "Name" parameter, which is used for events, which must
   # have the format Name_EventName.
   $window = new Win32::GUI::Window( -name    => 'AmphetaDesk::OS::Windows::_Window', 
                                     -text    => 'AmphetaDesk',
                                     -left    => ($screen_width - 600)/2,
                                     -top     => ($screen_height - 400)/2,
                                     -width   => 480, -height => 400,
                                     -maxsize => [480, 400],
                                     -minsize => [480, 400],
                                     -menu    => $menu_bar,
                                     -class   => $hwnd_class,
                                     -icon    => $icon,
                                     -maximizebox => 0
                                   );

   # create the systray icon.
   $systray_icon = $window->AddNotifyIcon( -name => "AmphetaDesk::OS::Windows::_Systray",
                                           -id   => 1,
                                           -icon => $icon,
                                           -tip  => 'AmphetaDesk'
                                         );

   # create the popup menu.
   $systray_menu = new Win32::GUI::Menu( "SystrayMenu Functions" => "SystrayMenu",
                                         "> Open Browser"        => "AmphetaDesk::OS::Windows::_OpenBrowser",
                                         "> Refresh Channels"    => "AmphetaDesk::OS::Windows::_RefreshChannels",
                                         "> Exit"                => "AmphetaDesk::OS::Windows::_SystrayExit"
                                       );

   # create our pretty logo.
   $window->AddLabel( -text    => "",
                      -name    => "Bitmap",
                      -left    => -34,
                      -top     => -3,
                      -width   => 505,
                      -height  => 116,
                      -style   => 14,
                      -visible => 1,
                    );

   # actually display the image...
   $img = new Win32::GUI::Bitmap( get_setting("files_gui_win_logo") );
   $window->Bitmap->SetImage($img); $window->Bitmap->Resize(505, 116);

   # set the font of our log box below. we grab the Win32::GUI version so
   # that we can work around a bug fixed in the newest version of Win32::GUI,
   # whilst still providing support for the older (used by our Win95 runtime).
   my $version = $Win32::GUI::VERSION; my $font_size;
   if ($version eq "0.0.558") { $font_size = 12; } else { $font_size = 7; }
   $font = Win32::GUI::Font->new( -name => "Verdana", -size => $font_size );  

   # create the log box which is gonna hold all our info.
   $logbox = $window->AddRichEdit( -name    => "AmphetaDesk::OS::Windows::_RichEdit",
                                   -font    => $font,
                                   -top     => 116,
                                   -left    => 0,
                                   -width   => 505,
                                   -height  => 240,
                                   -tabstop => 1,
                                   -style   => WS_CHILD | WS_VISIBLE | ES_LEFT |
                                               ES_MULTILINE | ES_AUTOVSCROLL |
                                               WS_VSCROLL | ES_READONLY,
                                   -exstyle => WS_EX_CLIENTEDGE
                                 );

   # and make it a little smaller than our window size.
   $logbox->Resize( $window->ScaleWidth, $window->ScaleHeight - 118 );

   # finally, show all
   # the junk we just did.
   $window->Show();

   return 1;

}

###############################################################################
# gui_listen - listen for window events for our gui.                          #
###############################################################################
# USAGE:                                                                      #
#   gui_listen;                                                               #
#                                                                             #
# NOTES:                                                                      #
#   This routine checks the event queue and sees if there is anything that    #
#   needs to be done. It's called from the main loop of our program.          #
#                                                                             #
# RETURNS:                                                                    #
#   1; this routine always returns happily.                                   #
###############################################################################

sub gui_listen {

   # anyone there?
   Win32::GUI::PeekMessage(0,0,0);
   Win32::GUI::DoEvents();

   return 1;

}

###############################################################################
# gui_note - send a note to our gui window.                                   #
###############################################################################
# USAGE:                                                                      #
#   gui_note("This is a gui window line. Yup.");                              #
#                                                                             #
# NOTES:                                                                      #
#   Much like note(), only we send our message to our os specific gui window. #
#                                                                             #
# RETURNS:                                                                    #
#   1; this routine always returns happily.                                   #
###############################################################################

sub gui_note {

   my ($message) = @_;

   # select our last line.
   $logbox->Select(999999,999999);
   $logbox->ReplaceSel("$message\n", 1);
   select(undef, undef, undef, 0.25);
 
   # autoscroll the log box.
   $logbox->SendMessage (0x115, 1, 0) while $message =~ /\n|$/g;

   # listen for good measure.
   Win32::GUI::PeekMessage(0,0,0);
   Win32::GUI::DoEvents();

   return 1;

}

###############################################################################
# open_url - open a url in the system's default browser.                      #
###############################################################################
# USAGE:                                                                      #
#    open_url( );                                                             #
#                                                                             #
# OS SPECIFIC NOTES:                                                          #
#    This routine checks in the registry for the user's default browser.      #
#    If we haven't been told to use anything else, we use the registry        #
#    settings, else we try to pass the $url as a shell parameter to the       #
#    user's preferred choice.                                                 #
#                                                                             #
# RETURNS:                                                                    #
#    1; we instruct the user to open their browser if we can't.               #
###############################################################################

sub open_url {

   # construct our url.
   my $url = "http://127.0.0.1:" . get_setting("urls_port") . "/index.html";

   # we spit out our suggestion just to catch all instances.
   note("If your browser doesn't load, go to <$url>.", 1);

   # what browser?
   my $browser = $Registry->{"Classes\\http\\shell\\open\\command"}->{'\\'};
   note("Your registry states that $browser is your default program.");

   # if a browser_path hasn't been set, try
   # to open the default browser using the native API.
   # we do an API call instead of a 'start $url' sort
   # of thing, because the API call seems far more stable -
   # we've experienced slowness with 'start $url'
   # and random crashes on some machines. 
   if ( get_setting("user_browser_path") eq "default" ) {
      my $ShellExecute = new Win32::API("shell32", "ShellExecuteA", ['N','P', 'P', 'P', 'P', 'I'], 'N');
      $ShellExecute->Call(0, "open", $url, 0, 0, 1);
   }

   # if a browser_path has been defined, try passing
   # the $url to the .exe and hope it understands.
   else {
      note("But instead, we'll try loading " . get_setting("user_browser_path") . ".");
      unless ( $fork_id_path = fork ) { system( qq|get_setting("user_browser_path") $url| ); }
   }

   return 1;

}

###############################################################################
# _things - various routines that control the gui and should be private.      #
###############################################################################
# USAGE:                                                                      #
#    These are internally called by the GUI and shouldn't be publically       #
#    used. So stop poking around here. Sheesh. Flippin' nosy people. Sigh.    #
###############################################################################

# various menu commands.
sub AmphetaDesk::OS::Windows::_EditCopy_Click      { note("Received 'Copy' request."); $logbox->SendMessage(0x301,0,0); }
sub AmphetaDesk::OS::Windows::_EditSelectAll_Click { note("Received 'Select All' request."); $logbox->Select(0,999999); }
sub AmphetaDesk::OS::Windows::_FileExit_Click      { note("Quitting AmphetaDesk due to File > Exit."); &exit_program; }

# re-open the browser window.
sub AmphetaDesk::OS::Windows::_OpenBrowser_Click { 

   unless ( $already_called ) { 

      # for some reason, we get two clicks every
      # time someone clicks a menu item. so,
      # we create a constant and check later on.
      $already_called = 1;

      # helloooooooo, nurse!
      note("Received 'Open Browser' request from Systray.");

      # do it!.
      open_url();
   }

   else { $already_called = 0; }
}

# Respond to the menu choice for 
# channel refreshing and browser opening.
sub AmphetaDesk::OS::Windows::_RefreshChannels_Click {

   unless( $already_called ) {

      # helloooooooo, nurse!
      note("Received 'Refresh Channels' request from Systray.");
      note("--------------------------------------------------------------------------------", 1);
      note("Refreshing the channel list and opening browser window...",                        1);
      note("--------------------------------------------------------------------------------", 1);

      # do the dirty deed.
      AmphetaDesk::MyChannels::download_my_channels();
      open_url(); # my name is SuperFlippinTiredMan!

      # for some reason, we get two clicks every
      # time someone clicks a menu item. so,
      # we create a constant and check later on.
      $already_called = 1;
   }

   else { $already_called = 0; }
}

# generic code to force removal of systray icon.
sub AmphetaDesk::OS::Windows::_Remove_Systray_Icon { Win32::GUI::NotifyIcon::Delete( $systray_icon->{-parent}, -id => $systray_icon->{-id} ); }

# if the systray icon is clicked, re-enable the window. we have
# to do this as a _MouseEvent as opposed to _DblClick due to 
# _DblClick not being supported on a NotifyIcon. The $event
# id is one seven: 512 => 'Mouse Move', 514 => 'Left Click',
# 515 => 'Left DoubleClick', 517 => 'Right Click', 518 =>
# 'Right DoubleClick',519 => 'Middle Down', and 520 => 'Middle Up'.
# Thanks to Joe Frazier, Jr. for this information.
sub AmphetaDesk::OS::Windows::_Systray_MouseEvent { my $event = shift; if ($event == 515) { $window->Enable; $window->Show; } }

# If someone right clicks the systray icon, show a menu.
sub AmphetaDesk::OS::Windows::_Systray_RightClick { 

   note("Received Systray right click.");

   # get the x and y coords of the mouse to display the menu at.
   my($x, $y) = Win32::GUI::GetCursorPos();

   # make the popup menu visible at the cursor
   $window->TrackPopupMenu($systray_menu->{SystrayMenu}, $x, $y-50);
}

# quit the program. we only do it this way so we can track what was clicked.
sub AmphetaDesk::OS::Windows::_SystrayExit_Click { note("Quitting AmphetaDesk due to Systray > Exit."); &exit_program; }

# hide the window if it's been minimized. the only
# way to get it back would be from the systray icon.
# we need to figure out what to do when people click
# the "X' on the window. minimize? or close the app?
sub AmphetaDesk::OS::Windows::_Window_Minimize  { note("Minimizing window to Systray."); $window->Disable; $window->Hide; return -1; }
sub AmphetaDesk::OS::Windows::_Window_Terminate { note("Quitting AmphetaDesk due to window termination."); &exit_program; }

# our exit code - called from various methods in the Windows
# GUI (systray quit, window termination, file->exit, etc.)
sub exit_program {

   # kill our children if they are there.
   if ( $fork_id_path ) {
      note("Attempting to kill pid #$fork_id_path.");
      kill( "TERM", $fork_id_path );
   }

   # remove the icon.
   AmphetaDesk::OS::Windows::_Remove_Systray_Icon;

   # ciao.
   exit;

}

1;
