The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package WebService::Simplenote::Note;

# ABSTRACT: represents an individual note

# TODO: API support for tags

use v5.10;
use Moose;
use MooseX::Types::DateTime qw/DateTime/;
use WebService::Simplenote::Note::Meta::Types;
use WebService::Simplenote::Note::Meta::Attribute::Trait::NotSerialised;
use Method::Signatures;
use DateTime;
use JSON qw//;
use Log::Any qw//;
use namespace::autoclean;

around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;
    
    if ( @_ == 1 && !ref $_[0] ) {
        my $note = JSON->new->utf8->decode($_[0]);
        return $class->$orig( $note );
    }
    else {
        return $class->$orig(@_);
    }
};

has logger => (
    is       => 'ro',
    isa      => 'Object',
    lazy     => 1,
    required => 1,
    default  => sub { return Log::Any->get_logger },
    traits   => [qw/NotSerialised/],
);

# set by server
has key => (
    is  => 'rw',
    isa => 'Str',
);

# set by server
has [ 'sharekey', 'publishkey' ] => (
    is  => 'ro',
    isa => 'Str',
);

has title => (
    is  => 'rw',
    isa => 'Str',
);

has deleted => (
    is      => 'rw',
    isa     => 'Bool',
    lazy    => 1,
    default => 0,
);

# XXX should default to DateTime->now?
has [ 'createdate', 'modifydate' ] => (
    is     => 'rw',
    isa    => DateTime,
    coerce => 1,
);

# set by server
has [ 'syncnum', 'version', 'minversion' ] => (
    is  => 'rw',
    isa => 'Int',
);

has tags => (
    is      => 'rw',
    traits  => ['Array'],
    isa     => 'ArrayRef[Str]',
    default => sub { [] },
    handles => {
        add_tag     => 'push',
        join_tags   => 'join',
        has_tags    => 'count',
        has_no_tags => 'is_empty',
    },
);

has systemtags => (
    is      => 'rw',
    traits  => ['Array'],
    isa     => 'ArrayRef[SystemTags]',
    default => sub { [] },
    handles => {
        set_markdown => [ push  => 'markdown' ],
        is_markdown  => [ first => sub { /^markdown/ } ],
        set_pinned   => [ push  => 'pinned' ],
        join_systags   => 'join',
        has_systags    => 'count',
        has_no_systags => 'is_empty',
    },
);

# XXX: always coerce to utf-8?
has content => (
    is      => 'rw',
    isa     => 'Str',
    trigger => \&_get_title_from_content,
);

method serialise {
    $self->logger->debug('Serialising note using: ', JSON->backend);
    my $json = JSON->new;
    $json->allow_blessed;
    $json->convert_blessed;
    my $serialised_note = $json->utf8->encode($self);

    return $serialised_note;
}

method TO_JSON {
    my %hash;
    for my $attr ( $self->meta->get_all_attributes ) {
        next if $attr->does('NotSerialised');
        my $reader = $attr->get_read_method;
        if (defined $self->$reader) {
            $hash{$attr->name} = $self->$reader;
        }
    }

    # convert dates, if present
    if (exists $hash{createdate}) {
        $hash{createdate} = $self->createdate->epoch;
    }
    
    if (exists $hash{modifydate}) {
        $hash{modifydate} = $self->modifydate->epoch;
    }
    
    return \%hash;
}

sub _get_title_from_content {
    my $self = shift;

    my $content = $self->content;

    # First line is title
    $content =~ /(.+)/;
    my $title = $1;

    # Strip prohibited characters
    # XXX preferable encoding scheme?
    chomp $title;

    # non-word chars to space
    $title =~ s/\W/ /g;

    # trim leading and trailing spaces
    $title =~ s/^\s+//;
    $title =~ s/\s+$//;

    $self->title( $title );

    return 1;
}

__PACKAGE__->meta->make_immutable;

1;

=head1 SYNOPSIS

  use WebService::Simplenote::Note;

  my $note = WebService::Simplenote::Note->new(
      content => "Some stuff",
  );

  printf "[%s] %s\n %s\n",
      $note->modifydate->iso8601,
      $note->title,
      $note->content;
  }

=head1 DESCRIPTION

This class represents a note suitable for use with Simplenote. You should read the 
L<Simplenote API|http://simplenoteapp.com/api/> docs for full details

=head1 METHODS

=over

=item WebService::Simplenote::Note->new($args)

The minimum required attribute to set is C<content>.

=item add_tag($str)

Push a new tag onto C<tags>.

=item set_markdown

Shortcut to set the C<markdown> system tag.

=item set_pinned

Shortcut to set the C<pinned> system tag.

=back

=head1 ATTRIBUTES

=over

=item logger

L<Log::Any> logger

=item key

Server-set unique id for the note.

=item title

Simplenote doens't use titles, so we autogenerate one from the first line of content.

=item deleted

Boolean; is this note in the trash?

=item createdate/modifydate

Datetime objects

=item tags

Arrayref[Str]; user-generated tags.

=item systemtags

Arrayref[Str]; special tags.

=item content

The body of the note