package RISCOS::DrawFile::Object;

require RISCOS::DrawFile::Common;

use strict;
use vars qw ($VERSION @ISA);

$VERSION = 0.03;
@ISA = 'RISCOS::DrawFile::Common';

# 0.02 does copy constructors.
# 0.03 PrePack does BBox not BBoxCalc

sub new ($) {
    my $proto = shift;
    my $class = ref($proto) || $proto;

    my ($self, $type) = ({});
    # It is defined and it is not a reference to something.
    if (defined $_[0]) {
	unless (ref $_[0]) {
	    $type = unpack ('I', $_[0])
	} elsif (ref ($_[0]) eq $class) {
	    return [$_[0]->Clone()];	# All done
	}
    }

    if (defined $_[1]) {
	my ($split, $spare);
	if (ref $_[1] eq 'ARRAY') {
	    $split = $_[1];
	} elsif (ref $_[1] eq 'CODE' && defined $type) {
	    # Call the bit of code, which will return an array.
	    $split = [&{$_[1]} ($type)];
	} else {
	    $split = [];
	}
	($type, $self->{'__LAYER'}, $self->{'__FLAGS'}, $spare) = @$split;
	if ($spare) {
	    warn "Reserved type byte is $spare (expected 0) in object type $type"
	      if $^W;
	}
	$self->{'__SPARE'} = $spare;
    }

    bless ($self, $class);
    wantarray ? ($self, $type) : $self;
}

# Mostly for the fonttable object to ennumerate fonts.
# Text objects should push fontnames into the hashref in $_[0]
sub PrePack {
    my $self = shift;
    $self->BBox (@_);
}

sub Layer ($) {
    my $self = shift;
    my $layer = $self->{'__LAYER'};
    $self->{'__LAYER'} = $_[0] if @_;
    $layer;
}

sub PackType {
    my $self = shift;
    my $type = defined $_[0] ? $_[0] : $self->{'__TYPE'};
    return pack 'I', $type
      unless defined $self->{'__LAYER'};
    pack ('C4', $type, $self->{'__LAYER'}, $self->{'__FLAGS'},
	  $self->{'__SPARE'} | 0);
}

sub PackTypeSizeBBox {
    my $self = shift;
    my $type = shift;
    my $size = shift;
    my $bbox;
    if (defined ($_[0])) {
	$bbox = pack 'i4', (ref $_[0] ? @{$_[0]} : @_);
    } else {
	# Need to cope with BBox returning undef or 0 (eg empty path objects)
	$bbox = $self->BBox();
	$bbox = $bbox ? pack 'i4', @$bbox
		      : pack 'x16';
	# Can't combine the x16 template, as @{undef} is not ();
    }
    $self->PackType($type) . pack ('I', (defined $size ? $size
						       : $self->Size()))
			   . $bbox;
}

sub Type {
    my $self = shift;
    $self->{'__TYPE'};
}

sub Write {
    my $self = shift;
    print {$_[0]} $self->Pack (@_);
}

# The extended tag is described by the following structure:
#
# typedef unsigned char draw_tagtyp;
# typedef unsigned char draw_layer;
# typedef unsigned char draw_objflags;
#
# struct draw_extag			/* Extended object tag */
# {
#	draw_tagtyp tag;		/* Basic object type */
#	draw_layer layer;		/* Object layer, 0 to 31 */
#	draw_objflags flag;		/* Object flags */
#	unsigned char spare;		/* For future expansion */
# };
#
# The following bits in flag are used at present:
#
# #define flag_NODISPLAY		1	/* Never displayed */
# #define flag_LOCKED		2	/* Object is locked */
# #define flag_HIDDEN		4	/* Temporarily hidden */

1;
__END__

=head1 NAME

RISCOS::DrawFile::Object

=head1 SYNOPSIS

Abstract base class for DrawFile objects.

=head1 DESCRIPTION

C<RISCOS::DrawFile::Object> provdes an abstract base class for DrawFile objects
to inherit from. It provides various methods, some of which are inherited from
C<RISCOS::DrawFile::Common> and hence are also can be used on entire DrawFiles.
See C<L<RISCOS::DrawFile::OpaqueObject>> for a minimal usable implementation of
a DrawFile object.

=head2 Methods

=over 4

=item new <data>, [<split function>, ...]

Create a new DrawFile object. If C<data> is not a reference it is assumed to be
packed data, and the first 4 bytes are interpreted as an integer to get the
object type. If a second argument is provided, a reference to code is assumed
to be a split function with the same interface as drawplus_split_type, and will
be called to split the extended object tag. If the second argument is a
reference to an array it is assumed to give (type, layer, flags, spare), and
these values are used in place of any derived from the first 4 bytes.

If I<data> is a reference to an object of the same class as the new object will
become then it is C<Clone()>ed and a list containing the clone returned. This
allows all derived classes to easily implement a copy constructor.

In scalar context C<new> returns a blessed reference to the new object. In
array context C<new> returns a list (object, type, fonttable). I<object> can be
a reference to a list of objects, and I<fonttable> is normally C<undef>. See
C<RISCOS::DrawFile::FontTable> for the procedure to return a fonttable if found.

Subclasses are expected to treat I<data> as based on its type. If a subclass
recieves a reference to a list rather than a single blessed reference then it
should return immediately with that list, as C<new> has been used as a copy
constructor.

=over 4

=item scalar

raw bytes from a DrawFile

=item scalar reference

raw bytes from a DrawFile, with type, length and bounding box stripped

=item array reference

object dependent list of parameters to construct a new object

=back

If a second argument is proviced C<new> sets the Layer, Flags and Spare members
of the object to the supplied/split values. C<new> B<never> sets the Type member
of the object - it is the responsibility of the derived class to store the Type
member as if it needs to retain this information.

=item PrePack <hash_reference>

is provided as a hook to perform calculations immediately before saving a
DrawFile. The hash reference is used to store the names of fonts needed in the
FontTable, keys are font names, values the number of text objects that use that
font (see C<PrePack> in C<RISCOS::DrawFile::Text> if you really must
know). The default C<PrePack> calls C<BBox> and returns this value. This ensures
that if any objects have been changed then their containing groups' bounding
boxes will be upadated to reflect this. Derived classes should maintain this
behaviour.

=item Type

returns the objects type. The default method looks in 'C<__TYPE>', which relies
on the derived class storing it there. If your derived class does not need to
store the Type you B<must> override this method, typically with something like

    sub Type { 2; }

Most classes don't need to store the type, as most classes deal with only one
type of object. Exceptions include C<OpaqueObject>, which stores all unknown
object types, and C<Text>, which implements normal text (type 1) and
transformed text (type 12).

=item Layer [<new_layer>]

returns the current layer. If an argument is provided it is used to set the
ojbects layer, and the old layer is returned.

=item PackType [<type>]

packs the object's tag (type) into a 4 byte string. If the object has a layer
defined then DrawPlus extended tag format is used.

The tag is taken from the first argument if supplied, else it is looked up as
'C<__TYPE>'. Note that C<new> in C<RISCOS::DrawFile::Object> does not set
'<__TYPE>', instead returning the value to the derived class.

=item PackTypeSizeBBox <tag>, <size>, <bounding_box>

returns a 24 byte string for the header common to all DrawFile objects
(apart from FontTables).

I<type> is passed to C<PackType> and the method C<Size> is called if I<size> is
undefined. If I<bounding_box> is defined then the object bounding box is taken
from it - if I<bounding_box> is an array reference should point to 4 values,
otherwise arguments three to six are taken to be the bounding box. If
I<bounding_box> is undefined then the method C<BBox> is called, and (0,0,0,0)
used if this returns C<undef>.

Usually C<PackTypeSizeBBox> can be called with B<no> arguments and it will do
the right thing.

=item Write <filehandle>, <fonttable>, ...

writes the object to the given filehandle. The default implementation C<print>s
the result of calling C<Pack> with the remainder of the argument list. C<Write>
should return true unless there was an error.

=back

=head2 Methods supplied by C<RISCOS::DrawFile::Common>

=over 4

=item Clone

returns a copy of this object.

=item BBox

returns a reference to an array giving the bounding box, or C<undef> if there is
is no bounding box for this object (I<i.e.> font tables, empty paths, option
objects). Note that option objects and empty paths store a bounding box of
(0,0,0,0) when saved. C<BBox> will attempt to call C<BBox_Calc> (which the
derived class B<must> provide) if the bounding box is currently unknown.

As the returned array reference B<is> the internal copy of the bounding box it
must not be modified.

=item Inside <bbox>

=item InsideOrTouching <bbox>

=item Intersect <bbox>

=item IntersectOrTouching <bbox>

=item Outside <bbox>

return a reference to the object if it has the correct relationship with the
bounding box (passed as an array reference), C<undef> if it does not.

=back

=head2 Methods derived classes must supply

In addition to the overriding above methods where appropriate (in particular
C<Type>) derived classes must provide the following methods

=over 4

=item BBox_Calc

(re)calculates the bounding box, returning a reference to an array, or undef if
there is no bounding box. As it has no idea about the data it contains
C<OpaqueObject> simply returns the bounding box it was given when it was called.

=item Size

returns the size of the object when saved in a DrawFile

=item Pack <undef>, fonttable, ...

returns a scalar containing the object packed ready to save into a DrawFile.

=back

=head1 BUGS

Not tested enough.

=head1 AUTHOR

Nicholas Clark <F<nick@unfortu.net>>
