/*
    This file is part of BOP.
    Copyright (C) 2004  Patrick Davalan

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    The GNU General Public License text is also available at
    http://www.gnu.org/
    or on the Copyright holder web site :
    http://patrick.davalan.free.fr/gnu-gpl.html
*/

#define DEBUG 0

#include <sys/times.h>

//FIXME comment the line below
//#define HAVE_RANDOM

// change the include to #include <bop.2/bop.h>
#include "bop.h"

typedef struct 
{
    BophHandle * hash ;         // hash to store lines
    unsigned long lineNum ;     // line number to make all lines different
} LoadData ;

static int load( void * data, char * line, unsigned int length)
{
#define loadData  ( (LoadData *) data )
    // this function is called for each input line.
    // put it in the hash
    size_t allocSize ;
    char * hashRecord ;
    BophEntry * entry ;         // stored hash entry
    unsigned long * pNum ;
    
    bopdEnter( ) ;
    bopdTrace(" line <%s> length=%u\n", line, length ) ;
    // prepend a line number to make all lines different
    loadData->lineNum += 1 ;
    allocSize = length + 2 + sizeof(unsigned long) ;
#ifndef HAVE_ALLOCA
    bopdTrace( "use bopmMalloc size=%u\n", allocSize ) ;
    hashRecord = bopmMalloc( allocSize) ;
#else
    bopdTrace( "use alloca size=%u\n", allocSize ) ;
    hashRecord = alloca( allocSize) ;
#endif
    pNum = ( unsigned long * ) hashRecord ;
    * pNum = loadData->lineNum ;
    memcpy( hashRecord + sizeof(unsigned long),
            line, length ) ;
    * (hashRecord + allocSize - 2) = '\n' ;
    * (hashRecord + allocSize - 1) = 0 ;
    
    // put it in hash
    entry = bophPut( loadData->hash, hashRecord, allocSize, NULL, 0, BOPH_PUT ) ;
    if ( entry == NULL )
    {
        bopxAbort( "Cannot store in hash" ) ;
    }

#ifndef HAVE_ALLOCA
    // the record has been copied in the hash, free it
    bopmFree( hashRecord ) ;
#endif
    
#undef loadData
    
    bopdReturn( false ) ;
    return( false ) ;
}


static int unload( void * data, BophEntry * entry )
{
    // this function is called for each hash entry.
    // output the line
    char * line ;
    
    bopdEnter( ) ;
    // do not output the line number
    
    line = ( (char *)bophGetKey( entry ) ) + sizeof(unsigned long) ;
    fputs( line, (FILE *)data ) ;
    bopdTrace( "fputs <%s>\n", line ) ; 
    
    bopdReturn( false ) ;
    return( false ) ;
}

int
main( int argc, char * argv[] )
{
    BophHandle * hash ;
    char * fileName ;
    unsigned int hashSize ;
    LoadData loadData ;

    bopmTrace ( ) ;

    bopdEnter( ) ;

    // get the filename argument
    if ( argc < 2 )
    {
        fprintf( stderr, "bopshuffle: argument expected\n" ) ;
        fprintf( stderr, "usage: bopshuffle filename\n" ) ;
        exit( EXIT_FAILURE ) ;
    }
    fileName = argv[1] ;

    // find a "random" hash size
    // que vient faire Marignan dans cette histoire ?
    hashSize = ( bopxRandom( ) % 8191 ) + 1515 ;

    // create a hash
    bopdTrace( "hash size %u\n", hashSize ) ;
    hash = bophNew( NULL, "shuffle hash", hashSize, NULL, NULL ) ;

    // load the file in the hash
    loadData.hash = hash ;
    loadData.lineNum = 0 ;
    bopxScanLines( &loadData, fileName, load ) ;

    // unload the hash on stdout
    bophScan( stdout, hash, unload ) ;
    
    // unusefully free the hash object
    bophDelete ( NULL, hash ) ;
    
    bopmMem( ) ;
    
    bopdReturn( EXIT_SUCCESS ) ;
    return( EXIT_SUCCESS ) ;
}