#!/usr/bin/perl

# EPG (Electronic Program Guide Decoder)
# Commercial Name: nexTView

use Video::Capture::V4l;
use Video::Capture::VBI;

package Video::Capture::VBI::EPG;

use Video::Capture::VBI qw(:DEFAULT %VPS_CNI %VT_NI);
use Storable;

use base 'Video::Capture::VBI::VT';

sub new {
   my $class = shift;
   my $self = $class->SUPER::new(@_);
   $self;
}

my $lastcni = -1;
sub feed {
   my $self = shift;
   my @r;
   for ($self->SUPER::feed(@_)) {
      if ($_->[0] == VBI_VPS) {
         if ($_->[3] != $lastcni) {
            $lastcni = $_->[3];
            printf "CNI %04x (%s)\n", $_->[3], $VPS_CNI{$_->[3] & 0xfff};
         }
      } else {
         push @r, $_;
      }
   }
   @r;
}

sub mydump {
   local $_ = shift;
   if (ref $_ eq "HASH") {
      print "{ ";
      while(my($k,$v)=each %$_) {
         print ", $k => ";
         mydump($v);
      }
      print " }";
   } elsif (ref $_ eq "ARRAY") {
      print "[ ";
      for (@$_) {
         print ", ";
         mydump($_);
      }
      print " ]";
   } else {
      y/\x20-\x7e//cd;
      print $_;
   }
}

sub set_epg {
   my $self = shift;
   my $name = shift;
   my $new_db = main::gen_db_name ($name);
   print "SET_EPG $new_db <= ", $self->{db_name}, "\n";
   if ($self->{db_name} and $new_db eq $self->{db_name}) {
      print "saving ",$new_db,"...";
      Storable::nstore($self->db, "$new_db~");
      rename "$new_db~", $new_db;
      print "ok\n";
   } else {
      $self->{db_name} = $new_db;
      print "trying to load $new_db...";
      $self->db(eval { $self->db(Storable::retrieve($new_db)) });
      print "ok\n";
   }
}

# just display the page
sub enter_page {
   my($self,$page)=@_;

   $pages++;

   return unless $page->{page} == 0x1df;

   my $sub = $page->{ctrl};
   my $stream = VTX_S3($sub);
   my $height = VTX_S4($sub)<<3 | VTX_S2($sub);
   my $seq    = VTX_S1($sub);

   my $s = \%{$self->{stream}{$stream}};

   delete $s->{raw} unless (($seq-1)&15) == $s->{seq};
   $s->{seq}=$seq;
   push @{$s->{raw}}, @{$page->{packet}}[1..$height];

   $packets[$stream]++;
   #print "stream $stream, height $height, seq $seq, ",scalar @{$s->{raw}},"<\n";

   for(decode_stream $s->{raw})  {
      if (my ($dt, $bi) = decode_block $_, \@{$self->{bundle}}) {
         if ($dt == 1) {
            my $ai = $self->{db}{ai};
            $self->set_epg($bi->{service_name});
            if ($ai) {
               print "$ai->{epg_version}, $ai->{epg_version_swo}, $ai->{this_network_op}, $ai->{service_name}\n";
               if ($bi->{epg_version} != $ai->{epg_version}
                   || $bi->{epg_version_swo} != $ai->{epg_version_swo}
                   || $bi->{this_network_op} != $ai->{this_network_op}) {
                  print "CHANNEL SWITCH, WIPING DATABASE\n";
                  delete $self->{db};
               }
            }
            $self->{db}{ai} = $bi;
         } elsif ($dt == 2) {
            $self->{db}{pi}{$bi->{netwop_no}}{$bi->{block_no}} = $bi;
         } elsif ($dt == 3) {
            $self->{db}{ni}{$bi->{block_no}} = $bi;
         } elsif ($dt == 4) {
            $self->{db}{oi}{$bi->{block_no}} = $bi;
         } elsif ($dt == 5) {
            $self->{db}{mi}{$bi->{block_no}} = $bi;
         } else {
            print "UNKNOWN BLOCK TYPE $dt found\n";
         }
      }
   }
   $|=1;
   printf "%7d %6d %6d %5d %5d %5d %5d\r", $pages, $packets[0], $packets[1],
          1*%{$self->{db}{pi}},
          1*%{$self->{db}{ni}},
          1*%{$self->{db}{oi}},
          1*%{$self->{db}{mi}};

   #printf "received epg page %04x\n", $sub;
}

sub enter_packet {
   my $self = shift;
   my $p = shift;
   return unless $p->[3] <= 1 && $p->[2] == 30;
   #printf "packet(@$p %04x)", $p->[6];
   print "\n";
}

sub db {
   my $self = shift;
   $self->{db} = shift if @_;
   $self->{db};
}

package main;

$vbi = new Video::Capture::V4l::VBI or die;

# the next line is optional (it enables buffering)
$vbi->backlog(150); # max. 5 seconds backlog (~4.6Mb)

$inp_fd = fileno STDIN;
$vbi_fd = $vbi->fileno;

$vt = new Video::Capture::VBI::EPG;

sub gen_db_name {
   shift() . ".epg";
}

print "Capturing VBI block. Make sure you have tuned in to a channel with EPG!\n";
print "Aquisition may take up to twenty minutes (and more)!\n";

for(;;) {
   my $r="";
   vec($r,$inp_fd,1)=1;
   vec($r,$vbi_fd,1)=1;
   select $r,undef,undef,0.04;
   $vt->feed(decode_field $vbi->field, VBI_VT|VBI_VPS) while $vbi->queued;
   if (vec($r,$inp_fd,1)) {
      $_ = <STDIN>;
      if (/^q/i) {
         last;
      }
   }
}

