#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "util.h"
#include "vpr_types.h"
#include "globals.h"
#include "hash.h"
#include "read_place.h"
#include "read_xml_arch_file.h"
#include "ReadLine.h"

/* extern, should be a header */
char **ReadLineTokens(INOUTP FILE * InFile, INOUTP int *LineNum);

void read_place(INP const char *place_file, INP const char *arch_file,
		INP const char *net_file, INP int L_nx, INP int L_ny,
		INP int L_num_blocks, INOUTP struct s_block block_list[]) {

	FILE *infile;
	char **tokens;
	int line;
	int i;
	int error;
	struct s_block *cur_blk;

	infile = fopen(place_file, "r");

	/* Check filenames in first line match */
	tokens = ReadLineTokens(infile, &line);
	error = 0;
	if (NULL == tokens) {
		error = 1;
	}
	for (i = 0; i < 6; ++i) {
		if (!error) {
			if (NULL == tokens[i]) {
				error = 1;
			}
		}
	}
	if (!error) {
		if ((0 != strcmp(tokens[0], "Netlist"))
				|| (0 != strcmp(tokens[1], "file:"))
				|| (0 != strcmp(tokens[3], "Architecture"))
				|| (0 != strcmp(tokens[4], "file:"))) {
			error = 1;
		};
	}
	if (error) {
		vpr_printf(TIO_MESSAGE_ERROR, "'%s' - Bad filename specification line in placement file.\n", place_file);
		exit(1);
	}
	if (0 != strcmp(tokens[2], arch_file)) {
		vpr_printf(TIO_MESSAGE_ERROR, "'%s' - Architecture file that generated placement (%s) does not match current architecture file (%s).\n", 
				place_file, tokens[2], arch_file);
		exit(1);
	}
	if (0 != strcmp(tokens[5], net_file)) {
		vpr_printf(TIO_MESSAGE_ERROR, "'%s' - Netlist file that generated placement (%s) does not match current netlist file (%s).\n", 
				place_file, tokens[5], net_file);
		exit(1);
	}
	free(*tokens);
	free(tokens);

	/* Check array size in second line matches */
	tokens = ReadLineTokens(infile, &line);
	error = 0;
	if (NULL == tokens) {
		error = 1;
	}
	for (i = 0; i < 7; ++i) {
		if (!error) {
			if (NULL == tokens[i]) {
				error = 1;
			}
		}
	}
	if (!error) {
		if ((0 != strcmp(tokens[0], "Array"))
				|| (0 != strcmp(tokens[1], "size:"))
				|| (0 != strcmp(tokens[3], "x"))
				|| (0 != strcmp(tokens[5], "logic"))
				|| (0 != strcmp(tokens[6], "blocks"))) {
			error = 1;
		};
	}
	if (error) {
		vpr_printf(TIO_MESSAGE_ERROR, "'%s' - Bad FPGA size specification line in placement file.\n",
				place_file);
		exit(1);
	}
	if ((my_atoi(tokens[2]) != L_nx) || (my_atoi(tokens[4]) != L_ny)) {
		vpr_printf(TIO_MESSAGE_ERROR, "'%s' - Current FPGA size (%d x %d) is different from size when placement generated (%d x %d).\n", 
				place_file, L_nx, L_ny, my_atoi(tokens[2]), my_atoi(tokens[4]));
		exit(1);
	}
	free(*tokens);
	free(tokens);

	tokens = ReadLineTokens(infile, &line);
	while (tokens) {
		/* Linear search to match pad to netlist */
		cur_blk = NULL;
		for (i = 0; i < L_num_blocks; ++i) {
			if (0 == strcmp(block_list[i].name, tokens[0])) {
				cur_blk = (block_list + i);
				break;
			}
		}

		/* Error if invalid block */
		if (NULL == cur_blk) {
			vpr_printf(TIO_MESSAGE_ERROR, "'%s':%d - Block in placement file does not exist in netlist.\n", 
					place_file, line);
			exit(1);
		}

		/* Set pad coords */
		cur_blk->x = my_atoi(tokens[1]);
		cur_blk->y = my_atoi(tokens[2]);
		cur_blk->z = my_atoi(tokens[3]);

		/* Get next line */
		assert(*tokens);
		free(*tokens);
		free(tokens);
		tokens = ReadLineTokens(infile, &line);
	}

	fclose(infile);
}

void read_user_pad_loc(char *pad_loc_file) {

	/* Reads in the locations of the IO pads from a file. */

	struct s_hash **hash_table, *h_ptr;
	int iblk, i, j, xtmp, ytmp, bnum, k;
	FILE *fp;
	char buf[BUFSIZE], bname[BUFSIZE], *ptr;

	vpr_printf(TIO_MESSAGE_INFO, "\n");
	vpr_printf(TIO_MESSAGE_INFO, "Reading locations of IO pads from '%s'.\n", pad_loc_file);
	file_line_number = 0;
	fp = fopen(pad_loc_file, "r");

	hash_table = alloc_hash_table();
	for (iblk = 0; iblk < num_blocks; iblk++) {
		if (block[iblk].type == IO_TYPE) {
			h_ptr = insert_in_hash_table(hash_table, block[iblk].name, iblk);
			block[iblk].x = OPEN; /* Mark as not seen yet. */
		}
	}

	for (i = 0; i <= nx + 1; i++) {
		for (j = 0; j <= ny + 1; j++) {
			if (grid[i][j].type == IO_TYPE) {
				for (k = 0; k < IO_TYPE->capacity; k++)
					grid[i][j].blocks[k] = OPEN; /* Flag for err. check */
			}
		}
	}

	ptr = my_fgets(buf, BUFSIZE, fp);

	while (ptr != NULL) {
		ptr = my_strtok(buf, TOKENS, fp, buf);
		if (ptr == NULL) {
			ptr = my_fgets(buf, BUFSIZE, fp);
			continue; /* Skip blank or comment lines. */
		}

		strcpy(bname, ptr);

		ptr = my_strtok(NULL, TOKENS, fp, buf);
		if (ptr == NULL) {
			vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Incomplete.\n", file_line_number);
			exit(1);
		}
		sscanf(ptr, "%d", &xtmp);

		ptr = my_strtok(NULL, TOKENS, fp, buf);
		if (ptr == NULL) {
			vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Incomplete.\n", file_line_number);
			exit(1);
		}
		sscanf(ptr, "%d", &ytmp);

		ptr = my_strtok(NULL, TOKENS, fp, buf);
		if (ptr == NULL) {
			vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Incomplete.\n", file_line_number);
			exit(1);
		}
		sscanf(ptr, "%d", &k);

		ptr = my_strtok(NULL, TOKENS, fp, buf);
		if (ptr != NULL) {
			vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Extra characters at end of line.\n",
					file_line_number);
			exit(1);
		}

		h_ptr = get_hash_entry(hash_table, bname);
		if (h_ptr == NULL) {
			vpr_printf(TIO_MESSAGE_WARNING, "[Line %d] Block %s invalid, no such IO pad.\n", 
					file_line_number, bname);
			ptr = my_fgets(buf, BUFSIZE, fp);
			continue;
		}
		bnum = h_ptr->index;
		i = xtmp;
		j = ytmp;

		if (block[bnum].x != OPEN) {
			vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Block %s is listed twice in pad file.\n",
					file_line_number, bname);
			exit(1);
		}

		if (i < 0 || i > nx + 1 || j < 0 || j > ny + 1) {
			vpr_printf(TIO_MESSAGE_ERROR, "Block #%d (%s) location, (%d,%d) is out of range.\n", 
					bnum, bname, i, j);
			exit(1);
		}

		block[bnum].x = i; /* Will be reloaded by initial_placement anyway. */
		block[bnum].y = j; /* I need to set .x only as a done flag.         */
		block[bnum].isFixed = TRUE;

		if (grid[i][j].type != IO_TYPE) {
			vpr_printf(TIO_MESSAGE_ERROR, "Attempt to place IO block %s at illegal location (%d, %d).\n", 
					bname, i, j);
			exit(1);
		}

		if (k >= IO_TYPE->capacity || k < 0) {
			vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Block %s subblock number (%d) is out of range.\n", 
					file_line_number, bname, k);
			exit(1);
		}
		grid[i][j].blocks[k] = bnum;
		grid[i][j].usage++;

		ptr = my_fgets(buf, BUFSIZE, fp);
	}

	for (iblk = 0; iblk < num_blocks; iblk++) {
		if (block[iblk].type == IO_TYPE && block[iblk].x == OPEN) {
			vpr_printf(TIO_MESSAGE_ERROR, "IO block %s location was not specified in the pad file.\n", 
					block[iblk].name);
			exit(1);
		}
	}

	fclose(fp);
	free_hash_table(hash_table);
	vpr_printf(TIO_MESSAGE_INFO, "Successfully read %s.\n", pad_loc_file);
	vpr_printf(TIO_MESSAGE_INFO, "\n");
}

void print_place(char *place_file, char *net_file, char *arch_file) {

	/* Prints out the placement of the circuit.  The architecture and    *
	 * netlist files used to generate this placement are recorded in the *
	 * file to avoid loading a placement with the wrong support files    *
	 * later.                                                            */

	FILE *fp;
	int i;

	fp = fopen(place_file, "w");

	fprintf(fp, "Netlist file: %s   Architecture file: %s\n", net_file,
			arch_file);
	fprintf(fp, "Array size: %d x %d logic blocks\n\n", nx, ny);
	fprintf(fp, "#block name\tx\ty\tsubblk\tblock number\n");
	fprintf(fp, "#----------\t--\t--\t------\t------------\n");

	for (i = 0; i < num_blocks; i++) {
		fprintf(fp, "%s\t", block[i].name);
		if (strlen(block[i].name) < 8)
			fprintf(fp, "\t");

		fprintf(fp, "%d\t%d\t%d", block[i].x, block[i].y, block[i].z);
		fprintf(fp, "\t#%d\n", i);
	}
	fclose(fp);
}