# vim: set cindent expandtab ts=4 sw=4:
#
# Copyright (c) 1998-2005 Chi-Keung Ho. All rights reserved.
#
# This programe is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# Extmail - a high-performance webmail to maildir
# $Id$
package Ext::Abook;

use Exporter;
use Ext::CSV;
use Fcntl qw(:flock);

use vars qw(@ISA @ABOOK $REF);
@ISA = qw(Exporter);
undef @ABOOK;
$REF = undef;

sub new {
    my $class = shift;
    my %opt = @_;

    my $self = {
        file => $opt{file} ? $opt{file} : 'abook.cf',
        lock => $opt{lock} ? 1:0,
        sort => $opt{sort} ? $opt{sort} : 'by_mailaddr',
    };

    bless $self, $class;

    $REF = new Ext::CSV unless($REF);
    $self->parse; # XXX auto 
    $self;
}

sub parse {
    my $self = shift;
    my $file = $self->{file};
    my $count = 0;
    open(FD, "< $file") or
        warn "$file not exists, abort\n" and return "";
    while(<FD>) {
        chomp;

        if ($REF->parse($_)) {
            my @field = $REF->fields;
            $ABOOK[$count] = \@field;
            $count ++;
        }else {
            warn $REF->error_input;
        }
    }

    close FD;
}

# CSV format:
#
# 'ID', 'Name', 'Email', 'Company', 'Mobile'
#   0      1       2         3         4

sub by_name {
    $a->[1] cmp $b->[1];
}

sub by_mailaddr {
    $a->[2] cmp $b->[2];
}

sub by_mobile {
    $a->[4] <=> $b->[4];
}

sub by_company {
    $a->[3] cmp $b->[4];
}

sub dump {
    shift;
    return \@ABOOK;
}

sub sort {
    my $self = shift;
    my @mybuf = @ABOOK; # deep copy

    for (0...scalar @mybuf-1) {
        $mybuf[$_] = [$_, @{$mybuf[$_]}];
    }
    shift @mybuf; # ignore the first line

    # sort it now
    my $method = $self->{sort};

    eval { @mybuf = sort $method @mybuf; };
    if ($@) {
        # default sort method
        @mybuf = sort by_mailaddr @mybuf;
    }
    unshift @mybuf, [0, 'Name', 'Email', 'Company', 'Mobile'];
    \@mybuf; # sorted array
}

sub search {
    my $self = shift;
    my $key = $_[0];
    my @id;
    my $ref = $self->sort;

    foreach(my $k=1; $k < scalar @$ref; $k++) {
        # join to a big string
        my $lid = $ref->[$k]->[0]; # line ID
        my $s = join('', splice(@{$ref->[$k]}, 1)); # ignore the first id
        if($s=~/$key/i) {
            push @id, $lid;# return $k only?
        }
    }
    @id;
}

# Schema: field0,1,2,3,4 => id, name, mailaddr, company, mobile
sub lookup {
    my $self = shift;
    return $ABOOK[$_[0]] if($ABOOK[$_[0]]);
    "";
}

sub delete {
    my $self = shift;
    my @id = @_;
    my @tarray;

    my $newid = 0;
    foreach(my $k=0; $k < scalar @ABOOK; $k++) {
        my $del = 0;
        for(@id) {
            if($k eq $_) { $del = 1; last; }
        }
        unless($del) {
            $tarray[$newid] = $ABOOK[$k];
            $newid++;
        }
    }
    @ABOOK = @tarray;
    undef @tarray;
}

sub append {
    my $self = shift;
    my $ref = $_[0]; # ARRAY ref
    my $id = scalar @ABOOK;
    $ABOOK[$id] = $ref; # add into it
}

sub update {
    my $self = shift;
    my ($id,$ref) = @_; # ref => ARRAY ref
    $ABOOK[$id] = $ref;# if not present, will auto
                       # append it
}

sub save {
    my $self = shift;
    my $file = $self->{file} || 'abook.cf';
    my $addhdr = 0;

    $addhdr = 1 if(!-r $file);
    open(FD, "> $file") or die "Write to $file error: $!\n";
    flock(FD, LOCK_EX);
    if($addhdr) {
        $REF->combine(('Name', 'Email', 'Company', 'Mobile'));
        print FD $REF->string,"\n";
    }
    foreach(my $k=0; $k< scalar @ABOOK; $k++) {
        my $val = $ABOOK[$k];
        $val = $REF->combine(@$val);
        $val = $REF->string($val);
        print FD $val,"\n";
    }
    flock(FD, LOCK_UN);
    close FD;
    1;
}

sub DESTROY {
    undef $REF;
    undef @ABOOK;
}

1;


syntax highlighted by Code2HTML, v. 0.9.1