#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "hash.h"
#include "vpr_types.h"
#include "OptionTokens.h"
#include "ReadOptions.h"
#include "read_settings.h"
#include "globals.h"

static boolean EchoEnabled;

static boolean Generate_PostSynthesis_Netlist;

static boolean *echoFileEnabled = NULL;
static char **echoFileNames = NULL;

static char **outputFileNames = NULL;

/******** Function prototypes ********/

static char **ReadBaseToken(INP char **Args, OUTP enum e_OptionBaseToken *Token);
static void Error(INP const char *Token);
static char **ProcessOption(INP char **Args, INOUTP t_options * Options);
static void MergeOptions(INOUTP t_options * dest, INP t_options * src, int id);
static char **ReadFloat(INP char **Args, OUTP float *Val);
static char **ReadInt(INP char **Args, OUTP int *Val);
static char **ReadOnOff(INP char **Args, OUTP boolean * Val);
static char **ReadClusterSeed(INP char **Args, OUTP enum e_cluster_seed *Type);
static char **ReadFixPins(INP char **Args, OUTP char **PinFile);
static char **ReadPlaceAlgorithm(INP char **Args,
		OUTP enum e_place_algorithm *Algo);
static char **ReadRouterAlgorithm(INP char **Args,
		OUTP enum e_router_algorithm *Algo);
static char **ReadPackerAlgorithm(INP char **Args,
		OUTP enum e_packer_algorithm *Algo);
static char **ReadBaseCostType(INP char **Args,
		OUTP enum e_base_cost_type *BaseCostType);
static char **ReadRouteType(INP char **Args, OUTP enum e_route_type *Type);
static char **ReadString(INP char **Args, OUTP char **Val);

/******** Globally Accessible Function ********/
/* Determines whether timing analysis should be on or off. 
 Unless otherwise specified, always default to timing.
 */
boolean IsTimingEnabled(INP t_options *Options) {
	/* First priority to the '--timing_analysis' flag */
	if (Options->Count[OT_TIMING_ANALYSIS]) {
		return Options->TimingAnalysis;
	}
	return TRUE;
}

/* Determines whether file echo should be on or off. 
 Unless otherwise specified, always default to on.
 */
boolean IsEchoEnabled(INP t_options *Options) {
	/* First priority to the '--echo_file' flag */
	if (Options->Count[OT_CREATE_ECHO_FILE]) {
		return Options->CreateEchoFile;
	}
	return FALSE;
}


boolean getEchoEnabled(void) {
	return EchoEnabled;
}

void setEchoEnabled(boolean echo_enabled) {
	/* enable echo outputs */
	EchoEnabled = echo_enabled;
	if(echoFileEnabled == NULL) {
		/* initialize default echo options */
		alloc_and_load_echo_file_info();
	}
}

boolean GetPostSynthesisOption(void){
  return Generate_PostSynthesis_Netlist;
}

void SetPostSynthesisOption(boolean post_synthesis_enabled){
  Generate_PostSynthesis_Netlist = post_synthesis_enabled;
}

boolean IsPostSynthesisEnabled(INP t_options *Options) {
  /* First priority to the '--generate_postsynthesis_netlist' flag */
  if (Options->Count[OT_GENERATE_POST_SYNTHESIS_NETLIST]) {
    return Options->Generate_Post_Synthesis_Netlist;
  }
  return FALSE;
}


void setAllEchoFileEnabled(boolean value) {
	int i;
	for(i = 0; i < (int) E_ECHO_END_TOKEN; i++) {
		echoFileEnabled[i] = value;
	}
}

void setEchoFileEnabled(enum e_echo_files echo_option, boolean value) {
	echoFileEnabled[(int)echo_option] = value;
}

void setEchoFileName(enum e_echo_files echo_option, const char *name) {
	if(echoFileNames[(int)echo_option] != NULL) {
		free(echoFileNames[(int)echo_option]);
	}
	echoFileNames[(int)echo_option] = my_strdup(name);
}

boolean isEchoFileEnabled(enum e_echo_files echo_option) {
	if(echoFileEnabled == NULL) {
		return FALSE;
	} else {
		return echoFileEnabled[(int)echo_option];
	}
}
char *getEchoFileName(enum e_echo_files echo_option) {
	return echoFileNames[(int)echo_option];
}

void alloc_and_load_echo_file_info() {
	echoFileEnabled = (boolean*)my_calloc((int) E_ECHO_END_TOKEN, sizeof(boolean));
	echoFileNames = (char**)my_calloc((int) E_ECHO_END_TOKEN, sizeof(char*));

	setAllEchoFileEnabled(TRUE);

	setEchoFileName(E_ECHO_INITIAL_CLB_PLACEMENT, "initial_clb_placement.echo");
	setEchoFileName(E_ECHO_INITIAL_PLACEMENT_TIMING_GRAPH, "initial_placement_timing_graph.echo");
	setEchoFileName(E_ECHO_INITIAL_PLACEMENT_SLACK, "initial_placement_slack.echo");
	setEchoFileName(E_ECHO_INITIAL_PLACEMENT_CRITICALITY, "initial_placement_criticality.echo");
	setEchoFileName(E_ECHO_END_CLB_PLACEMENT, "end_clb_placement.echo");
	setEchoFileName(E_ECHO_PLACEMENT_SINK_DELAYS, "placement_sink_delays.echo");
	setEchoFileName(E_ECHO_FINAL_PLACEMENT_TIMING_GRAPH, "final_placement_timing_graph.echo");
	setEchoFileName(E_ECHO_FINAL_PLACEMENT_SLACK, "final_placement_slack.echo");
	setEchoFileName(E_ECHO_FINAL_PLACEMENT_CRITICALITY, "final_placement_criticality.echo");
	setEchoFileName(E_ECHO_PLACEMENT_CRIT_PATH, "placement_crit_path.echo");
	setEchoFileName(E_ECHO_PB_GRAPH, "pb_graph.echo");
	setEchoFileName(E_ECHO_ARCH, "arch.echo");
	setEchoFileName(E_ECHO_PLACEMENT_CRITICAL_PATH, "placement_critical_path.echo");
	setEchoFileName(E_ECHO_PLACEMENT_LOWER_BOUND_SINK_DELAYS, "placement_lower_bound_sink_delays.echo");
	setEchoFileName(E_ECHO_PLACEMENT_LOGIC_SINK_DELAYS, "placement_logic_sink_delays.echo");
	setEchoFileName(E_ECHO_ROUTING_SINK_DELAYS, "routing_sink_delays.echo");
	setEchoFileName(E_ECHO_POST_FLOW_TIMING_GRAPH, "post_flow_timing_graph.blif");
	setEchoFileName(E_ECHO_POST_PACK_NETLIST, "post_pack_netlist.blif");
	setEchoFileName(E_ECHO_BLIF_INPUT, "blif_input.echo");
	setEchoFileName(E_ECHO_NET_DELAY, "net_delay.echo");
	setEchoFileName(E_ECHO_TIMING_GRAPH, "timing_graph.echo");
	setEchoFileName(E_ECHO_LUT_REMAPPING, "lut_remapping.echo");
	setEchoFileName(E_ECHO_PRE_PACKING_TIMING_GRAPH, "pre_packing_timing_graph.echo");
	setEchoFileName(E_ECHO_PRE_PACKING_TIMING_GRAPH_AS_BLIF, "pre_packing_timing_graph_as_blif.blif");
	setEchoFileName(E_ECHO_CLUSTERING_TIMING_INFO, "clustering_timing_info.echo");
	setEchoFileName(E_ECHO_PRE_PACKING_SLACK, "pre_packing_slack.echo");
	setEchoFileName(E_ECHO_PRE_PACKING_CRITICALITY, "pre_packing_criticality.echo");
	setEchoFileName(E_ECHO_CLUSTERING_BLOCK_CRITICALITIES, "clustering_block_criticalities.echo");
	setEchoFileName(E_ECHO_PRE_PACKING_MOLECULES_AND_PATTERNS, "pre_packing_molecules_and_patterns.echo");
	setEchoFileName(E_ECHO_MEM, "mem.echo");
	setEchoFileName(E_ECHO_RR_GRAPH, "rr_graph.echo");
	setEchoFileName(E_ECHO_TIMING_CONSTRAINTS, "timing_constraints.echo");	
	setEchoFileName(E_ECHO_CRITICAL_PATH, "critical_path.echo");	
	setEchoFileName(E_ECHO_SLACK, "slack.echo");	
	setEchoFileName(E_ECHO_CRITICALITY, "criticality.echo");
	setEchoFileName(E_ECHO_COMPLETE_NET_TRACE, "complete_net_trace.echo");
	setEchoFileName(E_ECHO_SEG_DETAILS, "seg_details.txt");
}

void free_echo_file_info() {
	int i;
	if(echoFileEnabled != NULL) {
		for(i = 0; i < (int) E_ECHO_END_TOKEN; i++) {
			if(echoFileNames[i] != NULL) {
				free(echoFileNames[i]);
			}
		}
		free(echoFileNames);
		free(echoFileEnabled);
		echoFileNames = NULL;
		echoFileEnabled = NULL;
	}
}

void setOutputFileName(enum e_output_files ename, const char *name, const char *default_name) {
	if(outputFileNames == NULL) {
		alloc_and_load_output_file_names(default_name);
	}
	if(outputFileNames[(int)ename] != NULL) {
		free(outputFileNames[(int)ename]);
	}
	outputFileNames[(int)ename] = my_strdup(name);
}

char *getOutputFileName(enum e_output_files ename) {
	return outputFileNames[(int)ename];
}

void alloc_and_load_output_file_names(const char *default_name) {
	char *name;

	if(outputFileNames == NULL) {

		outputFileNames = (char**)my_calloc((int)E_FILE_END_TOKEN, sizeof(char*));

		name = (char*)my_malloc((strlen(default_name) + 40) * sizeof(char));
		sprintf(name, "%s.critical_path.out", default_name);
		setOutputFileName(E_CRIT_PATH_FILE, name, default_name);
	
		sprintf(name, "%s.slack.out", default_name);
		setOutputFileName(E_SLACK_FILE, name, default_name);

		sprintf(name, "%s.criticality.out", default_name);
		setOutputFileName(E_CRITICALITY_FILE, name, default_name);

		free(name);
	}
}

void free_output_file_names() {
	int i;
	if(outputFileNames != NULL) {
		for(i = 0; i < (int)E_FILE_END_TOKEN; i++) {
			if(outputFileNames[i] != NULL) {
				free(outputFileNames[i]);
				outputFileNames[i] = NULL;
			}
		}
		free(outputFileNames);
		outputFileNames = NULL;
	}
}



/******** Subroutine implementations ********/

void ReadOptions(INP int argc, INP char **argv, OUTP t_options * Options) {
	char **Args, **head;
	int offset;
	
	/* Clear values and pointers to zero */
	memset(Options, 0, sizeof(t_options));

	/* Alloc a new pointer list for args with a NULL at end.
	 * This makes parsing the same as for archfile for consistency.
	 * Skips the first arg as it is the program image path */
	--argc;
	++argv;
	head = Args = (char **) my_malloc(sizeof(char *) * (argc + 1));
	memcpy(Args, argv, (sizeof(char *) * argc));
	Args[argc] = NULL;

	/* Go through the command line args. If they have hyphens they are 
	 * options. Otherwise assume they are part of the four mandatory
	 * arguments */
	while (*Args) {
		if (strncmp("--", *Args, 2) == 0) {
			*Args += 2; /* Skip the prefix */
			Args = ProcessOption(Args, Options);
		} else if (strncmp("-", *Args, 1) == 0) {
			*Args += 1; /* Skip the prefix */
			Args = ProcessOption(Args, Options);
		} else if (NULL == Options->ArchFile) {
			Options->ArchFile = my_strdup(*Args);
			vpr_printf(TIO_MESSAGE_INFO, "Architecture file: %s\n", Options->ArchFile);
			++Args;
		} else if (NULL == Options->CircuitName) {
			Options->CircuitName = my_strdup(*Args);
			/*if the user entered the circuit name with the .blif extension, remove it now*/
			offset = strlen(Options->CircuitName) - 5;
			if (offset > 0 && !strcmp(Options->CircuitName + offset, ".blif")) {
				Options->CircuitName[offset] = '\0';
			}
			vpr_printf(TIO_MESSAGE_INFO, "Circuit name: %s.blif\n", Options->CircuitName);
			vpr_printf(TIO_MESSAGE_INFO, "\n");
			++Args;
		} else {
			/* Not an option and arch and net already specified so fail */
			Error(*Args);
		}

		if (Options->Count[OT_SETTINGS_FILE] != Options->read_settings)
		{
			int tmp_argc = 0;
			char **tmp_argv = NULL;
			t_options SettingsFileOptions;

			tmp_argc = read_settings_file(Options->SettingsFile, &tmp_argv);

			ReadOptions(tmp_argc, tmp_argv, &SettingsFileOptions);

			MergeOptions(Options, &SettingsFileOptions, Options->Count[OT_SETTINGS_FILE]);

			Options->read_settings = Options->Count[OT_SETTINGS_FILE];

			/* clean up local data structures */
			free(tmp_argv);
		}
	}
	free(head);
}

static char **
ProcessOption(INP char **Args, INOUTP t_options * Options) {
	enum e_OptionBaseToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadBaseToken(Args, &Token);
	
	if (Token < OT_BASE_UNKNOWN) {
		/* If this was previously set by a lower priority source
		 * (ie. a settings file), reset the provenance and the
		 * count */
		if (Options->Provenance[Token])
		{
			Options->Provenance[Token] = 0;
			Options->Count[Token] = 1;
		}
		else
			++Options->Count[Token];
	}

	switch (Token) {
		/* File naming options */
	case OT_BLIF_FILE:
		return ReadString(Args, &Options->BlifFile);
	case OT_NET_FILE:
		return ReadString(Args, &Options->NetFile);
	case OT_PLACE_FILE:
		return ReadString(Args, &Options->PlaceFile);
	case OT_ROUTE_FILE:
		return ReadString(Args, &Options->RouteFile);
	case OT_SDC_FILE:
		return ReadString(Args, &Options->SDCFile);
	case OT_SETTINGS_FILE:
		return ReadString(Args, &Options->SettingsFile);
		/* General Options */
	case OT_NODISP:
		return Args;
	case OT_AUTO:
		return ReadInt(Args, &Options->GraphPause);
	case OT_PACK:
	case OT_ROUTE:
	case OT_PLACE:
		return Args;
	case OT_TIMING_ANALYZE_ONLY_WITH_NET_DELAY:
		return ReadFloat(Args, &Options->constant_net_delay);
	case OT_FAST:
	case OT_FULL_STATS:
		return Args;
	case OT_TIMING_ANALYSIS:
		return ReadOnOff(Args, &Options->TimingAnalysis);
	case OT_OUTFILE_PREFIX:
		return ReadString(Args, &Options->out_file_prefix);
	case OT_CREATE_ECHO_FILE:
		return ReadOnOff(Args, &Options->CreateEchoFile);
	case OT_GENERATE_POST_SYNTHESIS_NETLIST:
          
	  return ReadOnOff(Args, &Options->Generate_Post_Synthesis_Netlist);
		/* Clustering Options */
	case OT_GLOBAL_CLOCKS:
		return ReadOnOff(Args, &Options->global_clocks);
	case OT_HILL_CLIMBING_FLAG:
		return ReadOnOff(Args, &Options->hill_climbing_flag);
	case OT_SWEEP_HANGING_NETS_AND_INPUTS:
		return ReadOnOff(Args, &Options->sweep_hanging_nets_and_inputs);
	case OT_TIMING_DRIVEN_CLUSTERING:
		return ReadOnOff(Args, &Options->timing_driven);
	case OT_CLUSTER_SEED:
		return ReadClusterSeed(Args, &Options->cluster_seed_type);
	case OT_ALPHA_CLUSTERING:
		return ReadFloat(Args, &Options->alpha);
	case OT_BETA_CLUSTERING:
		return ReadFloat(Args, &Options->beta);
	case OT_RECOMPUTE_TIMING_AFTER:
		return ReadInt(Args, &Options->recompute_timing_after);
	case OT_CLUSTER_BLOCK_DELAY:
		return ReadFloat(Args, &Options->block_delay);
	case OT_ALLOW_UNRELATED_CLUSTERING:
		return ReadOnOff(Args, &Options->allow_unrelated_clustering);
	case OT_ALLOW_EARLY_EXIT:
		return ReadOnOff(Args, &Options->allow_early_exit);
	case OT_INTRA_CLUSTER_NET_DELAY:
		return ReadFloat(Args, &Options->intra_cluster_net_delay);
	case OT_INTER_CLUSTER_NET_DELAY:
		return ReadFloat(Args, &Options->inter_cluster_net_delay);
	case OT_CONNECTION_DRIVEN_CLUSTERING:
		return ReadOnOff(Args, &Options->connection_driven);
	case OT_SKIP_CLUSTERING:
		return Args;
	case OT_PACKER_ALGORITHM:
		return ReadPackerAlgorithm(Args, &Options->packer_algorithm);

		/* Placer Options */
	case OT_PLACE_ALGORITHM:
		return ReadPlaceAlgorithm(Args, &Options->PlaceAlgorithm);
	case OT_INIT_T:
		return ReadFloat(Args, &Options->PlaceInitT);
	case OT_EXIT_T:
		return ReadFloat(Args, &Options->PlaceExitT);
	case OT_ALPHA_T:
		return ReadFloat(Args, &Options->PlaceAlphaT);
	case OT_INNER_NUM:
		return ReadFloat(Args, &Options->PlaceInnerNum);
	case OT_SEED:
		return ReadInt(Args, &Options->Seed);
	case OT_PLACE_COST_EXP:
		return ReadFloat(Args, &Options->place_cost_exp);
	case OT_PLACE_CHAN_WIDTH:
		return ReadInt(Args, &Options->PlaceChanWidth);
	case OT_FIX_PINS:
		return ReadFixPins(Args, &Options->PinFile);
	case OT_ENABLE_TIMING_COMPUTATIONS:
		return ReadOnOff(Args, &Options->ShowPlaceTiming);
	case OT_BLOCK_DIST:
		return ReadInt(Args, &Options->block_dist);

		/* Placement Options Valid Only for Timing-Driven Placement */
	case OT_TIMING_TRADEOFF:
		return ReadFloat(Args, &Options->PlaceTimingTradeoff);
	case OT_RECOMPUTE_CRIT_ITER:
		return ReadInt(Args, &Options->RecomputeCritIter);
	case OT_INNER_LOOP_RECOMPUTE_DIVIDER:
		return ReadInt(Args, &Options->inner_loop_recompute_divider);
	case OT_TD_PLACE_EXP_FIRST:
		return ReadFloat(Args, &Options->place_exp_first);
	case OT_TD_PLACE_EXP_LAST:
		return ReadFloat(Args, &Options->place_exp_last);

		/* Router Options */
	case OT_MAX_ROUTER_ITERATIONS:
		return ReadInt(Args, &Options->max_router_iterations);
	case OT_BB_FACTOR:
		return ReadInt(Args, &Options->bb_factor);
	case OT_INITIAL_PRES_FAC:
		return ReadFloat(Args, &Options->initial_pres_fac);
	case OT_PRES_FAC_MULT:
		return ReadFloat(Args, &Options->pres_fac_mult);
	case OT_ACC_FAC:
		return ReadFloat(Args, &Options->acc_fac);
	case OT_FIRST_ITER_PRES_FAC:
		return ReadFloat(Args, &Options->first_iter_pres_fac);
	case OT_BEND_COST:
		return ReadFloat(Args, &Options->bend_cost);
	case OT_ROUTE_TYPE:
		return ReadRouteType(Args, &Options->RouteType);
	case OT_VERIFY_BINARY_SEARCH:
		return Args;
	case OT_ROUTE_CHAN_WIDTH:
		return ReadInt(Args, &Options->RouteChanWidth);
	case OT_ROUTER_ALGORITHM:
		return ReadRouterAlgorithm(Args, &Options->RouterAlgorithm);
	case OT_BASE_COST_TYPE:
		return ReadBaseCostType(Args, &Options->base_cost_type);

		/* Routing options valid only for timing-driven routing */
	case OT_ASTAR_FAC:
		return ReadFloat(Args, &Options->astar_fac);
	case OT_MAX_CRITICALITY:
		return ReadFloat(Args, &Options->max_criticality);
	case OT_CRITICALITY_EXP:
		return ReadFloat(Args, &Options->criticality_exp);

		/* Power options */
	case OT_POWER:
		return Args;
	case OT_ACTIVITY_FILE:
		return ReadString(Args, &Options->ActFile);
	case OT_POWER_OUT_FILE:
		return ReadString(Args, &Options->PowerFile);
	case OT_CMOS_TECH_BEHAVIOR_FILE:
		return ReadString(Args, &Options->CmosTechFile);

    /* Xifan Tang: Tileable routing support !!! */
	case OT_USE_TILEABLE_ROUTE_CHAN_WIDTH:
		return Args;

    /* Xifan Tang: FPGA X2P Options*/
    case OT_FPGA_X2P_RENAME_ILLEGAL_PORT:
      return Args;
    case OT_FPGA_X2P_SIGNAL_DENSITY_WEIGHT:
	  return ReadFloat(Args, &Options->fpga_spice_signal_density_weight);
    case OT_FPGA_X2P_SIM_WINDOW_SIZE:
	  return ReadFloat(Args, &Options->fpga_spice_sim_window_size);
    case OT_FPGA_X2P_COMPACT_ROUTING_HIERARCHY: 
      /* use a compact routing hierarchy in SPICE/Verilog generation */
      return Args;
    case OT_FPGA_X2P_OUTPUT_SB_XML: 
      /* Read the file prefix to output SB XML files */
      return ReadString(Args, &Options->sb_xml_dir);
    /* Xifan TANG: FPGA SPICE Model Options*/
    case OT_FPGA_SPICE:
      return Args;
    case OT_FPGA_SPICE_DIR:
      return ReadString(Args, &Options->spice_dir);
    case OT_FPGA_SPICE_PRINT_TOP_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_PB_MUX_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_CB_MUX_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_SB_MUX_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_CB_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_SB_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_GRID_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_LUT_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_HARDLOGIC_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_PRINT_IO_TESTBENCH:
      return Args;
    case OT_FPGA_SPICE_LEAKAGE_ONLY:
      return Args;
    case OT_FPGA_SPICE_PARASITIC_NET_ESTIMATION:
      return ReadOnOff(Args, &Options->fpga_spice_parasitic_net_estimation);
    case OT_FPGA_SPICE_TESTBENCH_LOAD_EXTRACTION:
      return ReadOnOff(Args, &Options->fpga_spice_testbench_load_extraction);
    case OT_FPGA_SPICE_SIM_MT_NUM:
	  return ReadInt(Args, &Options->fpga_spice_sim_mt_num);
    case OT_FPGA_SPICE_SIMULATOR_PATH:
      return ReadString(Args, &Options->fpga_spice_simulator_path);
    /* Xifan TANG: Synthesizable Verilog */
    case OT_FPGA_VERILOG_SYN:
      return Args;
    case OT_FPGA_VERILOG_SYN_DIR:
      return ReadString(Args, &Options->fpga_syn_verilog_dir);
    case OT_FPGA_VERILOG_SYN_EXPLICIT_MAPPING:
      return Args;
    case OT_FPGA_VERILOG_SYN_PRINT_TOP_TESTBENCH:
      return Args;
    case OT_FPGA_VERILOG_SYN_PRINT_AUTOCHECK_TOP_TESTBENCH:
      return ReadString(Args, &Options->fpga_verilog_reference_benchmark_file);
    case OT_FPGA_VERILOG_SYN_PRINT_INPUT_BLIF_TESTBENCH:
      return Args;
    case OT_FPGA_VERILOG_SYN_PRINT_FORMAL_VERIFICATION_TOP_NETLIST:
      return Args;
    case OT_FPGA_VERILOG_SYN_INCLUDE_TIMING:
      return Args;
    case OT_FPGA_VERILOG_SYN_INCLUDE_SIGNAL_INIT:
      return Args;
    case OT_FPGA_VERILOG_SYN_INCLUDE_ICARUS_SIMULATOR:
      return Args;
    case OT_FPGA_VERILOG_SYN_PRINT_MODELSIM_AUTODECK:
      return ReadString(Args, &Options->fpga_verilog_modelsim_ini_path);
    case OT_FPGA_VERILOG_SYN_PRINT_USER_DEFINED_TEMPLATE:
      return Args;
    case OT_FPGA_VERILOG_SYN_PRINT_REPORT_TIMING_TCL:
      return Args;
    case OT_FPGA_VERILOG_SYN_REPORT_TIMING_RPT_PATH:
      return ReadString(Args, &Options->fpga_verilog_report_timing_path);
    case OT_FPGA_VERILOG_SYN_PRINT_SDC_PNR:
      return Args;
    case OT_FPGA_VERILOG_SYN_PRINT_SDC_ANALYSIS:
      return Args;
    /* Xifan TANG: Bitstream generator */
    case OT_FPGA_BITSTREAM_GENERATOR:
      return Args;
//    case OT_FPGA_BITSTREAM_OUTPUT_FILE:	// AA: temporarily deprecated
//      return ReadString(Args, &Options->fpga_bitstream_file);
    /* mrFPGA: Xifan TANG */
    case OT_SHOW_SRAM:
    case OT_SHOW_PASS_TRANS:
       return Args;
    /* Xifan TANG: CLB_PIN_REMAP */
    case OT_PACK_CLB_PIN_REMAP:
    case OT_PLACE_CLB_PIN_REMAP:
       return Args;
 
	default:
		vpr_printf(TIO_MESSAGE_ERROR, "Unexpected option '%s' on command line.\n", *PrevArgs);
		exit(1);
	}
}

/*
 * Map options set in the source t_options to a target t_options
 * structure.  Existing values in the destination have priority
 * and will not be overwritten
 */
static void MergeOptions(INOUTP t_options * dest, INP t_options * src, int id)
{
	int Token;

	for (Token = 0; Token < OT_BASE_UNKNOWN; Token++)
	{
		/* Don't override values already set in the
		 * target destination.  Also do not process
		 * Tokens that are not present in the source.
		 */
		if ((dest->Count[Token] || (!src->Count[Token])))
			continue;

		dest->Count[Token] = src->Count[Token];
		dest->Provenance[Token] = id;

		switch (Token) {
			/* File naming options */
		case OT_BLIF_FILE:
			dest->BlifFile = src->BlifFile;
			break;
		case OT_NET_FILE:
			dest->NetFile = src->NetFile;
			break;
		case OT_PLACE_FILE:
			dest->PlaceFile = src->PlaceFile;
			break;
		case OT_ROUTE_FILE:
			dest->RouteFile = src->RouteFile;
			break;
		case OT_SETTINGS_FILE:
			dest->SettingsFile = src->SettingsFile;
			break;
		case OT_SDC_FILE:
			dest->SDCFile = src->SDCFile;
			break;
			/* General Options */
		case OT_NODISP:
			break;
		case OT_AUTO:
			dest->GraphPause = src->GraphPause;
			break;
		case OT_PACK:
		case OT_ROUTE:
		case OT_PLACE:
			break;
		case OT_TIMING_ANALYZE_ONLY_WITH_NET_DELAY:
			dest->constant_net_delay = src->constant_net_delay;
			break;
		case OT_FAST:
		case OT_FULL_STATS:
			break;
		case OT_TIMING_ANALYSIS:
			dest->TimingAnalysis = src->TimingAnalysis;
			break;
		case OT_OUTFILE_PREFIX:
			dest->out_file_prefix = src->out_file_prefix;
			break;
		case OT_CREATE_ECHO_FILE:
			dest->CreateEchoFile = src->CreateEchoFile;
			break;

			/* Clustering Options */
		case OT_GLOBAL_CLOCKS:
			dest->global_clocks = src->global_clocks;
			break;
		case OT_HILL_CLIMBING_FLAG:
			dest->hill_climbing_flag = src->hill_climbing_flag;
			break;
		case OT_SWEEP_HANGING_NETS_AND_INPUTS:
			dest->sweep_hanging_nets_and_inputs = src->sweep_hanging_nets_and_inputs;
			break;
		case OT_TIMING_DRIVEN_CLUSTERING:
			dest->timing_driven = src->timing_driven;
			break;
		case OT_CLUSTER_SEED:
			dest->cluster_seed_type = src->cluster_seed_type;
			break;
		case OT_ALPHA_CLUSTERING:
			dest->alpha = src->alpha;
			break;
		case OT_BETA_CLUSTERING:
			dest->beta = src->beta;
			break;
		case OT_RECOMPUTE_TIMING_AFTER:
			dest->recompute_timing_after = src->recompute_timing_after;
			break;
		case OT_CLUSTER_BLOCK_DELAY:
			dest->block_delay = src->block_delay;
			break;
		case OT_ALLOW_UNRELATED_CLUSTERING:
			dest->allow_unrelated_clustering = src->allow_unrelated_clustering;
			break;
		case OT_ALLOW_EARLY_EXIT:
			dest->allow_early_exit = src->allow_early_exit;
			break;
		case OT_INTRA_CLUSTER_NET_DELAY:
			dest->intra_cluster_net_delay = src->intra_cluster_net_delay;
			break;
		case OT_INTER_CLUSTER_NET_DELAY:
			dest->inter_cluster_net_delay = src->inter_cluster_net_delay;
			break;
		case OT_CONNECTION_DRIVEN_CLUSTERING:
			dest->connection_driven = src->connection_driven;
			break;
		case OT_SKIP_CLUSTERING:
			break;
		case OT_PACKER_ALGORITHM:
			dest->packer_algorithm = src->packer_algorithm;
			break;

			/* Placer Options */
		case OT_PLACE_ALGORITHM:
			dest->PlaceAlgorithm = src->PlaceAlgorithm;
			break;
		case OT_INIT_T:
			dest->PlaceInitT = src->PlaceInitT;
			break;
		case OT_EXIT_T:
			dest->PlaceExitT = src->PlaceExitT;
			break;
		case OT_ALPHA_T:
			dest->PlaceAlphaT = src->PlaceAlphaT;
			break;
		case OT_INNER_NUM:
			dest->PlaceInnerNum = src->PlaceInnerNum;
			break;
		case OT_SEED:
			dest->Seed = src->Seed;
			break;
		case OT_PLACE_COST_EXP:
			dest->place_cost_exp = src->place_cost_exp;
			break;
		case OT_PLACE_CHAN_WIDTH:
			dest->PlaceChanWidth = src->PlaceChanWidth;
			break;
		case OT_FIX_PINS:
			dest->PinFile = src->PinFile;
			break;
		case OT_ENABLE_TIMING_COMPUTATIONS:
			dest->ShowPlaceTiming = src->ShowPlaceTiming;
			break;
		case OT_BLOCK_DIST:
			dest->block_dist = src->block_dist;
			break;

			/* Placement Options Valid Only for Timing-Driven Placement */
		case OT_TIMING_TRADEOFF:
			dest->PlaceTimingTradeoff = src->PlaceTimingTradeoff;
			break;
		case OT_RECOMPUTE_CRIT_ITER:
			dest->RecomputeCritIter = src->RecomputeCritIter;
			break;
		case OT_INNER_LOOP_RECOMPUTE_DIVIDER:
			dest->inner_loop_recompute_divider = src->inner_loop_recompute_divider;
			break;
		case OT_TD_PLACE_EXP_FIRST:
			dest->place_exp_first = src->place_exp_first;
			break;
		case OT_TD_PLACE_EXP_LAST:
			dest->place_exp_last = src->place_exp_last;
			break;

			/* Router Options */
		case OT_MAX_ROUTER_ITERATIONS:
			dest->max_router_iterations = src->max_router_iterations;
			break;
		case OT_BB_FACTOR:
			dest->bb_factor = src->bb_factor;
			break;
		case OT_INITIAL_PRES_FAC:
			dest->initial_pres_fac = src->initial_pres_fac;
			break;
		case OT_PRES_FAC_MULT:
			dest->pres_fac_mult = src->pres_fac_mult;
			break;
		case OT_ACC_FAC:
			dest->acc_fac = src->acc_fac;
			break;
		case OT_FIRST_ITER_PRES_FAC:
			dest->first_iter_pres_fac = src->first_iter_pres_fac;
			break;
		case OT_BEND_COST:
			dest->bend_cost = src->bend_cost;
			break;
		case OT_ROUTE_TYPE:
			dest->RouteType = src->RouteType;
			break;
		case OT_VERIFY_BINARY_SEARCH:
			break;
		case OT_ROUTE_CHAN_WIDTH:
			dest->RouteChanWidth = src->RouteChanWidth;
			break;
		case OT_ROUTER_ALGORITHM:
			dest->RouterAlgorithm = src->RouterAlgorithm;
			break;
		case OT_BASE_COST_TYPE:
			dest->base_cost_type = src->base_cost_type;
			break;

			/* Routing options valid only for timing-driven routing */
		case OT_ASTAR_FAC:
			dest->astar_fac = src->astar_fac;
			break;
		case OT_MAX_CRITICALITY:
			dest->max_criticality = src->max_criticality;
			break;
		case OT_CRITICALITY_EXP:
			dest->criticality_exp = src->criticality_exp;
			break;
		default:
			break;
		}
	}
}

static char **
ReadBaseToken(INP char **Args, OUTP enum e_OptionBaseToken *Token) {
	struct s_TokenPair *Cur;

	/* Empty string is end of tokens marker */
	if (NULL == *Args)
		Error(*Args);

	/* Linear search for the pair */
	Cur = OptionBaseTokenList;
	while (Cur->Str) {
		if (strcmp(*Args, Cur->Str) == 0) {
			*Token = (enum e_OptionBaseToken) Cur->Enum;
			return ++Args;
		}
		++Cur;
	}

	*Token = OT_BASE_UNKNOWN;
	return ++Args;
}

static char **
ReadToken(INP char **Args, OUTP enum e_OptionArgToken *Token) {
	struct s_TokenPair *Cur;

	/* Empty string is end of tokens marker */
	if (NULL == *Args)
		Error(*Args);

	/* Linear search for the pair */
	Cur = OptionArgTokenList;
	while (Cur->Str) {
		if (strcmp(*Args, Cur->Str) == 0) {
			*Token = (enum e_OptionArgToken)Cur->Enum;
			return ++Args;
		}
		++Cur;
	}

	*Token = OT_ARG_UNKNOWN;
	return ++Args;
}

/* Called for parse errors. Spits out a message and then exits program. */
static void Error(INP const char *Token) {
	if (Token) {
		vpr_printf(TIO_MESSAGE_ERROR, "Unexpected token '%s' on command line.\n", Token);
	} else {
		vpr_printf(TIO_MESSAGE_ERROR, "Missing token at end of command line.\n");
	}
	exit(1);
}

static char **
ReadClusterSeed(INP char **Args, OUTP enum e_cluster_seed *Type) {
	enum e_OptionArgToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadToken(Args, &Token);
	switch (Token) {
	case OT_TIMING:
		*Type = VPACK_TIMING;
		break;
	case OT_MAX_INPUTS:
		*Type = VPACK_MAX_INPUTS;
		break;
	default:
		Error(*PrevArgs);
	}

	return Args;
}

static char **
ReadPackerAlgorithm(INP char **Args, OUTP enum e_packer_algorithm *Algo) {
	enum e_OptionArgToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadToken(Args, &Token);
	switch (Token) {
	case OT_GREEDY:
		*Algo = PACK_GREEDY;
		break;
	case OT_BRUTE_FORCE:
		*Algo = PACK_BRUTE_FORCE;
		break;
	default:
		Error(*PrevArgs);
	}

	return Args;
}

static char **
ReadRouterAlgorithm(INP char **Args, OUTP enum e_router_algorithm *Algo) {
	enum e_OptionArgToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadToken(Args, &Token);
	switch (Token) {
	case OT_BREADTH_FIRST:
		*Algo = BREADTH_FIRST;
		break;
	case OT_NO_TIMING:
		*Algo = NO_TIMING;
		break;
	case OT_TIMING_DRIVEN:
		*Algo = TIMING_DRIVEN;
		break;
	default:
		Error(*PrevArgs);
	}

	return Args;
}

static char **
ReadBaseCostType(INP char **Args, OUTP enum e_base_cost_type *BaseCostType) {
	enum e_OptionArgToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadToken(Args, &Token);
	switch (Token) {
	case OT_INTRINSIC_DELAY:
		*BaseCostType = INTRINSIC_DELAY;
		break;
	case OT_DELAY_NORMALIZED:
		*BaseCostType = DELAY_NORMALIZED;
		break;
	case OT_DEMAND_ONLY:
		*BaseCostType = DEMAND_ONLY;
		break;
	default:
		Error(*PrevArgs);
	}

	return Args;
}

static char **
ReadRouteType(INP char **Args, OUTP enum e_route_type *Type) {
	enum e_OptionArgToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadToken(Args, &Token);
	switch (Token) {
	case OT_GLOBAL:
		*Type = GLOBAL;
		break;
	case OT_DETAILED:
		*Type = DETAILED;
		break;
	default:
		Error(*PrevArgs);
	}

	return Args;
}

static char **
ReadPlaceAlgorithm(INP char **Args, OUTP enum e_place_algorithm *Algo) {
	enum e_OptionArgToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadToken(Args, &Token);
	switch (Token) {
	case OT_BOUNDING_BOX:
		*Algo = BOUNDING_BOX_PLACE;
		break;
	case OT_NET_TIMING_DRIVEN:
		*Algo = NET_TIMING_DRIVEN_PLACE;
		break;
	case OT_PATH_TIMING_DRIVEN:
		*Algo = PATH_TIMING_DRIVEN_PLACE;
		break;
	default:
		Error(*PrevArgs);
	}

	return Args;
}

static char **
ReadFixPins(INP char **Args, OUTP char **PinFile) {
	enum e_OptionArgToken Token;
	int Len;
	char **PrevArgs = Args;

	Args = ReadToken(Args, &Token);
	if (OT_RANDOM != Token) {
		Len = 1 + strlen(*PrevArgs);
		*PinFile = (char *) my_malloc(Len * sizeof(char));
		memcpy(*PinFile, *PrevArgs, Len);
	}
	return Args;
}

static char **
ReadOnOff(INP char **Args, OUTP boolean * Val) {
	enum e_OptionArgToken Token;
	char **PrevArgs;

	PrevArgs = Args;
	Args = ReadToken(Args, &Token);
	switch (Token) {
	case OT_ON:
		*Val = TRUE;
		break;
	case OT_OFF:
		*Val = FALSE;
		break;
	default:
		Error(*PrevArgs);
	}
	return Args;
}

static char **
ReadInt(INP char **Args, OUTP int *Val) {
	if (NULL == *Args)
		Error(*Args);
	if ((**Args > '9') || (**Args < '0'))
		Error(*Args);

	*Val = atoi(*Args);

	return ++Args;
}

static char **
ReadFloat(INP char ** Args, OUTP float *Val) {
	if (NULL == *Args) {
		Error(*Args);
	}

	if ((**Args != '-') && (**Args != '.')
			&& ((**Args > '9') || (**Args < '0'))) {
		Error(*Args);
	}

	*Val = atof(*Args);

	return ++Args;
}

static char **
ReadString(INP char **Args, OUTP char **Val) {
	if (NULL == *Args) {
		Error(*Args);
	}

	*Val = my_strdup(*Args);

	return ++Args;
}