#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use IO::Async::Loop;
use Net::Async::CassandraCQL;

use List::Util qw( max );
use POSIX qw( strftime );
use Term::ReadLine;

use Data::Dump 'pp';

use Protocol::CassandraCQL qw( CONSISTENCY_ONE );

my $loop = IO::Async::Loop->new;
my $cassie = Net::Async::CassandraCQL->new(
   host => "localhost"
);
$loop->add( $cassie );

my $term = Term::ReadLine->new( "cqlsh" );

$cassie->connect->get;

my $keyspace;

if( @ARGV ) {
   my $want = shift @ARGV;
   ( undef, $keyspace ) = $cassie->query( "USE $want;", CONSISTENCY_ONE )->get;
}

while( defined( my $query = $term->readline(sprintf "\ncql%s> ", defined $keyspace ? ":$keyspace" : "") ) ) {
   my $f = $cassie->query( $query, CONSISTENCY_ONE );
   $f->await;

   if( $f->failure ) {
      print $f->failure . "\n";
      next;
   }

   my ( $type, $result ) = $f->get or next;

   if( $type eq "keyspace" ) {
      $keyspace = $result;
   }
   elsif( $type eq "schema_change" ) {
      print join( " ", @$result ) . "\n";
   }
   elsif( $type eq "rows" ) {
      # TODO: Text::Table?

      my @columns; # formatted, one ARRAY per column, containing row data
      foreach my $i ( 0 .. $result->rows - 1 ) {
         my $d = $result->row_array( $i );
         foreach my $col ( 0 .. $#$d ) {
            $columns[$col][$i] = "", next if !defined $d->[$col];

            my $type = $result->column_type( $col );
            my $str;
            if( $type eq "BOOLEAN" ) {
               $str = $d->[$col] ? "true" : "false";
            }
            elsif( $type eq "BLOB" ) {
               $str = unpack "H*", $d->[$col];
            }
            elsif( $type eq "DOUBLE" ) {
               $str = sprintf "%.9g", $d->[$col];
            }
            elsif( $type eq "FLOAT" ) {
               $str = sprintf "%.5g", $d->[$col];
            }
            elsif( $type eq "TIMESTAMP" ) {
               my $sec  = int( $d->[$col] );
               my $msec = 1000 * ( $d->[$col] - $sec );
               $str = strftime( "%Y-%m-%d %H:%M:%S", localtime $sec ) . sprintf ".%03d", $msec;
            }
            elsif( $type eq "ASCII" or $type eq "VARCHAR" ) {
               $str = qq("$d->[$col]"); # TODO - neater somehow
            }
            else {
               $str = $d->[$col];
            }

            $columns[$col][$i] = $str;
         }
      }
      my @colwidths = map {
         max map { length } $result->column_shortname($_),
                            $result->column_type($_),
                            @{ $columns[$_] }
      } 0 .. $#columns;

      # Column names
      print join( " | ", map { sprintf "%-*s", $colwidths[$_], $result->column_shortname($_) } 0 .. $#columns ) . "\n";

      # Column types
      print join( " | ", map { sprintf "%-*s", $colwidths[$_], scalar $result->column_type($_) } 0 .. $#columns ) . "\n";

      # Divider
      print join( "-+-", map { "-" x $colwidths[$_] } 0 .. $#columns ) . "\n";

      # Row data
      foreach my $i ( 0 .. $result->rows - 1 ) {
         print join( " | ", map { sprintf "%-*s", $colwidths[$_], $columns[$_][$i] } 0 .. $#columns ) . "\n";
      }
   }
   else {
      print pp($result) . "\n";
   }
}

print "\n"; # clear the last prompt
