#! /usr/bin/perl -w

use strict;
use warnings;
use utf8;

## IMPORTANT ##
# When Net::Twitter::Lite encounters a Twitter API error or a network error, 
# it throws a Net::Twitter::Lite::Error object. 
# You can catch and process these exceptions by using eval blocks and testing $@
## from http://search.cpan.org/perldoc?Net::Twitter::Lite#ERROR_HANDLING
use Net::Twitter::Lite::WithAPIv1_1;
use FindBin qw($Bin);
use YAML::Tiny;
use Data::Dumper;
use Encode;

my $help = sub {
    die <<EOM;
usage: $0
    [{u}ser_timeline(default)|{m}sentions|{s}earch
        [screen_name
            [number_of_pages|all
                [dump]
            ]
        ]
    ]
EOM
};
if ($ARGV[0] && ($ARGV[0] eq '--help' || $ARGV[0] eq '-h') ) {
    &{$help};
}

my $method = $ARGV[0] || 'user_timeline';
my $screen_name = $ARGV[1] || '';
my $pages = $ARGV[2] || 1;
if ($pages eq 'all') {
    $pages = -1;
}
my $dump = $ARGV[3] || 0;

my $conf = loadconf("$Bin/config.yml");
if (! defined $conf) {
    die "$0: cannot parse config file.";
}

my $bot = login($conf);
if (! $bot->authorized) {
    die "$0: this client is not yet authorized.";
}


eval {
    my $page = 0;
    while ($pages - $page && $page <= 20) {
        $page++;
        
        my $param = ($screen_name)
            ? { page => $page, screen_name => $screen_name, count => 200, }
            : { page => $page, count => 200, }
        ;
    
        my $res;
        if ($method eq 'user_timeline' || $method eq 'u') {
            $res = $bot->user_timeline($param);
        }
        elsif ($method eq 'mentions' || $method eq 'm') {
            $res = $bot->mentions($param);
        }
        elsif ($method eq 'search' || $method eq 's') {
            my $key;
            foreach my $word (@{ $conf->{hashtag} }) {
                if ($key) {
                    $key .= " OR $word";
                }
                else {
                    $key = $word;
                }
            }
            $param->{q} = $key;
            $res = $bot->search($param)->{results};
        }
        else {
            warn "$0: unknown method '$method'";
            &{$help};
        }
        
        if ($dump) {
            foreach my $line (split /\n/, Dumper $res) {
                if ($line =~ /undef/) { next; }
                print $line, "\n";
            }
        }
        else {
            foreach my $status (@{$res}) {
                my $text = "";
                $text .= "(". $status->{id} . ") ";
                $text .= ($status->{user}{screen_name}) ?
                    $status->{user}{screen_name} : $status->{from_user};
                $text .= "|";
                $text .= ($status->{user}{name}) ?
                    $status->{user}{name} : $status->{from_user_name};
                $text .= " [" . $status->{created_at} . "]";
                $text .= " ".  $status->{text};
                $text =~ s/\n//;
                print encode('utf8', $text), "\n";
            }
        }
    }
};
if ($@) {
    evalrescue($@);
}


sub loadconf {
    # load configration data from yaml formatted file
    #   param   => scalar string of filename
    #   ret     => hash object of yaml data
    
    my $file = shift @_;
    
    my $yaml = YAML::Tiny->read($file);
    
    if ($!) {
        warn "$0: '$file' $!";
    }
    
    return $yaml->[0];
}

sub login {
    # make Net::Twitter::Lite::WithAPIv1_1 object and login
    #   param   => hash object of configration
    #   ret     => Net::Twitter::Lite::WithAPIv1_1 object
    
    my $conf = shift @_;
    
    my $bot = Net::Twitter::Lite::WithAPIv1_1->new(
        consumer_key    => $conf->{consumer_key},
        consumer_secret => $conf->{consumer_secret},
        legacy_lists_api => 0,
    );
    
    $bot->access_token($conf->{access_token});
    $bot->access_token_secret($conf->{access_token_secret});
    
    return $bot;
}

sub evalrescue {
    # output error message at eval error
    
    use Scalar::Util qw(blessed);
    
    if (blessed $@ && $@->isa('Net::Twitter::Lite::Error')) {
        warn $@->error;
        if ($@->twitter_error) {
            my $twitter_error = $@->twitter_error;
            if (defined $twitter_error->{errors}) {
                foreach my $error (@{$twitter_error->{errors}}) {
                    warn "code => "   , $error->{code}, "\n";
                    warn "message => ", $error->{message}, "\n";
                }
            }
            else {
                # unknown HASH structure
                use Data::Dumper;
                warn Dumper $twitter_error;
            }
        }
    }
    else {
        warn $@;
    }
}
