#! /usr/bin/perl -s

use FIT;

$semicircles_to_deg = 1 if !defined $semicircles_to_deg;
$mps_to_kph = 1 if !defined $mps_to_kph;
$use_gmtime = 0 if !defined $use_gmtime;
$show_version = 0 if !defined $show_version;

my $version = "0.04";

if ($show_version) {
  print $version, "\n";
  exit;
}

sub dump_it {
  my ($self, $desc, $v) = @_;

  print "Local message type: $desc->{local_message_type} (";
  print 'message name: ', $desc->{message_name}, ', ' if $desc->{message_name};
  print "message number: $desc->{message_number})\n";
  $self->print_all_fields($desc, $v, indent => '  ');
}

sub fetch_from {
  my $fn = shift;
  my $obj = new Garmin::FIT;

  $obj->semicircles_to_degree($semicircles_to_deg);
  $obj->mps_to_kph($mps_to_kph);
  $obj->use_gmtime($use_gmtime);
  $obj->file($fn);
  $obj->data_message_callback_by_name('', \&dump_it);

  unless ($obj->open) {
    print STDERR $obj->error, "\n";
    return;
  }

  my ($fsize, $proto_ver, $prof_ver, $h_extra, $h_crc_expected, $h_crc_calculated) = $obj->fetch_header;

  unless (defined $fsize) {
    print STDERR $obj->error, "\n";
    $obj->close;
    return;
  }

  my ($proto_major, $proto_minor) = $obj->protocol_version_major($proto_ver);
  my ($prof_major, $prof_minor) = $obj->profile_version_major($prof_ver);

  printf "File size: %lu, protocol version: %u.%02u, profile_verion: %u.%02u\n", $fsize, $proto_major, $proto_minor, $prof_major, $prof_minor;

  if ($h_extra ne '') {
    print "Extra octets in file header";

    my ($i, $n);

    for ($i = 0, $n = length($h_extra) ; $i < $n ; ++$i) {
      print "\n  " if !($i % 16);
      print ' ' if !($i % 4);
      printf " %02x", ord(substr($h_extra, $i, 1));
    }

    print "\n";
  }

  if (defined $h_crc_calculated) {
    printf "File header CRC: expected=0x%04X, calculated=0x%04X\n", $h_crc_expected, $h_crc_calculated;
  }

  1 while $obj->fetch;

  print STDERR $obj->error, "\n" if !$obj->EOF;
  printf "CRC: expected=0x%04X, calculated=0x%04X\n", $obj->crc_expected, $obj->crc;

  my $garbage_size = $obj->trailing_garbages;

  print "Trainling $garbage_size octets garbages skipped\n" if $garbage_size > 0;
  $obj->close;
}

if (@ARGV > 1) {
  do {
    print "***** $ARGV[0] *****\n";
    &fetch_from(shift @ARGV);
  } while (@ARGV);
}
elsif (@ARGV) {
  &fetch_from($ARGV[0]);
}
else {
  &fetch_from('-');
}

1;
__END__

=head1 NAME

Fitdump - Show contents of Garmin .FIT files

=head1 SYNOPSIS

  fitdump -show_version=1
  fitdump [-semicircles_to_deg=(0|1)] [-mps_to_kph=(0|1)] [-use_gmtime=(0|1)] [<file> ... ]

=head1 DESCRIPTION

B<Fitdump> reads the contents of Garmin .FIT files given on command line
(or standard input if no file is specified),
and print them in (hopefully) human readable form.

=for html The latest version is obtained via

=for html <blockquote>

=for html <!--#include virtual="/cgi-perl/showfile?/cycling/pub/fitdump-[0-9]*.tar.gz"-->.

=for html </blockquote>

It uses a Perl class

=for html <blockquote><a href="GarminFIT.shtml">

C<Garmin::FIT>.

=for html </a></blockquote>

of version 0.04 or later.
The main role of this program is to give a sample application of the class.

=head1 AUTHOR

Kiyokazu SUTO E<lt>suto@ks-and-ks.ne.jpE<gt>

=head1 DISCLAIMER etc.

This program is distributed with
ABSOLUTELY NO WARRANTY.

Anyone can use, modify, and re-distibute this program
without any restriction.

=head1 CHANGES

=head2 0.03 --E<gt> 0.04

=over 4

=item C<fetch_from()>

follows protocol version 1.2.

=back

=head2 0.02 --E<gt> 0.03

=over 4

=item C<fetch_from()>

should call the method C<close()> when C<fetch_header()> failed.

=back

=head2 0.01 --E<gt> 0.02

=over 4

=item C<init_it()>

removed.

=item C<dump_it()>

rewrite with new method C<print_all_fields> of C<Garmin::FIT>.

=item C<fetch_from()>

renamed from C<fetch_all()>.

rewrite with new methods of C<Garmin::FIT>.

=back

=cut
