#!/usr/bin/perl -w
#
# Program to interface with a Kodak Digital camera.
#
#  http://www.berkhirt.com/HomerProductions/
#  bhirt@berkhirt.com
#
# Copyright (c) 1998 by Brian Hirt, all rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#


use DC210;
use DC50;
use DC120;

# The wonderful libgtk from the gimp http://www.gtk.org & http://www.gimp.org
use Gtk;
use Gtk::Atoms;

use Carp;

use strict;

init Gtk;


my $dc = undef;
my $port = undef;
my $speed = undef;
my $cameraType = undef;


# vars for controlling downloads.
my $totalDownloadSize = undef;
my $totalBytesDownloaded = undef;
my $downloadLabel = undef;
my $downloadProgressBar = undef;
my $downloadDialog = undef;
my $nonSupportedCameraDialog = undef;

my $cameraStatusDialog = undef;

my $connectDialog = undef;
my $notConnectedDialog = undef;
my $cannotConnectDialog = undef;
my $alreadyConnectedDialog = undef;
my $pictureList = undef;

sub takePicture
{
  if (defined($dc)) 
  {
    $dc->takePicture;
  }
  else
  {
    notConnectedDialog();
  }
}

sub destroyWindow 
{
  my($widget, $windowref, $w2) = @_;
  $$windowref = undef;
  $w2 = undef if defined $w2;
  0;
}


sub deletePicture
{
# deletes the requested picture.
  my $dc = shift;
  my $pictureNumber = shift;

  my $info = $dc->pictureInfo($pictureNumber);

  if (defined($info))
  {
    $dc->deletePicture($pictureNumber);
  }
  else
  {
    print STDERR "bad picture number supplied or comminucations failure\n";
  }
}

sub dumpThumbnail
{
# downloads the requested thumbnail.
  my $dc = shift;
  my $pictureNumber = shift;

  my $info = $dc->pictureInfo($pictureNumber);

  if (defined($info))
  {
    my $fileName = $info->fileName;
    $fileName =~ s/\.JPG$/.BMP/i;

#    open(BMP,">T$fileName");
#    print BMP $dc->thumbnail($pictureNumber,$DC210::HIGH_RES_THUMBNAIL);
#    close(BMP);
  }
  else
  {
    print STDERR "bad picture number supplied or comminucations failure\n";
  }
}

sub dumpPicture
{
# downloads the requested picture.
  my $dc = shift;
  my $pictureNumber = shift;

  my $info = $dc->pictureInfo($pictureNumber);

  if (defined($info))
  {
    open(JPG,">" . $info->fileName);
    print JPG $dc->picture($pictureNumber);
    close(JPG);
  }
  else
  {
    print STDERR "bad picture number supplied or comminucations failure\n";
  }
}

sub cameraStatus
{
  my $this = shift;
  my $action = shift;

  if ($action eq 'show')
  {
    print STDERR "show action\n";
    if (not defined $cameraStatusDialog) 
    {
      print STDERR "creating dialog\n";
      $cameraStatusDialog = new Gtk::Dialog;
      $cameraStatusDialog->signal_connect( "destroy", \&cameraStatus, "close");
      $cameraStatusDialog->signal_connect( "delete_event", \&cameraStatus, "close");

      my $statusList = new Gtk::CList(2);
      $statusList->set_row_height(20);
      $statusList->column_titles_show;
      $statusList->set_selection_mode('single');
#      $statusList->set_policy ('automatic', 'automatic');
	  
      $statusList->set_column_title(0,'Option');
      $statusList->set_column_width(0, 100);
      $statusList->set_column_justification(0, 'left');

      $statusList->set_column_title(1,'Value');
      $statusList->set_column_width(1, 100);
      $statusList->set_column_justification(1, 'right');

      $statusList->border_width(5);
      $statusList->show;

      if (defined($dc) && defined($dc->status))
      {
        my $status = $dc->status;
        my $attribute;

	foreach $attribute ($status->attributes)
	{
	  $statusList->append($attribute,$status->$attribute());
	}
      }

      my $box1 = new Gtk::VBox(0,0);
      $box1->show;

      my $button = new Gtk::Button "OK";
      $button->can_default(1);
      $button->signal_connect("clicked", \&cameraStatus,'close');
      $button->grab_default;
      $button->show;

      $cameraStatusDialog->vbox->pack_start($statusList, 1, 1, 0);
      $cameraStatusDialog->action_area->pack_start($button, 1, 1, 0);
    }

    # if the dialog is not visible, show it.
    if (!$cameraStatusDialog->visible) 
    {
      $cameraStatusDialog->show;
    }
  }
  elsif ($action eq "close")
  {
    print STDERR "close action\n";
    if (defined($cameraStatusDialog))
    {
      $cameraStatusDialog->destroy;
      $cameraStatusDialog = undef;
    }
  }
  else
  {
    print STDERR "unknown action\n";
  }
}

sub setCameraType
{
  my $this = shift;

  $cameraType = shift;
  print STDERR "setting camera type to $cameraType\n";
}

sub setPort
{
  my $this = shift;

  $port = shift;
  print STDERR "setting port to $port\n";
}

sub setPortSpeed
{
  my $this = shift;

  $speed = shift;
  print STDERR "setting port speed to $speed\n";
}

sub disconnect
{
  my $this = shift;
  my $connectButton = shift;

  if (defined($dc))
  {
    $dc = undef;
    $this->hide;
    $connectButton->show;
    populatePictureList();
  }
}

sub connect
{
  my $this = shift;
  my $disconnectButton = shift;

  my $cameraClass = {};
  $cameraClass->{'DC40'} = 'DC50';
  $cameraClass->{'DC50'} = 'DC50';
  $cameraClass->{'DC120'} = 'DC120';
  $cameraClass->{'DC200'} = 'DC210';
  $cameraClass->{'DC210'} = 'DC210';

  if (!defined($dc))
  {
    if (defined( $cameraClass->{$cameraType} ))
    {
      $dc = $cameraClass->{$cameraType}->new($port,$speed);

      if (!defined($dc))
      {
	cannotConnectDialog();
      }
      else
      {
	$this->hide;
	$disconnectButton->show;
	populatePictureList();
      }
    }
    else
    {
      nonSupportedCameraDialog();
    }
  }
}

sub populatePictureList
{
  my $this = shift;

  if (defined($pictureList))
  {
    $pictureList->clear;

    if (defined($dc)) 
    {
      my $i;
      for $i ( 1 .. $dc->status->numberOfPictures ) 
      {
	my $pictureInfo = $dc->pictureInfo($i);

	$pictureList->append("$i", 
		$pictureInfo->fileName,
		$pictureInfo->fileSize,
		$pictureInfo->resolution,
		$pictureInfo->compressionMode);
        processQueuedEvents();
      }
    }
  }
  else
  {
    carp "cannot populate picture list if the picture list doesn't exist.";
  }
}

sub pictureSelected
{
  my $this = shift;
  my $blah = shift;

  print "row selected $this,$blah\n";
}

sub pictureUnselected
{
  my $this = shift;
  my $blah = shift;

  print "row unselected $this,$blah\n";
}

sub createPictureList
{
  my $this = shift;
      
  # Create a compound list and set options.
  $pictureList = new Gtk::CList(5);
  $pictureList->set_row_height(20);
  $pictureList->column_titles_show;
  $pictureList->set_selection_mode('multiple');
#  $pictureList->set_policy ('automatic', 'automatic');
      
  $pictureList->set_column_title(0,'#');
  $pictureList->set_column_width(0, 20);
  $pictureList->set_column_justification(0, 'right');

  $pictureList->set_column_title(1,'File Name');
  $pictureList->set_column_width(1, 120);
  $pictureList->set_column_justification(1, 'left');

  $pictureList->set_column_title(2,'File Size');
  $pictureList->set_column_width(2, 60);
  $pictureList->set_column_justification(2, 'right');

  $pictureList->set_column_title(3,'Resolution');
  $pictureList->set_column_width(3, 60);
  $pictureList->set_column_justification(3, 'left');

  $pictureList->set_column_title(4,'Compression');
  $pictureList->set_column_width(4, 60);
  $pictureList->set_column_justification(4, 'left');

  # receive notification that a row has been selected/unselected
  $pictureList->signal_connect("select_row", \&pictureSelected);
  $pictureList->signal_connect("unselect_row", \&pictureUnselected);

  $pictureList->border_width(5);
  $pictureList->show;

  my $box1 = new Gtk::VBox(0,0);
  $box1->show;
  $box1->pack_start($pictureList, 1, 1, 0);

  return $box1;
}

sub createCameraSelectionBox
{
  my $selectionBox = new Gtk::HBox(0, 10);
  $selectionBox->border_width(10);
  $selectionBox->show;

  my $label = new Gtk::Label 'Port ';
  $label->show;
  $selectionBox->pack_start($label, 0, 0, 0);

  # create a popup menu for device
  my $omenu = new Gtk::OptionMenu;
  my $menu = new Gtk::Menu;
  my $submenu = undef;
  my $menuItem = undef;

  setPort(undef,'/dev/cua0');
  foreach ('/dev/cua0','/dev/cua1','/dev/cua2','/dev/cua3')
  {
    $menuItem = new Gtk::RadioMenuItem "$_", $menuItem;
    $menuItem->signal_connect("activate", \&setPort, $_);
    $menu->append($menuItem);
    $menuItem->show;
  }
  $omenu->set_menu($menu);
  $omenu->show;
  $selectionBox->pack_start($omenu, 0, 0, 0);

  $label = new Gtk::Label 'Speed';
  $label->show;
  $selectionBox->pack_start($label, 0, 0, 0);

  # create a popup menu for speed
  $omenu = new Gtk::OptionMenu;
  $menu = new Gtk::Menu;
  $submenu = undef;
  $menuItem = undef;

  setPortSpeed(undef,9600);
  foreach (9600,19200,38400,57600,115200)
  {
    $menuItem = new Gtk::RadioMenuItem "$_", $menuItem;
    $menuItem->signal_connect("activate", \&setPortSpeed, $_);
    $menu->append($menuItem);
    $menuItem->show;
  }
  $omenu->set_menu($menu);
  $omenu->show;
  $selectionBox->pack_start($omenu, 0, 0, 0);

  $label = new Gtk::Label 'Camera Type';
  $label->show;
  $selectionBox->pack_start($label, 0, 0, 0);

  # create a popup menu for speed
  $omenu = new Gtk::OptionMenu;
  $menu = new Gtk::Menu;
  $submenu = undef;
  $menuItem = undef;

  setCameraType(undef,'DC210');
  foreach ('DC40','DC50','DC120','DC200','DC210')
  {
    $menuItem = new Gtk::RadioMenuItem "$_", $menuItem;
    $menuItem->signal_connect("activate", \&setCameraType, $_);
    $menu->append($menuItem);
    $menuItem->show;
  }
  $omenu->set_menu($menu);
  $omenu->show;
  $selectionBox->pack_start($omenu, 0, 0, 0);

  my $disconnectButton = new Gtk::Button "disconnect ...";
  my $connectButton = new Gtk::Button "connect ...";

  $disconnectButton->signal_connect(
	"clicked", \&disconnect,$connectButton);
  $connectButton->signal_connect(
	"clicked", \&connect,$disconnectButton);

  $disconnectButton->hide;
  $connectButton->show;


  $selectionBox->pack_start($disconnectButton, 0, 0, 0);
  $selectionBox->pack_start($connectButton, 0, 0, 0);
  $selectionBox->show;

  return $selectionBox;
}

sub deletePictures
{
  print STDERR "not implemented\n";
}

my $iAmIdle = 0;

sub idleNotify 
{
  print STDERR "-"; 
  $iAmIdle = 1;
}

sub processQueuedEvents
{
  $iAmIdle = 0;
  my $idle_tag = Gtk->idle_add(\&idleNotify);
  while (!$iAmIdle)
  {
     Gtk->main_iteration();
     print STDERR "+";
  }
  Gtk->idle_remove($idle_tag)
}

sub updateDownloadProgressBar
{
# notification called from DC210 during the download, updates the progress
# bar.  Throws some idle iteration in for processing events.
  my $pictureInfo = shift;
  my $amountRead = shift;

  if (defined($downloadProgressBar))
  {
    $downloadProgressBar->update(
	($totalBytesDownloaded + $amountRead) / $totalDownloadSize);

    # allow events to be processed (REDRAW)
    processQueuedEvents();
  }
}

sub cancelDownload
{
  my $this = shift;

  print STDERR "cannot cancel downloads yet.\n";
}

sub downloadPictures
{
# downloads the requested picture.
  my $this = shift;

  if (defined($dc)) 
  {
    if (defined($pictureList) && !defined($downloadDialog))
    {
      my @info = ();
      my @totalSize = ();

      $totalDownloadSize = 0;
      $totalBytesDownloaded = 0;

      createDownloadDialog();

      foreach ($pictureList->selection)
      {
        my $info = $dc->pictureInfo($_ + 1);
        $totalDownloadSize += $info->fileSize;

	push(@info,$info);
      }

      $dc->{'downloadNotify'} = \&updateDownloadProgressBar;

      my $numFiles = scalar(@info);
      my $currentFile = 1;
      foreach (@info)
      {
	$downloadLabel->set(
		"Downloading picture $currentFile of $numFiles\n" . 
		"Filename: " . $_->fileName . "\n");

        open(JPG,">" . $_->fileName);
        print JPG $dc->picture($_->pictureNumber);
        close(JPG);

        $currentFile++;
	$totalBytesDownloaded += $_->fileSize;
      }

      $dc->{'downloadNotify'} = undef;

      destroyDownloadDialog();
    }
  }
  else
  {
    notConnectedDialog();
  }
}

sub createMainWindow 
{
  my($box1,$button,$separator, $buffer, $label);

  my $window = Gtk::Window->new('toplevel');
  $window->set_name("main window");
  $window->set_uposition(20, 20);
  $window->set_usize(400, 200);

  $window->signal_connect("destroy" => \&Gtk::main_quit);
  $window->signal_connect("delete_event" => \&Gtk::false);

  # Create a vertical box for all components.
  $box1 = new Gtk::VBox(0, 0);
  $window->add($box1);
  $box1->show;

  $box1->pack_start(createCameraSelectionBox(), 0, 0, 0);
  $box1->pack_start(createPictureList(), 1, 1, 0);

  $separator = new Gtk::HSeparator;
  $box1->pack_start($separator, 0, 0, 0);
  $separator->show;

  my $box2 = new Gtk::HBox(0, 10);
  $box2->border_width(10);
  $box1->pack_start($box2, 0, 0, 0);
  $box2->show;

  $button = new Gtk::Button "Close";
  $button->signal_connect("clicked", \&do_exit);
  $box2->pack_start($button, 0, 0, 0);
  $button->can_default(1);
  $button->grab_default();
  $button->show;

  $button = new Gtk::Button "Download";
  signal_connect $button "clicked", \&downloadPictures;
  $box2->pack_start($button, 0, 0, 0);
  $button->show;

  $button = new Gtk::Button "Delete";
  signal_connect $button "clicked", \&deletePictures;
  $box2->pack_start($button, 0, 0, 0);
  $button->show;

  $button = new Gtk::Button "Status";
  $button->signal_connect("clicked", \&cameraStatus,"show");
  $box2->pack_start($button, 0, 0, 0);
  $button->show;

  $window->show;
}

sub do_exit 
{
  $dc = undef;
  Gtk->exit(0);
}

sub nonSupportedCameraDialog
{
  my($label, $button);
  
  if (not defined $nonSupportedCameraDialog) 
  {
    $cannotConnectDialog = new Gtk::Dialog;
    $cannotConnectDialog->signal_connect(
	"destroy", \&destroyWindow, \$nonSupportedCameraDialog);
    $cannotConnectDialog->signal_connect(
	"delete_event", \&destroyWindow, \$nonSupportedCameraDialog);
    $cannotConnectDialog->set_title("Camera Not Supported");
    $cannotConnectDialog->border_width(0);

    $label = new Gtk::Label "The selected camera is not supported\n" .
                            "by kdcpi\n";

    $label->set_padding(10, 10);
    $label->show;
    
    $button = new Gtk::Button "OK";
    $button->can_default(1);
    $button->signal_connect("clicked", \&nonSupportedCameraDialog);
    $button->grab_default;
    $button->show;

    $cannotConnectDialog->vbox->pack_start($label, 1, 1, 0);
    $cannotConnectDialog->action_area->pack_start($button, 1, 1, 0);
  }

  if (!$nonSupportedCameraDialog->visible) 
  {
    $nonSupportedCameraDialog->show;
  } 
  else 
  {
    $nonSupportedCameraDialog->destroy;
  }	
}

sub cannotConnectDialog
{
  my($label, $button);
  
  if (not defined $cannotConnectDialog) 
  {
    $cannotConnectDialog = new Gtk::Dialog;
    $cannotConnectDialog->signal_connect(
	"destroy", \&destroyWindow, \$cannotConnectDialog);
    $cannotConnectDialog->signal_connect(
	"delete_event", \&destroyWindow, \$cannotConnectDialog);
    $cannotConnectDialog->set_title("Camera Not Connected");
    $cannotConnectDialog->border_width(0);

    $label = new Gtk::Label "A connection with the camera could not\n" .
			    "be established.  Please check the port\n" .
			    "and speed settings to make sure they are\n" .
			    "correct ($port, $speed).\n";
    $label->set_padding(10, 10);
    $label->show;
    
    $button = new Gtk::Button "OK";
    $button->can_default(1);
    $button->signal_connect("clicked", \&cannotConnectDialog);
    $button->grab_default;
    $button->show;

    $cannotConnectDialog->vbox->pack_start($label, 1, 1, 0);
    $cannotConnectDialog->action_area->pack_start($button, 1, 1, 0);
  }

  if (!$cannotConnectDialog->visible) 
  {
    $cannotConnectDialog->show;
  } 
  else 
  {
    $cannotConnectDialog->destroy;
  }	
}

sub destroyDownloadDialog
{
  my($button);
  
  if (defined($downloadDialog))
  {
    $downloadDialog->destroy;

    $downloadDialog = undef
    $downloadLabel = undef;
    $downloadProgressBar = undef;
  }
}

sub createDownloadDialog
{
  my($button);
  
  if (not defined $downloadDialog) 
  {
    $downloadDialog = new Gtk::Dialog;
    $downloadDialog->signal_connect(
	"destroy", \&destroyDownloadDialog);
    $downloadDialog->signal_connect(
	"delete_event", \&destroyDownloadDialog);
    $downloadDialog->set_title("Downloading Pictures");
    $downloadDialog->border_width(0);

    $downloadLabel = new Gtk::Label "downloading images ...";
    $downloadLabel->set_padding(10, 10);
    $downloadLabel->show;

    $downloadProgressBar = new Gtk::ProgressBar;
    $downloadProgressBar->set_usize(200,20);
    $downloadProgressBar->show;

    my $box2 = new Gtk::HBox(0, 10);
    $box2->border_width(10);
    $box2->pack_start($downloadProgressBar, 0, 1, 0);
    $box2->show;

    $downloadDialog->vbox->pack_start($downloadLabel, 1, 1, 0);
    $downloadDialog->vbox->pack_start($box2, 0, 1, 0);
    
    $button = new Gtk::Button "Cancel";
    $button->can_default(1);
    $button->signal_connect("clicked", \&cancelDownload);
    $button->grab_default;
    $button->show;

    $downloadDialog->action_area->pack_start($button, 1, 1, 0);
    $downloadDialog->show;
  }
}

sub notConnectedDialog
{
  my($label, $button);
  
  if (not defined $notConnectedDialog) 
  {
    $notConnectedDialog = new Gtk::Dialog;
    $notConnectedDialog->signal_connect(
	"destroy", \&destroyWindow, \$notConnectedDialog);
    $notConnectedDialog->signal_connect(
	"delete_event", \&destroyWindow, \$notConnectedDialog);
    $notConnectedDialog->set_title("Camera Not Connected");
    $notConnectedDialog->border_width(0);

    $label = new Gtk::Label "A connection with the camera has not been " .
			    "established.";
    $label->set_padding(10, 10);
    $notConnectedDialog->vbox->pack_start($label, 1, 1, 0);
    $label->show;
    
    $button = new Gtk::Button "OK";
    $button->can_default(1);
    $button->signal_connect("clicked", \&notConnectedDialog);
    $button->grab_default;
    $button->show;

    $notConnectedDialog->action_area->pack_start($button, 1, 1, 0);
  }

  if (!$notConnectedDialog->visible) 
  {
    $notConnectedDialog->show;
  } 
  else 
  {
    $notConnectedDialog->destroy;
  }	
}

createMainWindow;

main Gtk;
exit;

