The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
##########################################################################
#                                                                        #
# © Copyright IBM Corporation 2001, 2004. All rights reserved.           #
#                                                                        #
# This program and the accompanying materials are made available under   #
# the terms of the Common Public License v1.0 which accompanies this     #
# distribution, and is also available at http://www.opensource.org       #
# Contributors:                                                          #
#                                                                        #
# Matt Lennon - Creation and framework.                                  #
#                                                                        #
# William Spurlin - Maintenance and defect fixes                         #
#                                                                        #
##########################################################################

=head1 NAME

CC::CompositeDiff - XXX

=cut

use CC::CC;
use CC::Activity;
use CC::Baseline;
use CC::Component;
# use Trace;
use strict;


##############################################################################
package CC::CompositeDiff;
##############################################################################

##############################################################################
sub new
##############################################################################
{
    # my $trace();
    my $class  = shift @_;
    my $result = shift @_;
    my $vob    = shift @_;
    my $this   = { };

    CC::CC::assert($result);

    # 'diffbl' results look like:
    #    << MyActivity "My Activity Headline"

    ($this->{kind}, $this->{act}) = split(' ', $result);

    $this->{act} = new CC::Activity($this->{act}, $vob);
    my $cleartool = ClearCase::CtCmd->new;
    $this->{cleartool}=$cleartool;
    $this->{status} = 0;
    return bless($this, $class);
}

##############################################################################
sub kind
##############################################################################
{
    my $this = shift @_;
    return $this->{kind};
}

##############################################################################
sub activity
##############################################################################
{
    my $this = shift @_;
    return $this->{act};
}

##############################################################################
sub print
##############################################################################
{
    my $this = shift @_;

    printf("%s %s \"%s\"\n",
           $this->kind(),
           $this->activity()->name(),
           $this->activity()->headline());

    return 1;
}


##############################################################################
package CC::BaselineDiff;
##############################################################################

##############################################################################
sub new
##############################################################################
{
    # my $trace();
    my $class = shift @_;
    my $lhs   = shift @_;
    my $rhs   = shift @_;
    my $this  = { };

    CC::CC::assert($lhs || $rhs);
    
    $this->{lhs} = $lhs;
    $this->{rhs} = $rhs;

    if ( ! $rhs) {
        $this->{kind} = '<<';
    } elsif ( ! $lhs) {
        $this->{kind} = '>>';
    } elsif ($lhs->equals($rhs)) {
        $this->{kind} = '==';
    } else {
        $this->{kind} = '<>';
    }

    return bless($this, $class);
}

##############################################################################
sub DESTROY
##############################################################################
{
    return 1;  # no-op
}

##############################################################################
sub lhs
##############################################################################
{
    my $this = shift @_;
    return $this->{lhs};
}

##############################################################################
sub rhs
##############################################################################
{
    my $this = shift @_;
    return $this->{rhs};
}

##############################################################################
sub kind
##############################################################################
{
    my $this = shift @_;
    return $this->{kind};
}

##############################################################################
sub print
##############################################################################
{
    my $this = shift @_;
    my $lhs  = $this->lhs();
    my $rhs  = $this->rhs();
    my $comp = ($lhs ? $lhs->component() : $rhs->component());

    printf("##### component %s:\t%20s %s %s\n",
           $comp->name(),
           ($lhs ? $lhs->name() : ''),
           $this->kind(),
           ($rhs ? $rhs->name() : ''));

    return 1;
}

##############################################################################
sub results
##############################################################################
{
    # my $trace();
    my $this  = shift @_;
    my $lhs   = $this->lhs();
    my $rhs   = $this->rhs();
    my $vob   = $lhs->vob();
    my @diffs;

    CC::CC::assert($this->kind() eq '<>');

    my @lines = $this->{cleartool}->exec('diffbl', $lhs->objsel(), $rhs->objsel());

    return  $this->{cleartool}->status? 0 : map { new CC::DiffResult($_, $vob); } @lines;
}


##############################################################################
package CC::CompositeDiff;
##############################################################################


##############################################################################
sub DESTROY
##############################################################################
{
    return 1;  # no-op
}

##############################################################################
sub lhs
##############################################################################
{
    my $this = shift @_;
    return $this->{lhs};
}

##############################################################################
sub rhs
##############################################################################
{
    my $this = shift @_;
    return $this->{rhs};
}

##############################################################################
sub results
##############################################################################
{
    # my $trace();
    my $this     = shift @_;
    my %lhs_hash = _component_to_operand_hash($this->lhs());
    my %rhs_hash = _component_to_operand_hash($this->rhs());
    my @comps    = _remove_duplicates(keys(%lhs_hash), keys(%rhs_hash));

    return map {
        new CC::BaselineDiff($lhs_hash{$_}, $rhs_hash{$_});
    } @comps;
}

##############################################################################
sub _component_to_operand_hash
##############################################################################
{
    my $op = shift @_;
    my ($dep, $comp);
    my %hash;

    if ($op->metatype() eq 'stream') {

        for $comp ($op->components()) {
            $hash{$comp->objsel()} = $op;
        }

    } elsif ($op->has_dependencies()) {

        for $dep ($op->dependencies()) {
            $hash{$dep->component()->objsel()} = $dep;
        }

    } else {

        $hash{$op->component()->objsel()} = $op;
    }

    return %hash;
}

##############################################################################
sub _remove_duplicates
##############################################################################
{
    my %item_hash;

    # Initialize hash using list items as keys, which eliminates duplicates.
    @item_hash{@_} = 0;

    # Reconsitute list from hash keys.
    return keys(%item_hash);
}

1;   # Make "use" and "require" happy