package Alzabo::Create::Index;

use strict;
use vars qw($VERSION);

use Alzabo::Create;
use Alzabo::Exceptions ( abbr => 'params_exception' );
use Alzabo::Utils;

use Params::Validate qw( :all );
Params::Validate::validation_options
    ( on_fail => sub { params_exception join '', @_ } );

use base qw(Alzabo::Index);

$VERSION = 2.0;

1;

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

    validate( @_, { table    => { isa => 'Alzabo::Create::Table' },
                    columns  => { type => ARRAYREF },
                    unique   => { type => BOOLEAN, default => 0 },
                    fulltext => { type => BOOLEAN, default => 0 },
                    function => { type => UNDEF | SCALAR,  default => undef },
                  } );
    my %p = @_;

    my $self = bless {}, $class;

    $self->{table} = $p{table};
    $self->{unique} = $p{unique} || 0;
    $self->{fulltext} = $p{fulltext} || 0;
    $self->{function} = $p{function};

    $self->{columns} = Tie::IxHash->new;

    foreach my $c (@{ $p{columns} })
    {
        my %p = Alzabo::Utils::safe_isa( $c, 'Alzabo::Column' ) ? ( column => $c ) : %$c;
        $self->add_column(%p);
    }

    $self->table->schema->rules->validate_index($self);

    return $self;
}

sub add_column
{
    my $self = shift;

    validate( @_, { column => { isa => 'Alzabo::Create::Column' },
                    prefix => { type => SCALAR,
                                optional => 1 } } );
    my %p = @_;

    my $new_name = $p{column}->name;

    params_exception "Column $new_name already exists in index."
        if $self->{columns}->EXISTS($new_name);

    $self->{columns}->STORE( $new_name, \%p );

    eval { $self->table->schema->rules->validate_index($self); };

    if ($@)
    {
        $self->{columns}->DELETE($new_name);

        rethrow_exception($@);
    }
}

sub delete_column
{
    my $self = shift;

    validate_pos( @_, { isa => 'Alzabo::Create::Column' } );
    my $c = shift;

    params_exception "Column " . $c->name . " is not part of index."
        unless $self->{columns}->EXISTS( $c->name );

    $self->{columns}->DELETE( $c->name );
}

sub set_prefix
{
    my $self = shift;

    validate( @_, { column => { isa => 'Alzabo::Create::Column' },
                    prefix => { type => SCALAR } } );
    my %p = @_;

    params_exception "Column " . $p{column}->name . " is not part of index."
        unless $self->{columns}->EXISTS( $p{column}->name );

    my $col = $self->{columns}->FETCH( $p{column}->name );
    my $old_val = delete $col->{prefix};
    $col->{prefix} = $p{prefix};

    eval { $self->table->schema->rules->validate_index($self); };

    if ($@)
    {
        if ($old_val)
        {
            $col->{prefix} = $old_val;
        }
        else
        {
            delete $col->{prefix};
        }

        rethrow_exception($@);
    }
}

sub set_unique
{
    my $self = shift;

    validate_pos( @_, 1 );
    $self->{unique} = shift;
}

sub set_fulltext
{
    my $self = shift;

    validate_pos( @_, 1 );

    my $old_val = $self->{fulltext};

    $self->{fulltext} = shift;

    eval { $self->table->schema->rules->validate_index($self); };

    if ($@)
    {
        $self->{fulltext} = $old_val;

        rethrow_exception($@);
    }
}

sub register_column_name_change
{
    my $self = shift;

    validate( @_, { column => { isa => 'Alzabo::Create::Column' },
                    old_name => { type => SCALAR } } );
    my %p = @_;

    return unless $self->{columns}->EXISTS( $p{old_name} );

    my $new_name = $p{column}->name;

    my $index = $self->{columns}->Indices( $p{old_name} );
    my $val = $self->{columns}->Values($index);
    $val->{column} = $p{column};
    $self->{columns}->Replace( $index, $val, $new_name );
}

__END__

=head1 NAME

Alzabo::Create::Index - Index objects for schema creation

=head1 SYNOPSIS

  use Alzabo::Create::Index;

=for pod_merge DESCRIPTION

=head1 INHERITS FROM

C<Alzabo::Index>

=for pod_merge merged

=head1 METHODS

=head2 new

The constructor takes the following parameters:

=over 4

=item * table => C<Alzabo::Create::Table> object

The table that this index is indexing.

=item * columns => [ C<Alzabo::Create::Column> object, .. ]

=item * columns => [ { column => C<Alzabo::Create::Column> object,
                       prefix => $prefix },
                      repeat as needed ...
                   ]

This parameter indicates which columns that are being indexed.  It can
either be an array reference of column objects, or an array reference
of hash references, each with a key called column and one called
prefix.

The prefix key is optional.

=item * unique => $boolean

Indicates whether or not this is a unique index.

=item * fulltext => $boolean

Indicates whether or not this is a fulltext index.

=item * function => $string

This can be used to create a function index where supported. The value
of this parameter should be the full function, with column names, such
as C<LCASE( username )>.

The "columns" parameter should include all the columns used in the
function.

=back

Returns a new C<Alzabo::Create::Index> object.

Throws: L<C<Alzabo::Exception::Params>|Alzabo::Exceptions>,
L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions>

=for pod_merge table

=for pod_merge columns

=head2 add_column

Adds a column to the index.

This method takes the following parameters:

=over 4

=item * column => C<Alzabo::Create::Column> object

=item * prefix => $prefix (optional)

=back

Throws: L<C<Alzabo::Exception::Params>|Alzabo::Exceptions>,
L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions>

=head2 delete_column (C<Alzabo::Create::Column> object)

Deletes the given column from the index.

Throws: L<C<Alzabo::Exception::Params>|Alzabo::Exceptions>,
L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions>

=for pod_merge prefix

=head2 set_prefix

This method takes the following parameters:

=over 4

=item * column => C<Alzabo::Create::Column> object

=item * prefix => $prefix

=back

Throws: L<C<Alzabo::Exception::Params>|Alzabo::Exceptions>,
L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions>

=for pod_merge unique

=head2 set_unique ($boolean)

Sets whether or not the index is a unique index.

=for pod_merge fulltext

=head2 set_fulltext ($boolean)

Set whether or not the index is a fulltext index.

Throws: L<C<Alzabo::Exception::Params>|Alzabo::Exceptions>,
L<C<Alzabo::Exception::RDBMSRules>|Alzabo::Exceptions>

=head2 register_column_name_change

This method takes the following parameters:

=over 4

=item * column => C<Alzabo::Create::Column> object

The column (with the new name already set).

=item * old_name => $old_name

=back

This method is called by the table object which owns the index when a
column name changes.  You should never need to call this yourself.

Throws: L<C<Alzabo::Exception::Params>|Alzabo::Exceptions>

=for pod_merge id

=head1 AUTHOR

Dave Rolsky, <autarch@urth.org>

=cut


syntax highlighted by Code2HTML, v. 0.9.1