###################################################################
# Copyright (C) 2006 RoyaltyShare, Inc.  All Rights Reserved
# $Id$
###################################################################

package Common::File;

###
#   Common::File
#
#   Provides functions to search for files in a specified path.  It
#   also will provide basic load balancing over drives.  This is a
#   Cross platform library and should work on Linux or Windows.
#
#   Usage:
#
#   This object can be used through static calls or using an object.
#
#   == Via Object Interface ==
#
#   my $path = new Common::File( path => \@search_path );
#
#   # Search the path for a file or partial file path
#   my $filepath = $path->search( $filename )
#
#   # Get a path to write to based on drive utilization
#   my $filepath = $path->balance( $filename );
#
#   == Via Object Interface ==
#
#   # Search the path for a file or partial file path
#   my $filepath = Common::File::Search( $filename, \@search_path );
#
#   # Get a path to write to based on drive utilization
#   my $filepath = Common::File::Balance( $filename, \@search_path );
#
#
#   Examples:
#
#      my @path = qw( C:/media D:/media E:/media );
#      my $filename = "service/client/000/000/file.dat";
#
#      my $filepath = Search( $filename, \@path );
#
#      # note if the file is found in the path the fill path to the file will
#      # be returned.  If the file is not found then undef is returned.
#
#      my $filepath = Balance( $filename, \@search_path );
#
#      # the full path to the file will be returned on the drive with the most
#      # available space.
#
###

use strict;
use warnings;

use Carp;

use Filesys::DfPortable;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::Log;

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

    return $self->_init(%args);
}

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

    assert( $args{path}, "Path required" );

    $self->{_path} = $args{path};

    return $self;
}

sub search {
    my $self = shift;
    my $file = shift;
    return Config::File::Search( $file, $self->{_path} );
}

sub balance {
    my $self = shift;
    my $file = shift;
    return Config::File::Balance( $file, $self->{_path} );
}

sub Search {
    my $filename = shift;
    my @path = ref( $_[0] ) ? @{ $_[0] } : @_;

    assert(@path);

    $filename =~ s/^[\/\\]//;
    Common::Log::Debug(" - Search for $filename in @path");

    for my $dir (@path) {
        $dir =~ s/[\/\\]$//;
        Common::Log::Debug(" - does '$dir/$filename' exist?");
        return "$dir/$filename" if ( -e "$dir/$filename" );
    }

    return;
}

sub Balance {
    my $filename = shift;
    my @path = ref( $_[0] ) ? @{ $_[0] } : @_;
    my ( $most_space_path, $most_free_space );
    my $driveinfo;
    my $free;

    $most_free_space = 0;

    assert(@path);

    $filename =~ s/^[\/\\]//;
    Common::Log::Debug(" - Find write drive for $filename in @path");

    for my $dir (@path) {
        $dir =~ s/[\/\\]$//;

        Common::Log::Debug(" - Checking drive space on left for $dir");

        $driveinfo = dfportable($dir);

        unless ($driveinfo) {
            print STDERR "WARNING: could not stat drive: $dir\n";
            next;
        }

        $free = $driveinfo->{bfree};

        Common::Log::Debug(" - $dir (free: $free)");

        if ( $free > $most_free_space ) {
            $most_free_space = $free;
            $most_space_path = $dir;
        }
    }

    return "$most_space_path/$filename";
}

1;
