package MTK::Core::Gui::Gtk::Playlist;

use Gtk2;
use Carp;

use MTK::Core::MetaBundle;

use base Exporter::;

our @EXPORT = qw(
	COLUMN_RECORD
	COLUMN_TITLE
	COLUMN_ARTIST
	COLUMN_ALBUM
	COLUMN_TRACK
	COLUMN_YEAR
	COLUMN_GENRE
	COLUMN_COMMENT
	COLUMN_DURATION
	COLUMN_BITRATE
	COLUMN_FREQUENCY
	COLUMN_FILENAME
	COLUMN_DIRECTORY
	COLUMN_PATHNAME
	COLUMN_SIZE
	COLUMN_FORMAT
	COLUMNS_NUMBER
);

# The data columns that we export via the tree model interface

use constant {
	COLUMN_RECORD  	=> 0,
	COLUMN_TITLE		=> 1,
	COLUMN_ARTIST		=> 2,
	COLUMN_ALBUM		=> 3,
	COLUMN_TRACK		=> 4,
	COLUMN_YEAR			=> 5,
	COLUMN_GENRE		=> 6,
	COLUMN_COMMENT		=> 7,
	COLUMN_DURATION	=> 8,
	COLUMN_BITRATE		=> 9,
	COLUMN_FREQUENCY	=> 10,
	COLUMN_FILENAME	=> 11,
	COLUMN_DIRECTORY	=> 12,
	COLUMN_PATHNAME	=> 13,
	COLUMN_SIZE			=> 14,
	COLUMN_FORMAT		=> 15,
	COLUMNS_NUMBER		=> 17,
};


use Glib::Object::Subclass 
	Glib::Object::,
#	interfaces => [qw/Gtk2::TreeModel Gtk2::TreeSortable/],	
	interfaces => [qw/Gtk2::TreeModel /],
;

sub INIT_INSTANCE {
	my $self = shift;

	$self->{n_columns} = COLUMNS_NUMBER;

	$self->{column_types} = [
		'Glib::Scalar',   # COLUMN_RECORD
		'Glib::String',	# COLUMN_TITLE
		'Glib::String',	# COLUMN_ARTIST
		'Glib::String',	# COLUMN_ALBUM
		'Glib::Uint',     # COLUMN_TRACK
		'Glib::String',   # COLUMN_YEAR
		'Glib::String',   # COLUMN_GENRE
		'Glib::String',   # COLUMN_COMMENT
		'Glib::String',   # COLUMN_DURATION
		'Glib::Uint',     # COLUMN_BITRATE
		'Glib::Uint',     # COLUMN_FREQUENCY
		'Glib::String',   # COLUMN_FILENAME
		'Glib::String',   # COLUMN_DIRECTORY
		'Glib::String',   # COLUMN_PATHNAME
		'Glib::Uint',     # COLUMN_SIZE
		'Glib::String',   # COLUMN_FORMAT
	];

	$self->{rows}     = [];

	# Random int to check whether an iter belongs to our model
	$self->{stamp} = sprintf '%d', rand (1<<31);
}

sub FINALIZE_INSTANCE {
	my $self = shift;

	# free all records and free all memory used by the list
	#warning IMPLEMENT
}

#
# tells the rest of the world whether our tree model has any special characteristics. 
#

sub GET_FLAGS { [qw/list-only/] }


#
# tells the rest of the world how many data
# columns we export via the tree model interface
#

sub GET_N_COLUMNS { shift->{n_columns}; }


#
# tells the rest of the world which type of
# data an exported model column contains
#

sub GET_COLUMN_TYPE {
	my ($self, $index) = @_;
	# and invalid index will send undef back to the calling XS layer,
	# which will croak.
	return $self->{column_types}[$index];
}


#
# converts a tree path (physical position) into a
# tree iter structure (the content of the iter
# fields will only be used internally by our model).
# We simply store a pointer to our CustomRecord
# structure that represents that row in the tree iter.
#

sub GET_ITER {
	my ($self, $path) = @_;

	die "no path" unless $path;

	my @indices = $path->get_indices;
	my $depth   = $path->get_depth;

	# we do not allow children
	# depth 1 = top level; a list only has top level nodes and no children
	die "depth != 1" unless $depth == 1;

	my $n = $indices[0]; # the n-th top level row

	return undef if $n >= @{$self->{rows}} || $n < 0;

	my $record = $self->{rows}[$n];
	
	die "no record" unless $record;
	
	unless ($record->{pos} == $n) {
		print Data::Dumper->Dump([$record]);
		die "bad record";
	}

	# We simply store a pointer to our custom record in the iter
	return [ $self->{stamp}, $n, $record, undef ];
}


#
#  custom_list_get_path: converts a tree iter into a tree path (ie. the
#                        physical position of that row in the list).
#

sub GET_PATH {
	my ($self, $iter) = @_;
	die "no iter" unless $iter;

	my $record = $iter->[2];

	my $path = Gtk2::TreePath->new;
	$path->append_index ($record->{pos});
	return $path;
}


#
# custom_list_get_value: Returns a row's exported data columns
#                        (_get_value is what gtk_tree_model_get uses)
#

sub GET_VALUE {
	my ($self, $iter, $column) = @_;

	die "bad iter" unless $iter;

	return undef unless $column < @{$self->{column_types}};

	my $record = $iter->[2];

	return undef unless $record;

	die "bad iter" if $record->{pos} >= @{$self->{rows}};

	if($column == COLUMN_RECORD) {
		return $record;
	} elsif($column == COLUMN_TITLE) {
		return $record->{title};
	} elsif($column == COLUMN_ARTIST) {
		return $record->{artist};
	} elsif($column == COLUMN_ALBUM) {
		return $record->{album};
	} elsif($column == COLUMN_TRACK) {
		return $record->{tracknum};
	} elsif ($column == COLUMN_YEAR) {
		return $record->{year};
	} elsif($column == COLUMN_GENRE) {
		return $record->{genre};
	} elsif($column == COLUMN_COMMENT) {
		return $record->{comment};
	} elsif($column == COLUMN_DURATION) {
		return $record->{duration};
	} elsif($column == COLUMN_BITRATE) {
		return $record->{bitrate};
	} elsif($column == COLUMN_FREQUENCY) {
		return $record->{frequency};
	} elsif($column == COLUMN_FILENAME) {
		return $record->{filename};
	} elsif($column == COLUMN_DIRECTORY) {
		return $record->{directory};
	} elsif($column == COLUMN_PATHNAME) {
		return $record->{pathname};
	} elsif($column == COLUMN_SIZE) {
		return $record->{size};
	} elsif($column == COLUMN_FORMAT) {
		return $record->{extension};
	}
}


#
# iter_next: Takes an iter structure and sets it to point to the next row.
#

sub ITER_NEXT {
	my ($self, $iter) = @_;

	return undef
		unless $iter && $iter->[2];

	my $record = $iter->[2];

	# Is this the last record in the list?
	return undef
		if $record->{pos} >= @{ $self->{rows} };

	my $nextrecord = $self->{rows}[$record->{pos} + 1];

	return undef unless $nextrecord;
	die "invalid record" unless $nextrecord->{pos} == ($record->{pos} + 1);

	return [ $self->{stamp}, $nextrecord->{pos}, $nextrecord, undef ];
}


#
# iter_children: Returns TRUE or FALSE depending on whether the row
#                specified by 'parent' has any children.  If it has
#                children, then 'iter' is set to point to the first
#                child.  Special case: if 'parent' is undef, then the
#                first top-level row should be returned if it exists.
#

sub ITER_CHILDREN {
	my ($self, $parent) = @_;

###	return undef unless $parent and $parent->[1];

	# this is a list, nodes have no children
	return undef if $parent;

	# parent == NULL is a special case; we need to return the first top-level row

 	# No rows => no first row
	return undef unless @{ $self->{rows} };

	# Set iter to first item in list
	return [ $self->{stamp}, 0, $self->{rows}[0] ];
}


#
# iter_has_child: Returns TRUE or FALSE depending on whether
#                 the row specified by 'iter' has any children.
#                 We only have a list and thus no children.
#

sub ITER_HAS_CHILD { FALSE }

#
# iter_n_children: Returns the number of children the row specified by
#                  'iter' has. This is usually 0, as we only have a list
#                  and thus do not have any children to any rows.
#                  A special case is when 'iter' is undef, in which case
#                  we need to return the number of top-level nodes, ie.
#                  the number of rows in our list.
#

sub ITER_N_CHILDREN {
	my ($self, $iter) = @_;

	# special case: if iter == NULL, return number of top-level rows
	return scalar @{$self->{rows}}
		if ! $iter;

	return 0; # otherwise, this is easy again for a list
}


#
# iter_nth_child: If the row specified by 'parent' has any children,
#                 set 'iter' to the n-th child and return TRUE if it
#                 exists, otherwise FALSE.  A special case is when
#                 'parent' is NULL, in which case we need to set 'iter'
#                 to the n-th row if it exists.
#

sub ITER_NTH_CHILD {
	my ($self, $parent, $n) = @_;

	# a list has only top-level rows
	return undef if $parent;

	# special case: if parent == NULL, set iter to n-th top-level row

	return undef if $n >= @{$self->{rows}};

	my $record = $self->{rows}[$n];

	die "no record" unless $record;
	die "bad record" unless $record->{pos} == $n;

	return [ $self->{stamp}, $n, $record ];
}


#
# iter_parent: Point 'iter' to the parent node of 'child'.  As we have a
#              a list and thus no children and no parents of children,
#              we can just return FALSE.
#

sub ITER_PARENT { FALSE }

#
# ref_node and unref_node get called as the model manages the lifetimes
# of nodes in the model.  you normally don't need to do anything for these,
# but may want to if you plan to implement data caching.
#
#sub REF_NODE { warn "REF_NODE @_\n"; }
#sub UNREF_NODE { warn "UNREF_NODE @_\n"; }

#
# new:  This is what you use in your own code to create a
#       new custom list tree model for you to use.
#

# we inherit new from Glib::Object::Subclass


#
# set: It's always nice to be able to update the data stored in a data
#      structure.  So, here's a method to let you do that.  We emit the
#      'row-changed' signal to notify all who care that we've updated
#      something.
#

sub set {
	my ($self, $treeiter) = @_;

	# create (col, value) pairs to update.
	my %vals = @_;

	# Convert the Gtk2::TreeIter to a more useable array reference.
	# Note that the model's stamp must be passed in as an argument.
	# This is so we can avoid trying to extract the guts of an iter
	# that we did not create in the first place.
	my $iter = $treeiter->to_arrayref($self->{stamp});
	
	my $record = $iter->[2];

	while(my ($col, $val) = each %vals) {
		if($col == COLUMN_TITLE) {
			$record->{title} = $val;
		} elsif($col == COLUMN_ARTIST) {
			$record->{artist} = $val;
		} elsif($col == COLUMN_ALBUM) {
			$record->{album} = $val;
		} elsif($col == COLUMN_TRACK) {
			$record->{tracknum} = $val;
		} elsif ($col == COLUMN_YEAR) {
			$record->{year} = $val;
		} elsif($col == COLUMN_GENRE) {
			$record->{genre} = $val;
		} elsif($col == COLUMN_COMMENT) {
			$record->{comment} = $val;
		} elsif($col == COLUMN_FILENAME) {
			$record->{filename} = $val;
		} elsif($col == COLUMN_DIRECTORY) {
			$record->{directory} = $val;
		} elsif($col == COLUMN_PATHNAME) {
			$record->{pathname} = $val;
		} elsif ($col == COLUMN_RECORD) {
			warn "Can't update the value of the Record column!";
		} else {
			warn "Invalid column used in set method!";
		}
	}

	$self->row_changed($self->get_path($treeiter), $treeiter);
}

sub get_iter_from_pathname {
	my ($self, $pathname) = @_;

	my ($record, $n);

	for(0..scalar (@{$self->{rows}})) {
		if($self->{rows}[$_]->{pathname} eq $pathname) {
			$record = $self->{rows}[$_];
			$n = $_;
			last;
		}
	}

	return Gtk2::TreeIter->new_from_arrayref([$self->{stamp}, $n, $record, undef]);
}

sub append_record {
	my ($self, $meta) = @_;

	my $record = $meta;

	push @{ $self->{rows} }, $record;
	$record->{pos} = @{$self->{rows}} - 1;

	# inform the tree view and other interested objects
	# (e.g. tree row references) that we have inserted
	# a new row, and where it was inserted
	
	my $path = Gtk2::TreePath->new;

	$path->append_index($record->{pos});
	$self->row_inserted($path, $self->get_iter($path));
}

sub remove_record {
	my ($self, $path) = @_;

	splice @{ $self->{rows} }, $path->to_string, 1;
	$self->row_deleted($path);
}



1;
