/* Name: applyLRU
 * Expected arguments: BM_BufferPool
 * Behavior: Would apply LRU as page replacement strategy on the buffer pool and return us the appropriate
 * 			 page to be replaced.
 * Returns: Success/failure flag.
 * Version: 1.0.0 */
int applyLRU(BM_BufferPool * const bm, BM_PageHandle * const page,
		const PageNumber pageNum) {
	BufferPool_Node *bf_node;
	bf_node = search_data(BP_StNode_ptr, bm);
	Buffer_page_Dtl *lowestPossiblePage;
	lowestPossiblePage = sortWeights(bm, bf_node);
	char *replacementAddress;
	replacementAddress = lowestPossiblePage->pageframes;

	RC writeVal = RC_OK;
	RC readVal = RC_OK;
	if (lowestPossiblePage->isdirty == TRUE) {
		writeVal = writeBlock(page->pageNum, bm->mgmtData, replacementAddress);
		lowestPossiblePage->isdirty=FALSE;
		bf_node->numwriteIO++;
	}
	readVal = readBlock(pageNum, bm->mgmtData, replacementAddress);
	if(readVal==RC_READ_NON_EXISTING_PAGE)
    {
        readVal =appendEmptyBlock(bm->mgmtData);
        readVal = readBlock(pageNum, bm->mgmtData, replacementAddress);
    }
	page->pageNum  = pageNum;
	page->data = lowestPossiblePage->pageframes;
    lowestPossiblePage->pagenums = pageNum;
    lowestPossiblePage->fixcounts+=1;
	lowestPossiblePage->replacementWeight =
    lowestPossiblePage->replacementWeight + 1;
	lowestPossiblePage->timeStamp =(long double)univtime++;
    bf_node->numreadIO++;
	if (readVal == RC_OK && writeVal == RC_OK)
		return 1; //success flag
	else
		return 0;
}
action decideAction(Game g) {
	vertex vertices[NUM_INT_VERTICES];
	arc arcs[NUM_INT_ARCS];

	int currentPlayer = getWhoseTurn(g);

	int myCampus = 0;
	int myGO8 = 0;
	int myARC = 0;

	if (currentPlayer == UNI_A) {
		myCampus = CAMPUS_A;
		myGO8 = GO8_A;
		myARC = ARC_A;
	} else if (currentPlayer == UNI_B) {
		myCampus = CAMPUS_B;
		myGO8 = GO8_B;
		myARC = ARC_B;
	} else if (currentPlayer == UNI_C) {
		myCampus = CAMPUS_C;
		myGO8 = GO8_C;
		myARC = ARC_C;
	}

	char allPaths[NUM_INT_VERTICES][PATH_LIMIT] = ALL_PATHS;
	char arcPaths[NUM_INT_ARCS - NUM_INT_VERTICES][PATH_LIMIT] = ARC_PATHS;

	// Populate database

	int i = 0;

	while (i < NUM_INT_VERTICES) {
		vertex newVertex;
		arc newArc;

		strcpy(newVertex.path, allPaths[i]);
		strcpy(newArc.path, allPaths[i]);

		newVertex.object = getCampus(g, allPaths[i]);
		newArc.object = getARC(g, allPaths[i]);

		vertices[i] = newVertex;
		arcs[i] = newArc;

		i++;
	}

	while (i < NUM_INT_ARCS) {
		arc newArc;

		strcpy(newArc.path, arcPaths[i - NUM_INT_VERTICES]);
		newArc.object = getARC(g, newArc.path);

		arcs[i] = newArc;

		i++;
	}

	// printf("Populated database\n");

	// If we have more than 5 campuses, plan for building GO8s
	// If we have enough resources to build a GO8...
	//    Upgrade the most valued campus to a GO8


	// Note that this AI will only build ARCs and campuses together at the same time
	// If we have enough resources to build an ARC and campus...
	// Get a list of all the "edge" vertices
	// Iterate through each connecting edge vertices, and give them a value
	// based off the hexes they are connected to.
	// For every campus of already existing resource, a point is subtracted
	// Their values is summed of the vertices they're connected to, too.
	// Cumalative values are halved for every vertex travelled to a maximum of 4 jumps
	// Then pick the highest scoring sub-vertex.
	// If there are multiple highest scoring sub-vertices, pick the one with the lowest index

	// Find vertices we own

	int myVertices[NUM_INT_VERTICES]; // Array of vertices that we own

	i = 0;
	int numMyVertices = 0;

	while (i < NUM_INT_VERTICES) {
		myVertices[i] = -1;

		if (vertices[i].object == myCampus || vertices[i].object == myGO8) {
			myVertices[numMyVertices] = i;
			numMyVertices++;
			// printf("%d\n", i);
		}
		i++;
	}

	// printf("Found our vertices\n");

	// Now scan through each vertex and store neighbours

	// Array of vertices that are accessible and aren't owned
	fromToArc considerations[NUM_INT_VERTICES][2];

	i = 0;
	int numConsiderations = 0;

	while (i < numMyVertices) {
		trio neighbouring = getNeighbouringVertices(myVertices[i]);

		if (neighbouring.a >= 0 && vertices[neighbouring.a].object == VACANT_VERTEX) {
			int arcId = getArcIdFromVertices(myVertices[i], neighbouring.a);

			if (arcs[arcId].object == VACANT_ARC || arcs[arcId].object == myARC) {
				considerations[numConsiderations][0].from = myVertices[i];
				considerations[numConsiderations][0].to = neighbouring.a;

				if (arcs[arcId].object == myARC) {
					considerations[numConsiderations][0].alreadyOwned = TRUE;
				} else {
					considerations[numConsiderations][0].alreadyOwned = FALSE;
				}

				numConsiderations++;
			}
		}
		if (neighbouring.b >= 0 && vertices[neighbouring.b].object == VACANT_VERTEX) {
			int arcId = getArcIdFromVertices(myVertices[i], neighbouring.b);
			if (arcs[arcId].object == VACANT_ARC || arcs[arcId].object == myARC) {
				considerations[numConsiderations][0].from = myVertices[i];
				considerations[numConsiderations][0].to = neighbouring.b;

				if (arcs[arcId].object == myARC) {
					considerations[numConsiderations][0].alreadyOwned = TRUE;
				} else {
					considerations[numConsiderations][0].alreadyOwned = FALSE;
				}

				numConsiderations++;
			}
		}
		if (neighbouring.c >= 0 && vertices[neighbouring.c].object == VACANT_VERTEX) {
			int arcId = getArcIdFromVertices(myVertices[i], neighbouring.c);

			if (arcs[arcId].object == VACANT_ARC || arcs[arcId].object == myARC) {
				considerations[numConsiderations][0].from = myVertices[i];
				considerations[numConsiderations][0].to = neighbouring.c;

				if (arcs[arcId].object == myARC) {
					considerations[numConsiderations][0].alreadyOwned = TRUE;
				} else {
					considerations[numConsiderations][0].alreadyOwned = FALSE;
				}

				numConsiderations++;
			}
		}

		i++;
	}

	fromToArc subConsiderations[NUM_INT_VERTICES][2];

	i = 0;
	int numSubConsiderations = 0;

	while (i < numConsiderations) {
		trio neighbouring = getNeighbouringVertices(considerations[i][0].to);

		if (neighbouring.a >= 0 && vertices[neighbouring.a].object == VACANT_VERTEX) {
			// Check that it's not within any other verticie
			if (canBuildCampusOn(vertices, neighbouring.a)) {
				int arcId = getArcIdFromVertices(considerations[i][0].to, neighbouring.a);

				if (arcs[arcId].object == VACANT_ARC || arcs[arcId].object == myARC) {
					subConsiderations[numSubConsiderations][0] = considerations[i][0];
					subConsiderations[numSubConsiderations][1] = considerations[i][1];

					subConsiderations[numSubConsiderations][1].from = considerations[i][0].to;
					subConsiderations[numSubConsiderations][1].to = neighbouring.a;

					if (arcs[arcId].object == myARC) {
						subConsiderations[numSubConsiderations][1].alreadyOwned = TRUE;
					} else {
						subConsiderations[numSubConsiderations][1].alreadyOwned = FALSE;
					}

					numSubConsiderations++;
				}
			}
		}

		if (neighbouring.b >= 0 && vertices[neighbouring.b].object == VACANT_VERTEX) {
			// Check that it's not within any other verticie
			if (canBuildCampusOn(vertices, neighbouring.b)) {
				int arcId = getArcIdFromVertices(considerations[i][0].to, neighbouring.b);

				if (arcs[arcId].object == VACANT_ARC || arcs[arcId].object == myARC) {
					subConsiderations[numSubConsiderations][0] = considerations[i][0];
					subConsiderations[numSubConsiderations][1] = considerations[i][1];

					subConsiderations[numSubConsiderations][1].from = considerations[i][0].to;
					subConsiderations[numSubConsiderations][1].to = neighbouring.b;

					if (arcs[arcId].object == myARC) {
						subConsiderations[numSubConsiderations][1].alreadyOwned = TRUE;
					} else {
						subConsiderations[numSubConsiderations][1].alreadyOwned = FALSE;
					}

					numSubConsiderations++;
				}
			}
		}

		if (neighbouring.c >= 0 && vertices[neighbouring.c].object == VACANT_VERTEX) {
			// Check that it's not within any other verticie
			if (canBuildCampusOn(vertices, neighbouring.c)) {
				int arcId = getArcIdFromVertices(considerations[i][0].to, neighbouring.c);

				if (arcs[arcId].object == VACANT_ARC || arcs[arcId].object == myARC) {
					subConsiderations[numSubConsiderations][0] = considerations[i][0];
					subConsiderations[numSubConsiderations][1] = considerations[i][1];

					subConsiderations[numSubConsiderations][1].from = considerations[i][0].to;
					subConsiderations[numSubConsiderations][1].to = neighbouring.c;

					if (arcs[arcId].object == myARC) {
						subConsiderations[numSubConsiderations][1].alreadyOwned = TRUE;
					} else {
						subConsiderations[numSubConsiderations][1].alreadyOwned = FALSE;
					}

					numSubConsiderations++;
				}
			}
		}

		i++;
	}

	// printf("Determined considerations\n");

	// Remove duplicate paths to the same vertex

	i = 0;
	int numPossibilities = 0;
	fromToArc possibilities[NUM_INT_VERTICES][2];

	while (i < numSubConsiderations) {
		// First result dominates others, unless it has an alreadyOwned flag
		// printf("%d: %d\n", i, considerations[i][1].to);

		int search = subConsiderations[i][1].to;

		int removeAll = FALSE;

		if (search >= 0) {
			possibilities[numPossibilities][0] = subConsiderations[i][0];
			possibilities[numPossibilities][1] = subConsiderations[i][1];

			int j = 0;
			while (j < numSubConsiderations) {
				if (subConsiderations[j][1].to == search) {
					// Same
					if (!removeAll && (subConsiderations[j][0].alreadyOwned ||
						subConsiderations[j][1].alreadyOwned)) {
						// Better option
						removeAll = TRUE;

						fromToArc pos1 = subConsiderations[j][0];
						fromToArc pos2 = subConsiderations[j][1];

						possibilities[numPossibilities][0] = pos1;
						possibilities[numPossibilities][1] = pos2;

						subConsiderations[j][1].to = -1;

						numPossibilities++;
					}
					subConsiderations[j][1].to = -1;
				}

				j++;
			}

			if (!removeAll) {
				numPossibilities++;
			}
		}

		i++;
	}

	// Now get the weight values of each consideration


	weightedVertex sortedWeights[numPossibilities];

	i = 0;
	while (i < numPossibilities) {
		int weight = getRecursiveVertexWeight(g, vertices, myVertices, numMyVertices,
			possibilities[i][1].to);
		weightedVertex newVertex;
		newVertex.weight = weight;
		newVertex.arcPath[0] = possibilities[i][0];
		newVertex.arcPath[1] = possibilities[i][1];

		sortedWeights[i] = newVertex;

		i++;
	}

	sortWeights(sortedWeights, numPossibilities);

	// printf("Weighted considerations\n");

	// Buy in order of weighting

	// i = 0;

	// while (i < numPossibilities) {
	// 	printf("%d\n", sortedWeights[i].arcPath[1].to);
	// 	i++;
	// }

	int attempt = 0;
	int domination = FALSE;

	if (numPossibilities == 0) {
		printf("Reached campus domination\n");
		domination = TRUE;
	}

	int GO8domination = FALSE;

	if (domination) {
		// Build GO8s
		weightedVertex sortedMyCampuses[numMyVertices];
		int numMyCampuses = 0;

		i = 0;
		while (i < numMyVertices) {
			if (vertices[myVertices[i]].object == myCampus) {
				int weight = getSingleVertexWeight(g, vertices, myVertices,
					numMyVertices, myVertices[i]);

				weightedVertex newVertex;
				newVertex.weight = weight;

				fromToArc newDestination = {0};
				newDestination.to = myVertices[i];
				newVertex.arcPath[0] = newDestination;

				sortedMyCampuses[numMyCampuses] = newVertex;

				numMyCampuses++;
			}

			i++;
		}

		sortWeights(sortedMyCampuses, numMyCampuses);

		int totalGO8s = getGO8s(g, UNI_A) + getGO8s(g, UNI_B) +
			getGO8s(g, UNI_C);

		if (numMyCampuses == 0 || totalGO8s >= 8) {
			printf("Reached GO8 Domiation\n");
			GO8domination = TRUE;
			numMyCampuses = 0;
		}

		i = 0;
		while (i < numMyCampuses) {
			if (enoughToBuildGO8(g, currentPlayer)) {
				action go8Action;
				go8Action.actionCode = BUILD_GO8;

				strcpy(go8Action.destination,
					vertices[sortedMyCampuses[i].arcPath[0].to].path);

				if (isLegalAction(g, go8Action)) {
					return go8Action;
					printf("Built GO8 at %d\n",
						sortedMyCampuses[i].arcPath[0].to);
				} else {
					printf("Could not build GO8 at %d\n",
						sortedMyCampuses[i].arcPath[0].to);
				}
			}

			i++;
		}
	}

	if (GO8domination) {
		// Wow pls, make spinoffs
		while (enoughToStartSpinoff(g, currentPlayer)) {
			action spinoffAction;
			spinoffAction.actionCode = START_SPINOFF;

			return spinoffAction;
		}
	}

	while (attempt < numPossibilities &&
		enoughToBuildCampus(g, currentPlayer, sortedWeights[attempt].arcPath)) {
		int firstArc = getArcIdFromVertices(sortedWeights[attempt].arcPath[0].from,
			sortedWeights[attempt].arcPath[0].to);

		if (firstArc < 0) {
			printf("Could not find path between %d and %d!\n",
				sortedWeights[attempt].arcPath[0].from,
				sortedWeights[attempt].arcPath[0].to);
		} else {
			if (!sortedWeights[attempt].arcPath[0].alreadyOwned) {
				action pathAction;

				pathAction.actionCode = OBTAIN_ARC;
				strcpy(pathAction.destination, arcs[firstArc].path);

				if (!isLegalAction(g, pathAction)) {
					printf("ARC between %d and %d is not legal?\n",
						sortedWeights[attempt].arcPath[0].from,
						sortedWeights[attempt].arcPath[0].to);
				} else {
					printf("Bought ARC between %d and %d\n",
					sortedWeights[attempt].arcPath[0].from,
					sortedWeights[attempt].arcPath[0].to);
					return pathAction;
				}
			}

			int secondArc = getArcIdFromVertices(sortedWeights[attempt].arcPath[1].from,
				sortedWeights[attempt].arcPath[1].to);

			if (secondArc < 0) {
				printf("Could not find path between %d and %d!\n",
					sortedWeights[attempt].arcPath[1].from,
					sortedWeights[attempt].arcPath[1].to);
			} else {
				if (!sortedWeights[attempt].arcPath[1].alreadyOwned) {
					action pathAction;

					pathAction.actionCode = OBTAIN_ARC;
					strcpy(pathAction.destination, arcs[secondArc].path);

					if (!isLegalAction(g, pathAction)) {
						printf("ARC between %d and %d is not legal?\n",
							sortedWeights[attempt].arcPath[1].from,
							sortedWeights[attempt].arcPath[1].to);
					} else {
						printf("Bought ARC between %d and %d\n",
						sortedWeights[attempt].arcPath[1].from,
						sortedWeights[attempt].arcPath[1].to);
						return pathAction;
					}
				}

				int vertexId = sortedWeights[attempt].arcPath[1].to;

				action campusAction;

				strcpy(campusAction.destination, vertices[vertexId].path);
				campusAction.actionCode = BUILD_CAMPUS;

				if (!isLegalAction(g, campusAction)) {
					printf("Campus at %d is not legal?\n", vertexId);
				} else {
					printf("Built campus at %d\n", vertexId);
					return campusAction;
				}
			}
		}

		attempt++;
	}

	action passAction;
	passAction.actionCode = PASS;

	return passAction;
}