#!/usr/bin/perl

use strict;
use warnings;
use Archive::Zip;
use Text::Iconv;
use Math::BigInt;
use Date::Manip;

my $src = $ARGV[0];
my $dst = $ARGV[1];

die "usage: $0 source.pib dest.csv" if !defined( $src) or !defined($dst);

print STDERR "WARNING: dest \"$dst\" exists, possibly overwriting\n" if -e "$dst";
open my $outfile, ">", $dst or die "Couldn't open dest \"$dst\" for writing";

my $pib = Archive::Zip->new();
die("Couldn't open PIB file") unless $pib->read( $src ) == Archive::Zip::AZ_OK;

print "PIB file opened, searching for call-log...\n";
my @matches = $pib->membersMatching( 'clogs_.*\.csl' );
die("Couldn't find call-log inside file") unless @matches;
die("More than one call-log file found in file") if @matches > 1;

print "Call-log file found\n";
my $utf16_to_utf8 = Text::Iconv->new("UTF-16", "UTF-8");
my $calls = $pib->contents($matches[0]->{"fileName"});
$calls = $utf16_to_utf8->convert( $calls );

sub parse_csv ($) {
	my @out;
	my @tmp;

	while( $_[0] =~ m{
			\G # Start where you left off, don't auto-skip
			(?:;|^|(\x0d\x0a?|\x0a)) # fields start here
			(?: # first try to match quoted ones!
			  "(
			   (?:[^"\\]|\\.)* # non-" non-\ or escape-sequence
			  )"
			  | # or unquoted
			  ([^";\x0d\x0a]*)
			)
			}mgx ) {
		if( defined $1 ) {
			push @out, [ @tmp ];
			@tmp = ();
		}
		if( defined $2 ) {
			my $tmp = $2;
			$tmp =~ s/\\(.)/$1/g;
			push @tmp, $tmp;
		}
		push @tmp, $3 if defined $3;
	}
	push @out, [ @tmp ] if @tmp;

	return @out;
}

my @call = parse_csv($calls);

shift @call; # first 2 lines contain no data
shift @call;
pop @call;  # last line is empty

print $outfile '"Call Start";"Call End";"In/Out/Missed";"Number";"Name"', "\n";

my $i = 0;
for my $c (@call) {
	# 0  ???
	# 1  ??? always "0x02,0x00,0x01,0x00"
	# 2  ??? always "0x40,0x00,0x02,0x00"
	# 3  ??? always "0x40,0x00,0x03,0x00"
	# 4  ??? always "0x03,0x00,0x04,0x00"
	# 5  ??? "0x1f,0x00,0x06,0x00" or "0x03,0x00,0x09,0x00" (skip col 6-9, final 0 is at 14) 
	# 6  ??? "0x1f,0x00,0x07,0x00" or "0x03,0x00,0x09,0x00" (skip col 7-9, final 0 is at 15)
	# 7  ??? "0x1f,0x00,0x0a,0x00" or "0x03,0x00,0x09,0x00" (skip col 8-9, final 0 is at 16)
	# 8  ??? "0x41,0x00,0x08,0x00" or "0x03,0x00,0x09,0x00" (skip col 9  , final 0 is at 17)
	# 9  ??? always "0x03,0x00,0x09,0x00" (or skipped)
	# 10  ??? always 1
	# 11  ??? start-timestamp???
	# 12  ??? end-timestamp???
	# 13  ??? 2055/7 (called), 2052/4 (missed), 2054/6 (received), 2053/5 (called)
	# 14  number
	# 15  Name / hex-string for voicemail
	# 16  {m,h,pgr,w,...} (missing for voicemail calls)
	# 17  ???
	# 18  ??? always 0

	shift @{$c}; # get rid of the field 0-4
	shift @{$c};
	shift @{$c};
	shift @{$c};
	shift @{$c};
	
	my $state = 5;
	my $endat = 18;
	while( $state < $endat ) {
		my $temp = shift @{$c};
		if( $state <= 9 ) {
			if( $temp =~ m/0x03,0x00,0x09,0x00/ ) {
				$endat = 9 + $state;
				$state = 10;
			} else {
				$state++;
			}
		} elsif( $state == 10 ) {
			if( $temp ne "1" ) {
				die "field 10 should always be \"1\", input line $i (+2):\n", @{$call[$i]};
			}
			$state++;
		} elsif( $state == 11 or $state == 12 ) {
			$state++;
			my $t = Math::BigInt->new( "0x" . join '', reverse map { s/0x//; $_ } split /,/, $temp );
			# $t contains the number of 100ns since 1601-01-01T01:00:00
			$t->bdiv(10000000);   # convert to seconds
			my $date = DateCalc("1 jan 1601 01:00:00", "+ " . $t->bstr() );
			print $outfile "\"", UnixDate($date, "%Y-%m-%d %H:%M:%S"), "\";";
		} elsif( $state == 13 ) {
			$state++;
			print $outfile '"missed";' if $temp eq '4' || $temp eq '2052';
			print $outfile '"out";' if $temp eq '5' || $temp eq '2053';
			print $outfile '"in";' if $temp eq '6' || $temp eq '2054';
			print $outfile '"out";' if $temp eq '7' || $temp eq '2055';
		} elsif( $state == 14 ) {
			$state++;
			print $outfile "\"$temp\";";
		} elsif( $state == 15 ) {
			$state++;
			print $outfile "\"$temp";
			if( $endat > 17 ) {
				$temp = shift @{$c};
				$state++;
				print $outfile " ($temp)\";";
			} else {
				print $outfile "\";";
			}
		} else {
			$state++;
		}
	}
	print $outfile "\n";
	$i++;
}
