
package Palm::Progect::Record;


use base 'Palm::Progect::VersionDelegator';
use Carp;
use strict;

use CLASS;
use base qw(Class::Accessor);

sub Accessors {
    qw(
        description
        note
        type
        priority
        completed
        completed_actual
        completed_limit
        level
        has_next
        has_child
        has_prev
        is_opened
        has_todo
        date_due
        todo_link_data
    )
}

CLASS->mk_accessors(CLASS->Accessors);

my %Categories = ('Unfiled' => 0);  # name => id

# Class method to set all available category names at once,
# using the Palm::StdAppinfo format of a list of hashrefs
# in the form of:
#     { name => 'Some cat', id => 7, renamed => 'who cares' }
#
sub set_categories {
    my $class = shift;

    my @categories = @_;

    if (!@categories and $categories[0] !~ /^\s*unfiled\s*$/i ) {
        unshift @categories, 'Unfiled';
    }

    %Categories = ();

    # I don't know what the category ids are used
    # for, considering the 'category number'
    # associated with a pdb record refers to
    # the category's *position* within this array,
    # not its 'id'.

    for (my $id = 0; $id < @categories; $id++) {

        my $name  = $categories[$id]{'name'};

        next unless $id;   # Skip 'Unfiled'
        next unless $name; # Skip blank categories

        $Categories{$name} = $id;
    }

    # Add the 'Unfiled' category, which is
    # always zero.
    $Categories{'Unfiled'} = 0;
}

# Class method to get all available category names, in
# the order of their category ids
sub get_categories {
    my $class = shift;

    # Since the keys and values are both meant to be
    # unique, we can reverse the %Categories hash:

    my %categories_by_id = reverse %Categories;

    my @categories;
    for my $id (sort { $a <=> $b } keys %categories_by_id) {
        push @categories, {
            id      => $id,
            name    => $categories_by_id{$id},
            renamed => 0,
        };
    }

    return @categories if wantarray;
    return \@categories;
}

# object method accessor
sub category_name {
    my $self = shift;
    if (defined $_[0]) {
        my $category_name = $_[0];

        $self->{category_name} = $category_name;

        if (not exists $Categories{$category_name}) {
            # Put this category_name in the Class-global %Categories
            # hash, setting it's category_id to the max number
            # of categories that are already there, plus one
            $Categories{$category_name} = (scalar keys %Categories);
        }
    }
    if ($self->{category_name} and $self->{category_name} eq 'Unfiled') {
        return '';
    }
    else {
        return $self->{category_name};
    }
}

# In order to assign a category id to a record, the category
# must already exist; i.e. it must have been set via
# the class methods set_categories or add_categories

sub category_id {
    my $self        = shift;
    my $category_id = shift;

    if (defined $category_id) {


        # Since both keys and values of %Categories are unique,
        # we can reverse the hash...
        my %cat_lookup = reverse %Categories;

        # Internally, we only maintain the category_name
        # and we lookup the id if its requested.
        # So if someone sets the category id, we actually
        # look up the category_name for that id and store it instead

        if (exists $cat_lookup{$category_id}) {
            $self->{'category_name'} = $cat_lookup{$category_id};
        }
        else {
            croak "There is no category with the id #$category_id.  Before setting a record's category call Palm::Progect::Records->set_categories with the complete list of categories\n";
        }
    }

    my $cat_name = $self->{'category_name'} || 'Unfiled';
    return $Categories{$cat_name};
}

1;

__END__

=head1 NAME

Palm::Progect::Record - Individual Records of the Progect Database

=cut

=head1 SYNOPSIS

    for my $rec (@{ $progect->records }) {
        my $description = $rec->description;
        my $priority    = $rec->priority;
        my $category    = $rec->category_name;
        print "[$priority] {$category} $description\n";
    }

=head1 DESCRIPTION

Each L<Palm::Progect> object contains a list of records in its C<records> method.

Each record is a C<Palm::Progect::Record> object.

=head1 METHODS

=over 4

=item description

The text of the record

=item note

The note attached to the record, if any.  If the record does not have a
note, then this value will be false.

=item type

The type of the record

Type will be one of the six types of records:

=over 4

=item RECORD_TYPE_PROGRESS

=item RECORD_TYPE_NUMERIC

=item RECORD_TYPE_ACTION

=item RECORD_TYPE_INFO

=item RECORD_TYPE_EXTENDED

=item RECORD_TYPE_LINK

=back

These symbols are available from the C<Palm::Progect::Constants> module.

For instance:

    use Palm::Progect::Constants;

    if ($rec->type == RECORD_TYPE_ACTION) {
        print "The record is an action item!\n";
    }

=item priority

Priority of the item, 1 - 5, or 0, for B<No priority>.

=item completed

The value of this field depends on the type of the record.

For action items, completed is B<true> if the record has been completed,
C<false> otherwise.

For progress items, completed is the percent complete, a value from 0-100.

For numeric items (e.g. 5/20), completed reflects the percent complete
(see C<completed_actual> and C<completed_limit>, below).  Note that
setting the C<completed> method will not affect C<completed_actual> or
C<completed_limit>.

=item completed_actual

=item completed_limit

Numeric items indicate their 'completeness' with an arbitrary ratio:
Some value out of another value, for instance 5 out of 20, or 5/20,
where 5 is the C<completed_actual> and 20 is the C<completed_limit>.

=item level

The indent level of the record.

=item has_next

True if the record has another record following it, false otherwise.

=item has_child

True if the record contains another record, false otherwise.

=item has_prev

True if the record follows another record, false otherwise.

=item is_opened

True if the record is open, revealing the records it contains, false otherwise.

=item has_todo

True if the record links to a record in the todo database, false otherwise.

=item date_due

The due date of the record, in unix time format (i.e. seconds since the epoch).
If the record does not have a due date then this value will be false.

=item category_name

The name of the record's category, if any.  If you set C<category_name>
to the name of a category that doesn't yet exist, then that category will
be created.

=back

=head1 AUTHOR

Michael Graham E<lt>mag-perl@occamstoothbrush.comE<gt>

Copyright (C) 2002 Michael Graham.  All rights reserved.
This program is free software.  You can use, modify,
and distribute it under the same terms as Perl itself.

The latest version of this module can be found on http://www.occamstoothbrush.com/perl/

=head1 SEE ALSO

progconv

L<Palm::PDB(3)>

http://progect.sourceforge.net/

=cut
