# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Test Runner System.
#
# The Initial Developer of the Original Code is Maciej Maczynski.
# Portions created by Maciej Maczynski are Copyright (C) 2001
# Maciej Maczynski. All Rights Reserved.
#
# Contributor(s): David Koenig <dkoenig@novell.com>

=head1 NAME

Bugzilla::Testopia::XmlTestCase - Testopia XmlTestCase object

=head1 DESCRIPTION

This module parsers a XML representation of a Testopia test plan,
test case, test environment, category or build and returns Testopia
objects re

=head1 SYNOPSIS

use Bugzilla::Testopia::XmlTestCase;

=cut

package Bugzilla::Testopia::XmlTestCase;
#use fields qw(testplans testcases tags categories builds);

use strict;

use Bugzilla::Product;
use Bugzilla::Testopia::Attachment;
use Bugzilla::Testopia::Build;
use Bugzilla::Testopia::Category;
use Bugzilla::Testopia::TestCase;
use Bugzilla::Testopia::TestPlan;
use Bugzilla::Testopia::TestRun;
use Bugzilla::Testopia::TestTag;
use Bugzilla::Testopia::Util;
use Bugzilla::Testopia::XmlReferences;
use Bugzilla::User;
use Bugzilla::Util;

use Class::Struct;
#
# The XmlTestCase structure stores data for the verfication processing.  The database is not updated
# until a verfication pass is made through the XML data.  Some of the TestCase class references are
# database references that will not be valid until the class has been stored in the database.  This
# structure stores these references to be used during verfication and writting to the database. 
# 
struct
(
	'Bugzilla::Testopia::XmlTestCase',
	{
		attachments					=> '@',
		blocks						=> 'Bugzilla::Testopia::XmlReferences',
		category					=> '$',
		component_ids				=> '@',
		dependson					=> 'Bugzilla::Testopia::XmlReferences',
		tags						=> '@',
		testcase					=> 'Bugzilla::Testopia::TestCase',
		testplan					=> 'Bugzilla::Testopia::XmlReferences',
	}
);

sub add_attachment()
{
	my ($self,$tag) = @_;
		
	push @{$self->attachments}, $tag;
}

sub add_tag()
{
	my ($self,$tag) = @_;
		
	push @{$self->tags}, $tag;
}

=head2 get_available_products

Returns a list of products.  This is the same code as Bugzilla::Testopia::TestPlan->get_available_products
without view restrictions.

=cut

sub get_available_products {
    my $dbh = Bugzilla->dbh;
    
    my $products = $dbh->selectall_arrayref(
            "SELECT id, name 
               FROM products
           ORDER BY name", 
             {"Slice"=>{}});
    return $products;
}

sub add_component()
{
	my ($self,$component,$component_product) = @_;
	my $component_id = "";
	my $product_id = "";
	
	return "Component $component needs to provide a product." if ( $component_product eq "" );
	
	# Find the product identifier.
	my $products_ref = get_available_products();
	foreach my $product (@$products_ref)
	{
		if ( $component_product eq $product->{name} )
		{
			$product_id = $product->{id};
			last;
		}
	}
	return "Cannot find product $component_product for component $component." if ( $product_id eq "" );
	
	# Find the component identifier for the product's componet
	my $components_ref = Bugzilla::Testopia::TestPlan->get_product_components($product_id,1);
	foreach my $product_component ( @$components_ref )
	{
		if ( $component eq $product_component->{name} )
		{
			$component_id = $product_component->{id};
			last;
		}
	}
	return "Product $component_product does not have a component named $component." if ( $component_id eq "" );	
	
	# Save the component identifier for this Test Case.		
	push @{$self->component_ids}, $component_id;
	
	return "";
}

sub debug_display()
{	
	my ($self) = @_;
	my $display_variable = "";

    my %text = %{$self->testcase->text()} if ( defined $self->testcase->text() );
	print STDERR "Testcase: " . $self->testcase->summary() . "\n";
	my $testcase_id = "null";
	$testcase_id = $self->testcase->id() if ( $self->testcase->id() );
	print STDERR "   ID                      " . $testcase_id . "\n";
	print STDERR "   Action\n";
	if ( defined $text{'action'} )
	{
		my @results = split(/\n/,$text{'action'});
		foreach my $result (@results)
		{
			print STDERR "      $result\n";
		}
	}
	my $alias = "null";
	$alias = $self->testcase-alias() if ( $self->testcase->alias() );
	print STDERR "   Alias                   " . $alias . "\n";
	my %author = %{$self->testcase->author()};
	my $author_id = $author{"id"};
	my $author_login = $author{"login"};
	print STDERR "   Author                  " . $author_login . " (id=" . $author_id . ", hash=" . $self->testcase->author() . ")\n";
	print STDERR "   Blocks                  " . $self->blocks->display() . "\n";
	print STDERR "   Category                " . $self->category . "\n";
	print STDERR "   Depends On              " . $self->dependson->display() . "\n";
	print STDERR "   Expected Results\n";
	if ( defined $text{'effect'} )
	{
		my @results = split(/\n/,$text{'effect'});
		foreach my $result (@results)
		{
			print STDERR "      $result\n";
		}
	}
	print STDERR "   Summary                 " . $self->testcase->summary() . "\n";
	#TODO:print STDERR "   Default Product Version " . $self->testcase->product_version() . "\n";
	#TODO:print STDERR "   Document                " . $self->testcase->text() . "\n";
	my %tester = %{$self->testcase->default_tester()};
	my $tester_id = $tester{"id"};
	my $tester_login = $tester{"login"};
	print STDERR "   Tester                  " . $tester_login . " (id=" . $tester_id . ", hash=" . $self->testcase->default_tester() . ")\n";
	print STDERR "   Is Automated            " . $self->testcase->isautomated() . "\n";
	#TODO:print STDERR "   Plans                   " . $self->testcase->plans() . "\n";
	#TODO:print STDERR "   Priority                " . $self->testcase->priority_id() . "\n";
	#TODO:print STDERR "   Product                 " . $self->testcase->product_id() . "\n";
	print STDERR "   Requirement             " . $self->testcase->requirement() . "\n";

	print STDERR "   Script                  " . $self->testcase->script() . "\n";
	print STDERR "   Script Arguments        " . $self->testcase->arguments() . "\n";
	print STDERR "   Status                  " . $self->testcase->status() . "\n";
				
	foreach my $tag (@{$self->tags})
	{
		print STDERR "   Tag                     " . $tag . "\n";
	}
		
	my @attachments = @{$self->testcase->attachments()};
	foreach my $attachment (@attachments)
	{
		my %submitter = %{$self->testcase->submitter()};
		$author_login = $author{"login"};
		print STDERR "   Attachment                     " . $attachment->description() . "\n";
		print STDERR "      Filename                    " . $attachment->filename() . "\n";
		print STDERR "      Mime Type                   " . $attachment->mime_type(). "\n";
		print STDERR "      Submitter                   " . $author_login . "\n";
	}
}

sub get_testcase_ids()
{
	my ($self, $field, @new_testcases) = @_;
	my $error_message = "";
	
	my @testcase_id = @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)};
	
	foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} )
	{
		foreach my $testcase (@new_testcases)
		{
			push @testcase_id, $testcase->testcase->id() if ( $testcase->testcase->summary() eq $testcase_summary );
		}
	}
	
	#TODO Testplans using Database_Description
	foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} )
	{
			$error_message .= "\n" if  ( $error_message ne "" );
			$error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup for blocking test case " . $testcase_summary . "' for Test Case '". $self->testcase->summary . "'.";
	}
	return $error_message if ( $error_message ne "" );
	
	my @return_testcase_id;
	foreach my $testcase_id (@testcase_id)
	{
		my $testcase = Bugzilla::Testopia::TestCase->new($testcase_id);
		if ( ! defined($testcase) )
		{
			$error_message .= "\n" if  ( $error_message ne "" );
			$error_message .= "Could not find blocking Test Case '" . $testcase_id . "' for Test Case '". $self->testcase->summary . "'.";
		}
		else
		{
			push @return_testcase_id, $testcase->id();
		}
	}
	return $error_message if ( $error_message ne "" );
	
	return @return_testcase_id;
}

sub store()
{
	my ($self, @new_testplans) = @_;
	my $error_message = "";
	my @testplan_id = @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)};
	
	# If we have any references to test plans from the XML data we need to search the @new_testplans
	# array to find the actual test plan id.  The new_testplans array contains all test plans created
	# from the XML.
	# Order of looping does not matter, number of test plans associated to each test case should be small.
	foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} )
	{
		foreach my $testplan (@new_testplans)
		{
			push @testplan_id, $testplan->id() if ( $testplan->name() eq $testplan_name );
		}
	}
	
	#TODO Testplans using Database_Description
	foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} )
	{
			$error_message .= "\n" if  ( $error_message ne "" );
			$error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup of test plan " . $testplan_name . "' for Test Case '". $self->testcase->summary . "'.";
	}
	return $error_message if ( $error_message ne "" );
	
	# Have to have a testplan to determine valid categories for testcase.
	return "Test Case '" . $self->testcase->summary . "' needs a Test Plan." if ( $#testplan_id == -1 );
	
	# Verify that each testplan exists.
	my @testplan;
	foreach my $testplan_id (@testplan_id)
	{
		my $testplan = Bugzilla::Testopia::TestPlan->new($testplan_id);
		if ( ! defined($testplan) )
		{
			$error_message .= "\n" if  ( $error_message ne "" );
			$error_message .= "Could not find Test Plan '" . $testplan_id . "' for Test Case '". $self->testcase->summary . "'.";
		}
		else
		{
			push @testplan, $testplan;
		}
	}
	return $error_message if ( $error_message ne "" );
	
	# Verify that each testplan has the testcase's category associated to it.  If the category does not
	# exist it will be created.
	foreach my $testplan (@testplan)
	{
		my $categoryid = -1;
		
		push my @categories, @{$testplan->product->categories};
    	foreach my $category (@categories)
    	{
    		if ( $category->name eq $self->category )
			{
				$categoryid = $category->id;
				last;
			}
    	}
    	if ( $categoryid == -1 )
    	{
    		my $new_category = Bugzilla::Testopia::Category->new({
    			product_id  => $testplan->product_id,
				name        => $self->category,
				description => "FIX ME.  Created during Test Plan import."
			});
			$categoryid = $new_category->store();
    	}
    	$self->testcase->{'category_id'} = $categoryid if ( ! defined($self->testcase->{'category_id'}) );
    }
	my $case_id = $self->testcase->store();
	$self->testcase->{'case_id'} = $case_id;
	foreach my $attachment ( @{$self->attachments} )
	{
		$attachment->{'case_id'} = $case_id;
		$attachment->store();
	}
	foreach my $asciitag ( @{$self->tags} )
	{
		my $classtag = Bugzilla::Testopia::TestTag->new({'tag_name' => $asciitag});
		my $tagid = $classtag->store;
		$self->testcase->add_tag($tagid);
	}
	
	# Link the testcase to each of it's testplans.
	foreach my $testplan ( @testplan )
	{
		$self->testcase->link_plan($testplan->id(),$case_id)
	}
	
	# Code below requires the testplans to be linked into testcases before being run.
	foreach my $component_id ( @{$self->component_ids} )
	{
		$self->testcase->add_component($component_id);
	}
	
	return $error_message;
}

sub store_relationships()
{
	my ($self, @new_testcases) = @_;

	my $blocks = join(' ',$self->get_testcase_ids("blocks",@new_testcases));
    my $dependson = join(' ',$self->get_testcase_ids("dependson",@new_testcases));

	if ( $blocks ne "" || $dependson ne "" )
	{
		# Need to add the current blocks and dependson from the Test Case; otherwise, they will
		# be removed.
		$blocks .= $self->testcase->blocked_list_uncached();
    	$dependson .= $self->testcase->dependson_list_uncached();
		$self->testcase->update_deps($dependson,$blocks);
	}
}

1;
