/*
 * Copyright 2002-2004 by Hans Reiser, licensing governed by 
 * reiserfsprogs/README
 */

#include "debugreiserfs.h"
#include <search.h>
#include <obstack.h>

#define obstack_chunk_alloc malloc
#define obstack_chunk_free free


/* try to find blocks which contain only items which are */


/* read blocks marked in debug_bitmap and collect statistic of it:
 number of stat data */
struct {
    unsigned long all;
    unsigned long items [5];
    unsigned long unique_keys; /* keys of stat datas */
    unsigned long unique_entry_keys; /* keys which appear in directory entries */
    unsigned long names; /* dir entries but "." and ".." */
    unsigned long dir_blocks; /* block containing only one directory item */
    unsigned long unique_items;
    unsigned long leaves;
    unsigned long blocks_to_skip;
} fs_stat;


/* store unique item heads */
struct obstack items;

/* tree sorting item heades by comp_items_1 */
void * items_tree;


static int comp_items_1 (const void * p1, const void * p2)
{
    int retval;
    struct item_head * ih1, * ih2;

    /*    
    if (*(int *)p1 != *(int *)p2)
      retval = 1;
    else
      retval = 0;
    */
    retval = comp_keys (p1, p2);
    /*retval = comp_short_keys (p1, p2);*/
    if (retval)
	return retval;


    ih1 = (struct item_head *)p1;
    ih2 = (struct item_head *)p2;

    if (get_ih_item_len (ih1) < get_ih_item_len (ih2))
	return -1;
    if (get_ih_item_len (ih1) > get_ih_item_len (ih2))
	return 1;
    if (get_ih_entry_count (ih1) < get_ih_entry_count (ih2))
	return -1;
    if (get_ih_entry_count (ih1) > get_ih_entry_count (ih2))
	return 1;

    return 0;
}


/*
static void print_node (const void *nodep, VISIT value, int level)
{
    int i;

    if (value == leaf) {
	for (i = 0; i < level; i ++)
	    reiserfs_warning (stdout, "\t");
	reiserfs_warning (stdout, "%H\n", *(struct item_head **)nodep);
	return;
    }
    if (value == postorder) {
	for (i = 0; i < level; i ++)
	    reiserfs_warning (stdout, "\t");
	reiserfs_warning (stdout, "%H\n", *(struct item_head **)nodep);
    }
}
*/


static int is_unique_item (struct obstack * ostack, void ** tree, void * ih)
{
    void * res;
    void * key1;

    key1 = obstack_copy (ostack, ih, IH_SIZE);
    res = tsearch (key1, tree, comp_items_1);
    if (!res)
	reiserfs_panic ("Too many keys found");

/*    twalk (*tree, print_node);*/
/*    reiserfs_warning (stderr, "\n\n");*/
    if (*(void **)res != key1) {
	/* key is in tree already, remove it from obstack */
      /*reiserfs_warning (stdout, "%H is skipped\n", ih);fflush (stdout);*/
	obstack_free (ostack, key1);
	return 0;
    }

    /*reiserfs_warning (stdout, "%k is added\n", ih);fflush (stdout);*/
    return 1;
}


static void stat1_the_leaf (reiserfs_filsys_t * fs, struct buffer_head * bh)
{
    int i, i_num;
    struct item_head * ih;
    int is_there_unique_item;


    ih = B_N_PITEM_HEAD (bh, 0);
    is_there_unique_item = 0;
    i_num = leaf_item_number_estimate(bh);
    for (i = 0; i < i_num; i ++, ih ++) {
	/* count all items */
	fs_stat.all ++;

	if (is_unique_item (&items, &items_tree, ih)) {
	    /* this is item we have not seen yet */
	    fs_stat.unique_items ++;
	    is_there_unique_item ++;
	}
    }

    if (!is_there_unique_item) {
	/* the node contains only items we have seen already. so we will skip
           it */
	fs_stat.blocks_to_skip ++;
	reiserfs_bitmap_clear_bit (input_bitmap (fs), bh->b_blocknr);
    } else {
	ih = B_N_PITEM_HEAD (bh, 0);
	/* node contains at least one unique item. We will put it in, count items of each type */
	for (i = 0; i < i_num; i ++, ih ++) {
	    fs_stat.items [get_type (&ih->ih_key)] ++;
	}
    }
}

/*
static void stat2_the_leaf (struct buffer_head * bh)
{
    int i;
    struct item_head * ih;

    ih = B_N_PITEM_HEAD (bh, 0);
    for (i = 0; i < node_item_number (bh); i ++, ih ++) {
	
    }
}
*/

void do_stat (reiserfs_filsys_t * fs)
{
    unsigned long i;
    unsigned long done, total;
    struct buffer_head * bh;
    int type;
    FILE * fp;


    obstack_init (&items);
    items_tree = 0;

/*
    bh = bread (fs->s_dev, 8211, fs->s_blocksize);
    stat1_the_leaf (fs, bh);
    
    return;
*/

    /* pass 0 of stating */
    total = reiserfs_bitmap_ones (input_bitmap (fs));
    done = 0;
    for (i = 0; i < get_sb_block_count (fs->fs_ondisk_sb); i ++) {
	if (!reiserfs_bitmap_test_bit (input_bitmap (fs), i))
	    continue;

	print_how_far (stderr, &done, total, 1, be_quiet (fs));

	bh = bread (fs->fs_dev, i, fs->fs_blocksize);
	if (!bh) {
	    printf ("could not read block %lu\n", i);
	    continue;
	}
	type = who_is_this (bh->b_data, bh->b_size);
	if (type != THE_LEAF && type != HAS_IH_ARRAY) {
	    reiserfs_bitmap_clear_bit (input_bitmap (fs), i);
	    brelse (bh);
	    continue;
	}
	fs_stat.leaves ++;
	stat1_the_leaf (fs, bh);
	brelse (bh);
    }

    reiserfs_warning (stderr, "\nThere were found on the '%s' device:\n"
		      "\tleaves %lu\n"
		      "\ttotal number of items %lu\n"
		      "\tblocks containing at least one unique item %lu\n"
		      "\tblocks which can be skipped %lu\n"
		      "\t\tstat data %lu\n"
		      "\t\tindirect %lu\n"
		      "\t\tdirect %lu\n"
		      "\t\tdirectory items %lu\n"
		      "\tunique items %lu\n",
		      /*
		      "\tnames there (but \".\" and \"..\") %lu\n"
		      "\tpointing to unique keys %lu\n"
		      "other items %lu\n"
		      "blocks containing only 1 dir item %lu\n",
		      */
		      fs->fs_file_name,
		      fs_stat.leaves,
		      fs_stat.all,
		      fs_stat.leaves - fs_stat.blocks_to_skip,
		      fs_stat.blocks_to_skip,
		      fs_stat.items [TYPE_STAT_DATA],
		      fs_stat.items [TYPE_INDIRECT],
		      fs_stat.items [TYPE_DIRECT],
		      fs_stat.items [TYPE_DIRENTRY],
		      fs_stat.unique_items);
/*
		      fs_stat.names,
		      fs_stat.unique_keys,
		      fs_stat.items [4],
		      fs_stat.dir_blocks);
*/
    if (!input_bitmap_file_name(fs))
	return;

    fp = fopen (input_bitmap_file_name(fs), "w");
    if (!fp) {
	reiserfs_exit (1, "could not open %s to save bitmap: %m\n",
		       input_bitmap_file_name(fs));
    }
    reiserfs_warning (stderr, "Updated bitmap contains %u blocks marked\n",
		      reiserfs_bitmap_ones (input_bitmap (fs)));
    
    reiserfs_bitmap_save (fp, input_bitmap (fs));
    fclose (fp);
    return;


    /* pass 2 of stating */
    reiserfs_warning (stderr, "Looking for blocks containing only keys not pointed by any of entries\n");
    total = reiserfs_bitmap_ones (input_bitmap (fs));
    done = 0;
    for (i = 0; i < get_sb_block_count (fs->fs_ondisk_sb); i ++) {
	if (!reiserfs_bitmap_test_bit (input_bitmap (fs), i))
	    continue;

	print_how_far (stderr, &done, total, 1, be_quiet (fs));

	bh = bread (fs->fs_dev, i, fs->fs_blocksize);
	if (!bh) {
	    printf ("could not read block %lu\n", i);
	    continue;
	}
	/*stat2_the_leaf (bh);*/
    }

}
