<?php
/* vim:set softtabstop=4 shiftwidth=4 expandtab: */
/**
 *
 * LICENSE: GNU Affero General Public License, version 3 (AGPLv3)
 * Copyright 2001 - 2017 Ampache.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * Still not fully tested but "Appears to work" use at your own risk
 * sort_files
 * This script has a lot of stuff to worry about. It's primary duty is to re-organize
 * your files based on some sane, and predefined (in the interface) order using the
 * tag information gathered and updated in ampache. Sort_Pattern defines the directory
 * structure and rename_pattern defines the file pattern. This script should allow you
 * to do both or neither. Oooh and allow you to sort with A,B,C,D prefix
 *
 * Attempt 1 - Do each file one by one and satisfy the needs of each file by its self (this is going to be slow)
 * Cache information so we don't have to check for every file!
 */

/* Don't do anything just tell me what you would do */
//$test_mode = true;

/* m(__)m */
$alphabet_prefix = true;

define('NO_SESSION','1');
$path = dirname(__FILE__);
$prefix = realpath($path . '/../');
require_once $prefix . '/lib/init.php';

ob_end_clean();

/* First Clean the catalog to we don't try to write anything we shouldn't */

$sql = "SELECT `id` FROM `catalog` WHERE `catalog_type`='local'";
$db_results = Dba::read($sql);

$catalogs = array();

while ($r = Dba::fetch_row($db_results)) {

    $catalog = Catalog::create_from_id($r['0']);
    $songs = $catalog->get_songs();

    printf (T_('Starting Catalog: %s'), stripslashes($catalog->name));
    echo "\n";

    /* Foreach through each file and find it a home! */
    foreach ($songs as $song) {
        /* Find this poor song a home */
        $song->format();
        $directory     = sort_find_home($song,$catalog->sort_pattern,$catalog->path);
        $filename     = $song->f_file;
        $fullpath = $directory . "/" . $filename;

        /* Check for Demo Mode */
        if ($test_mode) {
            /* We're just talking here... no work */
            echo T_("Moving File...");
            echo "\n\t";
            printf (T_('Source: %s'), $song->file);
            echo "\n\t";
            printf (T_('Dest: %s'), $fullpath);
            echo "\n";
            flush();
        }
        /* We need to actually do the moving (fake it if we are testing)
         * Don't try to move it, if it's already the same friggin thing!
         */
        if ($song->file != $fullpath && strlen($fullpath)) {
            sort_move_file($song,$fullpath);
        }

    } // end foreach song

} // end foreach catalogs

/************** FUNCTIONS *****************/
/**
 * sort_find_home
 * Get the directory for this file from the catalog and the song info using the sort_pattern
 * takes into account various artists and the alphabet_prefix
 */
function sort_find_home($song,$sort_pattern,$base) {

    $home = rtrim($base,"\/");
    $home = rtrim($home,"\\");

    /* Create the filename that this file should have */
    $album     = sort_clean_name($song->f_album_full);
    $artist = sort_clean_name($song->f_artist_full);
    $track    = sort_clean_name($song->track);
    $title    = sort_clean_name($song->title);
    $year    = sort_clean_name($song->year);
    $comment = sort_clean_name($song->comment);

    /* Do the various check */
    $album_object = new Album($song->album);
    $album_object->format();
    if ($album_object->artist_count != '1') {
        $artist = "Various";
    }

    /* IF we want a,b,c,d we need to know the first element */
    if ($GLOBALS['alphabet_prefix']) {
        $sort_pattern = preg_replace("/\/?%o\//","",$sort_pattern);
        $first_element = substr($sort_pattern,0,2);
        $element = sort_element_name($first_element);
        if (!$element) { $alphabet = 'ZZ'; }
        else {     $alphabet = strtoupper(substr(${$element},0,1)); }
        $alphabet = preg_replace("/[^A-Za-z0-9]/","ZZ",$alphabet);

        $home .= "/$alphabet";
    }

    /* Replace everything we can find */
    $replace_array = array('%a','%A','%t','%T','%y','%c');
    $content_array = array($artist,$album,$title,$track,$year,$comment);
    $sort_pattern = str_replace($replace_array,$content_array,$sort_pattern);

    /* Remove non A-Z0-9 chars */
    $sort_pattern = preg_replace("[^\\\/A-Za-z0-9\-\_\ \'\,\(\)]","_",$sort_pattern);

    $home .= "/$sort_pattern";

    return $home;

} // sort_find_home

/**
 * sort_element_name
 * gets the name of the %? in a yea.. too much beer
 */
function sort_element_name($key) {

    switch ($key) {
        case '%t':
            return 'title';
        break;
        case '%T':
            return 'track';
        break;
        case '%a':
            return 'artist';
        break;
        case '%A':
            return 'album';
        break;
        case '%y':
            return 'year';
        break;
        default:
        break;
    } // switch on key

    return false;

} // sort_element_name

/**
 * sort_clean_name
 * We have to have some special rules here
 * This is run on every individual element of the search
 * Before it is put togeather, this removes / and \ and also
 * once I figure it out, it'll clean other stuff
 */
function sort_clean_name($string) {

    /* First remove any / or \ chars */
    $string = preg_replace('/[\/\\\]/','-',$string);

    $string = str_replace(':',' ',$string);

    $string = preg_replace('/[\!\:\*]/','_',$string);

    return $string;

} // sort_clean_name

/**
 * sort_move_file
 * All this function does is, move the friggin file and then update the database
 * We can't use the rename() function of PHP because it's functionality depends on the
 * current phase of the moon, the alignment of the planets and my current BAL
 * Instead we cheeseball it and walk through the new dir structure and make
 * sure that the directories exist, once the dirs exist then we do a copy
 * and unlink.. This is a little unsafe, and as such it verifys the copy
 * worked by doing a filesize() before unlinking.
 */
function sort_move_file($song,$fullname) {

    $old_dir    = dirname($song->file);

    $info = pathinfo($fullname);

    $directory     = $info['dirname'];
    $file        = $info['basename'];
    $data = preg_split("/[\/\\\]/",$directory);
    $path = '';

    /* We not need the leading / */
    unset($data[0]);

    foreach ($data as $dir) {

        $dir = sort_clean_name($dir);
        $path .= "/" . $dir;

        /* We need to check for the existance of this directory */
        if (!is_dir($path)) {
            if ($GLOBALS['test_mode']) {
                echo "\t";
                printf (T_('Making %s Directory'), $path);
                echo "\n";
            }
            else {
                debug_event('mkdir',"Creating $path directory",'5');
                $results = mkdir($path);
                if (!$results) {
                    printf (T_('Error: Unable to create %s move failed'), $path);
                    echo "\n";
                    return false;
                }
            } // else we aren't in test mode
        } // if it's not a dir

    } // foreach dir

    /* Now that we've got the correct directory structure let's try to copy it */
    if ($GLOBALS['test_mode']) {
        echo "\t";
        // HINT: %1$s: file, %2$s: directory
        printf (T_('Copying %1$s to %2$s'), $file, $directory);
        echo "\n";
        $sql = "UPDATE song SET file='" . Dba::escape($fullname) . "' WHERE id='" . Dba::escape($song->id) . "'";
        echo "\tSQL: $sql\n";
        flush();
    }
    else {

        /* Check for file existance */
        if (file_exists($fullname)) {
            debug_event('file exists','Error: $fullname already exists','1');
            printf (T_('Error: %s already exists'), $filename);
            echo "\n";
            return false;
        }

        $results = copy($song->file,$fullname);
        debug_event('copy','Copied ' . $song->file . ' to ' . $fullname,'5');

        /* Look for the folder art and copy that as well */
        if (!AmpConfig::get('album_art_preferred_filename') OR strstr(AmpConfig::get('album_art_preferred_filename'),"%")) {
            $folder_art     = $directory . DIRECTORY_SEPARATOR . 'folder.jpg';
            $old_art     = $old_dir . DIRECTORY_SEPARATOR . 'folder.jpg';
        }
        else {
            $folder_art     = $directory . DIRECTORY_SEPARATOR . sort_clean_name(AmpConfig::get('album_art_preferred_filename'));
            $old_art     = $old_dir . DIRECTORY_SEPARATOR . sort_clean_name(AmpConfig::get('album_art_preferred_filename'));
        }

        debug_event('copy_art','Copied ' . $old_art . ' to ' . $folder_art,'5');
        @copy($old_art,$folder_art);

        if (!$results) { printf (T_('Error: Unable to copy file to %s'), $fullname); echo "\n"; return false; }

        /* Check the filesize */
        $new_sum = Core::get_filesize($fullname);
        $old_sum = Core::get_filesize($song->file);

        if ($new_sum != $old_sum OR !$new_sum) {
            printf (T_('Error: Size Inconsistency, not deleting %s'), $song->file);
            echo "\n";
            return false;
        } // end if sum's don't match

        /* If we've made it this far it should be safe */
        $results = unlink($song->file);
        if (!$results) { printf (T_('Error: Unable to delete %s'), $song->file); echo "\n"; }

        /* Update the catalog */
        $sql = "UPDATE song SET file='" . Dba::escape($fullname) . "' WHERE id='" . Dba::escape($song->id) . "'";
        $db_results = Dba::write($sql);

    } // end else

    return true;

} // sort_move_file

?>
