# $Id: Folder.pm,v 1.12 2001/08/29 19:49:28 joern Exp $

package JaM::Folder;

use strict;
use Carp;
use Data::Dumper;

sub dbh 		{ shift->{dbh}				}

sub id			{ my $s = shift; $s->{id}
		          = shift if @_; $s->{id}		}
sub folder_id		{ my $s = shift; $s->{id}
		          = shift if @_; $s->{id}		}
sub name		{ my $s = shift; $s->{name}
		          = shift if @_; $s->{name}		}
sub sibling_id		{ my $s = shift; $s->{sibling_id}
		          = shift if @_; $s->{sibling_id}	}
sub leaf		{ my $s = shift; $s->{leaf}
		          = shift if @_; $s->{leaf}		}
sub path		{ my $s = shift; $s->{path}
		          = shift if @_; $s->{path}		}
sub selected_mail_id	{ my $s = shift; $s->{selected_mail_id}
		          = shift if @_; $s->{selected_mail_id}	}
sub mail_sum		{ my $s = shift; $s->{mail_sum}
		          = shift if @_; $s->{mail_sum}		}
sub mail_read_sum	{ my $s = shift; $s->{mail_read_sum}
		          = shift if @_; $s->{mail_read_sum}	}
sub status		{ my $s = shift; $s->{status}
		          = shift if @_; $s->{status}	}
sub opened		{ my $s = shift; $s->{opened}
		          = shift if @_; $s->{opened}		}
sub sort_column		{ my $s = shift; $s->{sort_column}
		          = shift if @_; $s->{sort_column}	}
sub sort_direction	{ my $s = shift; $s->{sort_direction}
		          = shift if @_; $s->{sort_direction}	}
sub show_max		{ my $s = shift; $s->{show_max}
		          = shift if @_; $s->{show_max}		}
sub show_all		{ my $s = shift; $s->{show_all}
		          = shift if @_; $s->{show_all}		}
sub undeletable		{ my $s = shift; $s->{undeletable}
		          = shift if @_; $s->{undeletable}	}

my $FOLDERS;
my $DBH;

sub init {
	my $type = shift;
	my %par = @_;
	my ($dbh) = @par{'dbh'};
	return 1 if $FOLDERS;
	$FOLDERS = $type->query ( dbh => $dbh, init => 1 );
	$DBH = $dbh;
	1;
}

sub by_id {
	my $type = shift;
	my ($folder_id) = @_;
	confess "not initialized" unless $FOLDERS;
	confess "unknown folder id '$folder_id'"
		if not exists $FOLDERS->{$folder_id};
	return $FOLDERS->{$folder_id};
}

sub by_path {
	my $type = shift;
	my ($path) = @_;
	confess "not initialized" unless $FOLDERS;

	my $result = $type->query (
		where => 'path=?',
		params => [ $path ]
	);

	my ($ret) = values %{$result};
	return $ret;
}

sub all_folders {
	my $type = shift;
	confess "not initialized" unless $FOLDERS;
	return $FOLDERS;
}

sub query {
	my $type = shift;
	my %par = @_;
	my  ($dbh, $where, $params, $no_cache, $init) =
	@par{'dbh','where','params','no_cache','init'};

	confess "not initialized" if not $FOLDERS and not $init;

	$dbh ||= $DBH;
	$where = "where $where" if $where;

	my %folders;

	if ( not $no_cache and $FOLDERS ) {
		my $ar = $dbh->selectcol_arrayref (
			"select id
			 from   Folder
			 $where", {}, @{$params}
		);
		@folders{@{$ar}} = @$FOLDERS{@{$ar}};

	} else {
		my $sth = $dbh->prepare (
			"select id, name, parent_id, leaf,
				path, selected_mail_id, mail_sum, status,
				mail_read_sum, opened, sort_column,
				sort_direction, sibling_id, show_max,
				show_all, undeletable
			 from   Folder
			 $where"
		);

		$sth->execute (@{$params}) if $params;
		$sth->execute              if not $params;

		my ($id, $name, $parent_id, $leaf,
		    $path, $selected_mail_id, $mail_sum, $status,
		    $mail_read_sum, $opened, $sort_column,
		    $sort_direction, $sibling_id, $show_max,
		    $show_all, $undeletable);

		$sth->bind_columns (\(
		    $id, $name, $parent_id, $leaf,
		    $path, $selected_mail_id, $mail_sum, $status,
		    $mail_read_sum, $opened, $sort_column,
		    $sort_direction, $sibling_id, $show_max,
		    $show_all, $undeletable
		));

		while ( $sth->fetch ) {
			my $self = {
				dbh    	            => $dbh,
				id	            => $id,
				name	            => $name,
				parent_id           => $parent_id,
				sibling_id          => $sibling_id,
				leaf	            => $leaf,
				path	            => $path,
				selected_mail_id    => $selected_mail_id,
				mail_sum            => $mail_sum,
				mail_read_sum       => $mail_read_sum,
				status		    => $status,
				opened	            => $opened,
				sort_column         => $sort_column,
				sort_direction      => $sort_direction,
				show_max	    => $show_max,
				show_all	    => $show_all,
				undeletable	    => $undeletable,
			};
			$folders{$id} = bless $self, $type;
		}
	}
	
	return \%folders;
}

sub create {
	my $type = shift;
	my %par = @_;
	my  ($dbh, $name, $parent, $sibling) =
	@par{'dbh','name','parent','sibling'};

	confess "not initialized" unless $FOLDERS;
	$dbh ||= $DBH;

	my $parent_id  = $parent->id;
	my $sibling_id = $sibling ? $sibling->id : 99999;
	my $path = $parent->path."/$name";
	$path =~ s!/+!/!g;

	$dbh->do (
		"insert into Folder
		 (name, parent_id, sibling_id, leaf, path)
		 values
		 (?, ?, ?, ?, ?)", {},
		 $name, $parent_id, $sibling_id, 1, $path
	);
	
	my $id = $dbh->{mysql_insertid};

	my $self = $type->query (
		dbh => $dbh,
		where => "id=?",
		params => [ $id ],
		no_cache => 1,
	)->{$id};

	$FOLDERS->{$id} = $self;
	
	return $self;
}

sub save {
	my $self = shift;
	
	confess "not initialized" unless $FOLDERS;

	my $parent_id = $self->parent_id;
	my $path;
	if ( $parent_id ) {
		$path = (ref $self)->by_id($parent_id)->path."/";
	} else {
		$path = "/";
	}

	$path .= $self->name;
	$path =~ s!/+!/!g;
	$self->path($path);

	$self->dbh->do (
		"update Folder set
			sibling_id=?, name=?, parent_id=?, leaf=?,
			path=?, selected_mail_id=?, mail_sum=?,
			mail_read_sum=?, opened=?, sort_column=?,
			sort_direction=?, show_max=?, show_all=?,
			status=?, undeletable=?
		 where id=?", {},
		 $self->{sibling_id}, $self->{name},
		 $self->{parent_id}, $self->{leaf}, $self->{path},
		 $self->{selected_mail_id}, $self->{mail_sum},
		 $self->{mail_read_sum}, $self->{opened},
		 $self->{sort_column}, $self->{sort_direction},
		 $self->{show_max}, $self->{show_all}, $self->{status},
		 $self->{undeletable},
		 $self->{id}
	);

	1;
}

sub recalculate_folder_stati {
	my $type = shift;
	my %par = @_;
	my  ($dbh) = @par{'dbh'};
	
	confess "not initialized" unless $FOLDERS;
	$dbh ||= $DBH;

	my $sth = $dbh->prepare (	
		"select f1.id, sum(f2.mail_sum-f2.mail_read_sum)
		 from   Folder f1, Folder f2
		 where  f2.path like concat(f1.path,'%')
		 group by 1"
	);
	$sth->execute;

	my ($ar, $old_status, $new_status, $folder);

	while ( $ar = $sth->fetchrow_arrayref ) {
		$folder = $type->by_id($ar->[0]);
		$old_status = $folder->status;
		$new_status = ($ar->[1] ? 'N' : 'R');
		$new_status = 'NC' if $new_status eq 'N' and $folder->mail_sum == $folder->mail_read_sum;
		if ( $old_status ne $new_status ) {
			$folder->status($new_status);
			$folder->save;
		}
	}
	
	$sth->finish;

	1;
}

sub mark_all_read {
	my $self = shift;

	confess "not initialized" unless $FOLDERS;

	$self->dbh->do (
		"update Mail set status='R' where folder_id=?", {},
		$self->id
	);
	
	$self->mail_read_sum ( $self->mail_sum );
	$self->save;

	1;
}

sub get_first_child_folder_id {
	my $self = shift;
	
	confess "not initialized" unless $FOLDERS;

	my ($folder_id) = $self->dbh->selectrow_array (
		"select Folder.id
		 from   Folder left outer join Folder Sibling
		 	  on Folder.id = Sibling.sibling_id
		 where  Sibling.sibling_id is NULL and
		 	Folder.parent_id=?", {}, $self->id
	);
	
	return $folder_id;
}

sub get_last_child_folder_id {
	my $self = shift;
	
	confess "not initialized" unless $FOLDERS;

	my ($folder_id) = $self->dbh->selectrow_array (
		"select id
		 from   Folder
		 where  parent_id=? and sibling_id=99999", {},
		$self->id
	);
	
	return $folder_id;
}

sub sibling_of_id {
	my $self = shift;

	confess "not initialized" unless $FOLDERS;

	my ($id) = $self->dbh->selectrow_array (
		"select id
		 from   Folder
		 where  sibling_id = ?", {}, $self->id
	);
	
	return $id;
}

sub childs {
	my $self = shift;
	
	return (ref $self)->query (
		where => 'parent_id=?',
		params => [ $self->id ]
	);
}

sub descendants {
	my $self = shift;
	
	my $path = $self->path;
	$path = $path.'/%';

	return (ref $self)->query (
		where => "path like '$path'"
	);
}

sub parent_id {
	my $self = shift;
	my ($value) = @_;
	return $self->{parent_id} if not @_;

	# this computes a the new path
	$self->{parent_id} = $value;
	$self->save;

	# get direct childs
	my $childs = $self->childs;
	
	# compute new path of the childs
	foreach my $child ( values %{$childs} ) {
		# save computes the correct path
		$child->save;
		# do the same for the childs of this child
		$child->parent_id($self->id);
	}
	
	return $value;
}

sub delete_content {
	my $self = shift;
	
	my $desc = $self->descendants;
	my @desc_ids = keys %{$desc};
	
	my $delete_mail_folder_ids = join (',', @desc_ids, $self->id);
	my $delete_folder_ids      = join (',', @desc_ids);

	my ($cnt) = $self->dbh->do (
		"delete from Mail where folder_id in ($delete_mail_folder_ids)"
	);
	
	if ( $delete_folder_ids ) {
		# first delete associated filters
		require JaM::Filter::IO;

		my $filter_ids = $self->dbh->selectcol_arrayref (
			"select id
			 from   IO_Filter
			 where  folder_id in ($delete_folder_ids)"
		);

		my $filter;
		foreach my $filter_id ( @{$filter_ids} ) {
			$filter = JaM::Filter::IO->load (
				dbh => $self->dbh,
				filter_id => $filter_id
			);
			$filter->delete;
		}
	
		# now the folders themselves
		$cnt = $self->dbh->do (
			"delete from Folder where id in ($delete_folder_ids)"
		);
	
		delete @$FOLDERS{@desc_ids};
	}
	
	$self->mail_sum(0);
	$self->mail_read_sum(0);
	$self->leaf(1);
	$self->save;
	
	my %desc_ids;
	@desc_ids{@desc_ids} = (1) x @desc_ids;
	return \%desc_ids;
}

1;
