#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "util.h"
#include "vpr_types.h"
#include "globals.h"
#include "pack.h"
#include "read_blif.h"
#include "ff_pack.h"
#include "cluster.h"
#include "output_clustering.h"
#include "read_xml_arch_file.h"

/* #define DUMP_PB_GRAPH 1 */
/* #define DUMP_BLIF_INPUT 1 */

static void unclustered_stats (int max_lut_size);

void try_pack(INP struct s_packer_opts *packer_opts, INP const t_arch * arch, INP t_model *user_models, INP t_model *library_models) {
	boolean *is_clock;
	int num_models;
	t_model *cur_model;

	printf("Begin packing of %s \n", packer_opts->blif_file_name);

	/* determine number of models in the architecture */
	num_models = 0;
	cur_model = user_models;
	while(cur_model) {
		num_models++;
		cur_model = cur_model->next;
	}
	cur_model = library_models;
	while(cur_model) {
		num_models++;
		cur_model = cur_model->next;
	}

	/* begin parsing blif input file */
	read_blif (packer_opts->blif_file_name, 
				packer_opts->sweep_hanging_nets_and_inputs,
				user_models,
				library_models);
/* TODO: Do check blif here 
eg. 
 for(i = 0; i < num_logical_blocks; i++) {
	 if(logical_block[i].model->num_inputs > max_subblock_inputs) {
		 printf(ERRTAG "logical_block %s of model %s has %d inputs but architecture only supports subblocks up to %d inputs\n",
			 logical_block[i].name, logical_block[i].model->name, logical_block[i].model->num_inputs, max_subblock_inputs);
		 exit(1);
	 }
 }


*/
#ifdef DUMP_BLIF_INPUT
	echo_input (packer_opts->blif_file_name, "blif_input.echo", library_models);
#endif

	absorb_buffer_luts ();
	compress_netlist (); /* remove unused inputs */
	/* NB:  It's important to mark clocks and such *after* compressing the   *
	* netlist because the vpack_net numbers, etc. may be changed by removing      *
	* unused inputs .                                      */

	is_clock = alloc_and_load_is_clock (packer_opts->global_clocks);

	printf("\nAfter removing unused inputs:\n");
	printf("Total Blocks: %d.  Total Nets: %d.  Total inputs %d ouptuts %d\n", num_logical_blocks, num_logical_nets, 
	   num_p_inputs, num_p_outputs);


	/* Uncomment line below if you want a dump of compressed netlist. */
	/* echo_input (packer_opts->blif_file_name, packer_opts->lut_size, "packed.echo"); */

	if (packer_opts->skip_clustering == FALSE) {
		do_clustering (arch,
					num_models,
					packer_opts->global_clocks, 
					is_clock, 
					packer_opts->hill_climbing_flag,
					packer_opts->output_file, 
					packer_opts->timing_driven, 
					packer_opts->cluster_seed_type, 
					packer_opts->alpha, 
					packer_opts->beta,
					packer_opts->recompute_timing_after, 
					packer_opts->block_delay, 
					packer_opts->intra_cluster_net_delay, 
					packer_opts->inter_cluster_net_delay, 
					packer_opts->aspect,
					packer_opts->allow_unrelated_clustering, 
					packer_opts->allow_early_exit, 
					packer_opts->connection_driven,
					packer_opts->packer_algorithm,
					packer_opts->hack_no_legal_frac_lut,
					packer_opts->hack_safe_latch);
	}
	else {
		printf("Skip clustering not supported\n");
		exit(1);
	}

	free (is_clock);

	saved_logical_blocks = logical_block;
	logical_block = NULL;
	num_saved_logical_blocks = num_logical_blocks;
	saved_logical_nets = vpack_net;
	vpack_net = NULL;
	num_saved_logical_nets = num_logical_nets; /* Todo: Use this for error checking */
	
	printf("\nNetlist conversion complete.\n\n");
}


void unclustered_stats (int max_lut_size) {

/* Dumps out statistics on an unclustered netlist -- i.e. it is just *
 * packed into VPACK_LUT + FF logic blocks, but not local routing from     *
 * output to input etc. is assumed.                                  */
	assert(0);
#if 0
 int iblk, num_logic_blocks, num_subckts;
 int min_inputs_used, min_clocks_used;
 int max_inputs_used, max_clocks_used;
 int summ_inputs_used, summ_clocks_used;
 int inputs_used, clocks_used;

 printf("\nUnclustered Netlist Statistics:\n\n");
 num_logic_blocks = num_logical_blocks - num_p_inputs - num_p_outputs;
 printf("%d Logic Blocks.\n", num_logic_blocks);

 min_inputs_used = max_lut_size+1;
 min_clocks_used = 2;
 max_inputs_used = -1;
 max_clocks_used = -1;
 summ_inputs_used = 0;
 summ_clocks_used = 0;
 num_subckts = 0;

 for (iblk=0;iblk<num_logical_blocks;iblk++) {
	 if (strcmp(logical_block[iblk].model->name, MODEL_LOGIC) == 0 || 
		 strcmp(logical_block[iblk].model->name, MODEL_LATCH) == 0) {
	   assert(logical_block[iblk].type == VPACK_COMB || logical_block[iblk].type != VPACK_LATCH);
       inputs_used = logical_block[iblk].num_input_nets;
	   if (logical_block[iblk].clock_net != OPEN)
          clocks_used = 1;
       else 
          clocks_used = 0;

       min_inputs_used = min (min_inputs_used, inputs_used);
       max_inputs_used = max (max_inputs_used, inputs_used);
       summ_inputs_used += inputs_used;

       min_clocks_used = min (min_clocks_used, clocks_used);
       max_clocks_used = max (max_clocks_used, clocks_used);
       summ_clocks_used += clocks_used;
    }
	 else if (strcmp(logical_block[iblk].model->name, MODEL_INPUT) != 0 &&
			  strcmp(logical_block[iblk].model->name, MODEL_OUTPUT) != 0 ) {
		assert(logical_block[iblk].type == VPACK_COMB);
		num_subckts++;
	}
 }

 printf("\n\t\t\tAverage\t\tMin\tMax\n");
 printf("Logic Blocks / Cluster\t%f\t%d\t%d\n", 1., 1, 1);
 printf("Used Inputs / Cluster\t%f\t%d\t%d\n", (float) summ_inputs_used /
        (float) num_logic_blocks, min_inputs_used, max_inputs_used);
 printf("Used Clocks / Cluster\t%f\t%d\t%d\n", (float) summ_clocks_used /
        (float) num_logic_blocks, min_clocks_used, max_clocks_used);
 printf("Subcircuits in design \t%d\n", num_subckts);
 printf("\n");
#endif
}

