#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2011 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------

package Report::Dynamic::Factory;
use strict;
use warnings;

use Data::Dumper;

use lib '/app/tools/common/lib';
use lib '/app/tools/report/lib';

use Report::DB::Item::Report;    # !!! I'll uncomment this once the class exists
use Common::Log;
use Common::Assert;
use Common::Exception;
use Common::Client;

#
#  ((((((((((  Public Interface
#

# my $report = Report::Factory->Fetch($reportID);
#
# Returns a reference to a Report object, blessed
# into the correct subclass.
#
sub Fetch {
    my ( $class, $id ) = @_;
    assert($id);

    # Load the db record.
    #
    my $dbItem = Report::DB::Item::Report->Lookup( report_id => $id );

    #die Common::Exception->new("unable to lookup Report with id $id") unless $dbItem;
    return unless ($dbItem);    # The non-existance of a report is something we need as well

    # Determine the actual class/package name
    #
    my $subclass = $class->ClassFromGroupType( $dbItem->report_group(), $dbItem->report_type() );
    return undef unless $subclass;

    #    eval "require $subclass"; # !!! I already 'eval'd' this into scope...
    my $reportObj = $subclass->new( dbItem => $dbItem );

    return $reportObj;
}

# my $class = Report::Factory->ClassFromGroupType($group, $type);
#
# Returns the class (aka Package) that matches the given group and type.
#
sub ClassFromGroupType {
    my ( $class, $group, $type ) = @_;

    assert($group);
    assert($type);

    # I'm not going to cache this stuff - Just going to build this on request.
    # ... And I'm just going to iterate over all possible classes until I find
    #     the one that matches.
    # This wouldn't be very good if I expected to call this over and over again, but
    # I don't expect to call this more than once per web request.  We can always
    # add in a cache if we want to.
    #
    my $classes = $class->_Classes();
    foreach my $class (@$classes) {
        eval "require $class";

        die $@ if ($@);

        if ( $class->Group() eq $group && $class->Type() eq $type ) {
            return $class;
        }
    }

    # Can't find it?   Throw an exception.
    #
    # JPK - From time to time report types will be retired or removed
    #
    #    die Common::Exception->new("ERROR - Unable to find a class matching group $group, type $type");
    Common::Log::Print("Unable to find a class matching group $group, type $type - Skipping")
      unless ( "$group:$type" =~ m/^Catalog:Albums Only|Catalog:Albums & Tracks$/ );
    return undef;
}

# Returns XML (actually just a hash reference) that lists the groups and types of reports
# available for the given access level.
#
sub GetGroupTypeXML {
    my ( $class, $userLevel ) = @_;
    assert($userLevel);

    # Fetch the current Client object. We'll need this to test against the client's type mask.
    #
    my $client = Common::Client::Current();

    # I don't think we want to bother caching this stuff.
    # We'll just build it every time.
    #
    # First I'll build a nested hash structure, then 'unroll' it to create friendlier XML.
    #
    my %groupTypeHash;
    my @groupArray;
    my $classes = $class->_Classes();
    foreach my $class (@$classes) {

        # !!! This is safe to do more than once. Perl just ignores this if we've loaded the package already.
        # !!! We might be able to lose this if we require 'use' statements in the derived Factory class.
        #
        eval "require $class";
        if ($@) {
            die $@;
        }
        if ( $client->isClientType( $class->ClientTypeMask() ) && $class->UserLevelIsValid($userLevel) ) {
            my $group = $class->Group();
            my $type  = $class->Type();
            if ( !$groupTypeHash{$group} ) {
                $groupTypeHash{$group} = [];
                push @groupArray, $group;
            }
            push @{ $groupTypeHash{$group} }, $type;
        }
    }

    my %outHash;
    foreach my $group (@groupArray) {
        my %groupData;
        $groupData{'Name'} = $group;
        $groupData{'TypeList'}{'Type'} = $groupTypeHash{$group};
        push @{ $outHash{'GroupList'}{'Group'} }, \%groupData;
    }

    return \%outHash;
}

# List all the base report directories used to store reports. This is used in
# orphaned report cleanup.
sub GetReportDirs {
    my $class         = shift;
    my $reportClasses = $class->_Classes();

    my %dirs;

    foreach my $report (@$reportClasses) {
        eval "require $report";
        die $@ if ($@);

        $dirs{ $report->ReportDir } = 1;
    }

    return keys(%dirs);

}

# So, we are going to have classes 'register' with the Factory.
# Basically, there will be a virtual class method that must be overridden, which
# will be a list of class package names.
#
sub _Classes {
    assert( 0, 'override' );

    # ... should look something like this:
    #
    # return
    # [
    #    'RPS::Report::Dynamic::Catalog::Foobar',
    #    'RPS::Report::Dynamic::Catalog::Barfoo',
    #    'RPS::Report::Dynamic::Payee::Blah',
    #    ...
    # ];
}

my $gTypeHash;

sub _TypeHash {

    # We'll build this once, lazilly.
    #
    if ( !$gTypeHash ) {
    }
}

1;
