// SPDX-License-Identifier: GPL-3.0-or-later

// This file is part of the http://github.com/aaronbloomfield/ccc repository,
// and is released under the GPL 3.0 license.

pragma solidity ^0.8.24;

import "./IPoll.sol";

// A smart contract to allow voting on something.
contract Poll is IPoll {

	// struct Choice is defined in IPoll.sol, so we can't re-define
	// it here, and is defined as:
	//
	// struct Choice {
	//   uint id;
	//   string name;
	//   uint votes;
	// }

	// The following two events are defined in IPoll.sol, so we can't
	// re-define them here.  They are defined as:
	//
	// event votedEvent (uint indexed _id);
	// event choiceAddedEvent (uint indexed _id);

	// A mapping to keep track of who has voted; the fact that it is public
	// means that a getter function called voted() will also be defined.  As
	// this voted() function is implementing the one specified in
	// IPoll.sol, we have to put the 'override' keyword here.
	mapping (address => bool) public override voted;

	// A mapping to store the various chioces.  Because the type of this
	// mapping (Chioce storage) is ever so slightly different than what is
	// returned from the choices() function defined in IPoll.sol
	// (Choices memory), we can't declare this as public to get an getter
	// function.
	mapping (uint => Choice) internal _choices;

	// This is the getter function for the above mapping that solves the type
	// difference (i.e., that "Choices storage" != "Choices memory").  As
	// this choices() function is implementing the one specified in
	// IPoll.sol, we have to put the 'override' keyword here.
	function choices(uint i) public view override returns (Choice memory) {
		return _choices[i];
	}

	// How many choices have been added?  It's public, so we get a free getter
	// function called num_choices().  As this num_choices() function is
	// implementing the one specified in IPoll.sol, we have to put
	// the 'override' keyword here.
	uint public override num_choices;

	// This allows the voter to know what this poll is voting on.  It's
	// public, so we get a free getter function called purpose().  As this
	// purpose() function is implementing the one specified in IPoll.sol, we
	// have to put the 'override' keyword here.
	string public override constant purpose = "Vote on your favorite color";

	// The constructor, which runs when it's first deployed to the blockchain;
	// this adds our voting choices.
	// YOU HAVE TO CHANGE THE CODE HEREIN -- make up your own choices
	constructor() {
		addChoice("red");
		addChoice("orange");
		addChoice("yellow");
		addChoice("green");
		addChoice("blue");
		addChoice("purple");
	}

	// The function that adds a choice to be voted upon.  It implements the
	// function of the same name from IPoll.sol, so we put
	// the 'override' keyword here.
	function addChoice (string memory _name) public override {
		_choices[num_choices] = Choice(num_choices, _name, 0);
		emit choiceAddedEvent(num_choices);
		num_choices++;
	}

	// The function that allows one to vote; it checks if that account has
	// already voted, and prevents double voting.  It implements the function
	// of the same name from IPoll.sol, so we put the 'override'
	// keyword here.
	function vote (uint _id) public override {
		require(!voted[msg.sender], "sender has already voted");
		require(_id >= 0 && _id < num_choices, "invalid vote selection");
		voted[msg.sender] = true;
		_choices[_id].votes++;
		emit votedEvent(_id);
	}

	// This function is, as its name implies, unnecessary; it is there to show
	// a read-only function -- meaning one that does not modify the state of
	// the object at all.  As it is not defined in IPoll.sol, we do
	// not need the 'override' keyword here.
	function unnecessaryFunction() public view returns (string memory) {
		return _choices[0].name;
	}

	// We'll see the necessity and use of this function later, when we talk
	// about Tokens.  For now, keep this function exactly as-is.  As it is
	// not defined in IPoll.sol, we do not need the 'override'
	// keyword here.
	function supportsInterface(bytes4 interfaceId) external override pure returns (bool) {
		return interfaceId == type(IPoll).interfaceId || interfaceId == 0x01ffc9a7;
	}

}