package Net::FileMaker::XML::Database;

use strict;
use warnings;
use Net::FileMaker::XML::ResultSet;
use Carp;

use base qw(Net::FileMaker::XML);
#
# Particular methods have specific parameters that are optional, but need to be validated to mitigate sending
# bad parameters to the server.
my $acceptable_params = {
    'find'    => '-recid|-lop|-op|-max|-skip|-sortorder|-sortfield|-script|-script\.prefind|-script\.presort',	
	'findall' => '-recid|-lop|-op|-max|-skip|-sortorder|-sortfield|-script|-script\.prefind|-script\.presort',
	'findany' => '-recid|-lop|-op|-max|-skip|-sortorder|-sortfield|-script|-script\.prefind|-script\.presort',
	'delete'  => '-db|-lay|-recid|-script',
	'dup'     => '-db|-lay|-recid|-script',
	'edit'    => '-db|-lay|-recid|-modid|-script',
	'new'     => '-db|-lay|-script'
};

=head1 NAME

Net::FileMaker::XML::Database

=cut

our $VERSION = 0.063;

=head1 SYNOPSIS

This module handles all the tasks with XML data. Don't call this module directly, instead use L<Net::FileMaker::XML>.

    use Net::FileMaker::XML;
    my $fm = Net::FileMaker::XML->new(host => $host);
    my $db = $fm->database(db => $db, user => $user, pass => $pass);
    
    my $layouts = $db->layoutnames;
    my $scripts = $db->scriptnames;
    my $records = $db->findall( layout => $layout, params => { '-max' => '10'});
    my $records = $db->findany( layout => $layout, params => { '-skip' => '10'});

=head1 METHODS

=cut

sub new
{
	my($class, %args) = @_;

	my $self = {
		host      => $args{host},
		db        => $args{db},
		user      => $args{user},
		pass      => $args{pass},
		resultset => '/fmi/xml/fmresultset.xml',
                ua        => LWP::UserAgent->new,
                xml       => XML::Twig->new,
        uri => URI->new($args{host}),
	};

    bless $self , $class;
	return $self;
}

=head2 layoutnames

Returns an arrayref containing layouts accessible for the respective database.

=cut

sub layoutnames
{
	my $self = shift;
        my $xml = $self->_request(
                user      => $self->{user},
                pass      => $self->{pass},
                resultset => $self->{resultset},
                query     => '-layoutnames',
                params    => { '-db' => $self->{db} }
        );   


	return $self->_compose_arrayref('LAYOUT_NAME', $xml);
}

=head2 scriptnames

Returns an arrayref containing scripts accessible for the respective database.

=cut

sub scriptnames
{
	my $self = shift;
        my $xml = $self->_request(
                user      => $self->{user},
                pass      => $self->{pass},
                resultset => $self->{resultset},
                query     => '-scriptnames',
                params    => { '-db' => $self->{db} }
        );   


	return $self->_compose_arrayref('SCRIPT_NAME', $xml);
}

=head2 find(layout => $layout, params => { parameters })

Returns a L<Net::FileMaker::XML::ResultSet> for a specific database and layout.

=cut

sub find
{
	my ($self, %args) = @_;

    my $params = { 
        '-lay' => $args{layout},
        '-db'  => $self->{db}
    };

    $params = $self->_assert_params(def_params => $params ,params => $args{params} , acceptable_params => $acceptable_params , type => 'find');
	
	my $xml = $self->_request(
			resultset => $self->{resultset}, 
			user      => $self->{user}, 
			pass      => $self->{pass}, 
			query     => '-find',
			params    => $params
	);

	return Net::FileMaker::XML::ResultSet->new(rs => $xml , db => $self);
}


=head2 findall(layout => $layout, params => { parameters }, nocheck => 1)

Returns a L<Net::FileMaker::XML::ResultSet> of all rows on a specific database and layout.

C<nocheck> is an optional argument that will skip checking of parameters if set to 1.

=cut

sub findall
{
	my ($self, %args) = @_;

	my $params = { 
		'-lay' => $args{layout},
		'-db'  => $self->{db}
	};

    $params = $self->_assert_params(def_params => $params ,params => $args{params} , acceptable_params => $acceptable_params, type => 'findall');

	my $xml = $self->_request(
			resultset => $self->{resultset}, 
			user      => $self->{user}, 
			pass      => $self->{pass}, 
			query     => '-findall',
			params    => $params
	);

    return Net::FileMaker::XML::ResultSet->new(rs => $xml , db => $self);
}

=head2 findany(layout => $layout, params => { parameters }, nocheck => 1)

Returns a L<Net::FileMaker::XML::ResultSet> of random rows on a specific database and layout.

C<nocheck> is an optional argument that will skip checking of parameters if set to 1.

=cut

sub findany
{
	my ($self, %args) = @_;

	my $params = { 
		'-lay' => $args{layout},
		'-db'  => $self->{db}
	};

    $params = $self->_assert_params(def_params => $params ,params => $args{params} , acceptable_params => $acceptable_params, type => 'findany');

	my $xml = $self->_request(
			resultset => $self->{resultset}, 
			user      => $self->{user}, 
			pass      => $self->{pass}, 
			query     => '-findany',
			params    => $params
	);

    return Net::FileMaker::XML::ResultSet->new(rs => $xml , db => $self);
}



=head2 edit(layout => $layout , recid => $recid , params => { params })

Updates the row with the fieldname/value pairs passed to params.
Returns a L<Net::FileMaker::XML::ResultSet> object.

=cut

#todo: add tests to /t/01_xml

sub edit
{
    my ($self, %args) = @_;

    my $params = { 
        '-lay' => $args{layout},
        '-db'  => $self->{db}
    };
    
    # just to make the recid param more visible than putting it into the params
    croak 'recid must be defined' if(! defined $args{recid});
    $params->{'-recid'}  = $args{recid};
    
    $params = $self->_assert_params(def_params => $params ,params => $args{params} , acceptable_params => $acceptable_params, type => 'edit');
    
    my $xml = $self->_request(
            resultset => $self->{resultset}, 
            user      => $self->{user}, 
            pass      => $self->{pass}, 
            query     => '-edit',
            params    => $params
    );

    return Net::FileMaker::XML::ResultSet->new(rs => $xml , db => $self);
}

=head2 remove(layout => $layout , recid => $recid , params => { params })

Deletes the record with that specific record id and returns a L<Net::FileMaker::XML::ResultSet> object.

=cut

sub remove
{
    my ($self, %args) = @_;

    my $params = { 
        '-lay' => $args{layout},
        '-db'  => $self->{db}
    };
    
    # just to make the recid param more visible than putting it into the params
    croak 'recid must be defined' if(! defined $args{recid});
    $params->{'-recid'}  = $args{recid};
    
    $params = $self->_assert_params(def_params => $params ,params => $args{params} , acceptable_params => $acceptable_params, type => 'delete');    
    
    my $xml = $self->_request(
            resultset => $self->{resultset}, 
            user      => $self->{user}, 
            pass      => $self->{pass}, 
            query     => '-delete',
            params    => $params
    );

    return Net::FileMaker::XML::ResultSet->new(rs => $xml , db => $self);
}



=head2 insert(layout => $layout , recid => $recid , params => { params })

Creates a new record and populates that record with the fieldname/value pairs passed to params.

Returns an L<Net::FileMaker::XML::ResultSet> object.

=cut

sub insert
{
    my ($self, %args) = @_;

    my $params = { 
        '-lay' => $args{layout},
        '-db'  => $self->{db}
    };
    
    $params = $self->_assert_params(def_params => $params ,params => $args{params} , acceptable_params => $acceptable_params , type => 'new');
    
    my $xml = $self->_request(
            resultset => $self->{resultset}, 
            user      => $self->{user}, 
            pass      => $self->{pass}, 
            query     => '-new',
            params    => $params
    );

    return Net::FileMaker::XML::ResultSet->new(rs => $xml , db => $self);
}



=head2 total_rows(layout => $layout)

Returns a scalar with the total rows for a given layout.

=cut

sub total_rows
{
	my($self, %args) = @_;

	# Just do a findall with 1 record and parse the result. This might break on an empty database.
	my $xml = $self->_request(
		user      => $self->{user},
		pass      => $self->{pass},
		resultset => $self->{resultset},
		params    => {'-db' => $self->{db}, '-lay' => $args{layout}, '-max' => '1' },
		query     => '-findall'
	);

	return $xml;
}


1; # End of Net::FileMaker::XML::Database;

