contract CreditCarbonRegistry is AccessControl {

    address public carbonInventoryControl;
    bool public registerOnlyMode = false;

    mapping(uint256 => Project) public projects;
    mapping(string => Batch) public batches;

    bytes32 public constant CERTIFIER_ROLE = keccak256("CERTIFIER_ROLE");

    struct Project {
        string name;
        string projectType;
        string certifications;
        bool created;
    }

    struct Batch {
        string serialNumber;
        uint256 projectId;
        string vintage;
        string creditType;
        uint256 units;
        address broker;
        address token;
        bytes32 originalTx;
        bool created;
    }

    modifier onlyAdmin() {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Registry: sender is not an ADMIN");
        _;
    }

    modifier onlyCertifier() {
        require(hasRole(CERTIFIER_ROLE, msg.sender), "Registry: sender is not a CERTIFIER");
        _;
    }

    event ProjectCreated(uint256 projectId, string name, string projectType, string certifications, address indexed certifier);
    event ProjectUpdated(uint256 projectId, string name, string projectType, string certifications, address indexed certifier);
    event BatchCreated(string serialNumber, uint256 projectId, string vintage, string creditType, uint256 units, address broker, address token, address indexed certifier);
    event BatchUpdated(string serialNumber, uint256 projectId, string vintage, string creditType, uint256 units, address broker, address token, address indexed certifier);

    constructor(address _carbonInventoryControl)
        public {
            _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
            carbonInventoryControl = _carbonInventoryControl;
        }

    function updateInventoryControl(address _newAddress)
        external
        onlyAdmin {
            carbonInventoryControl = _newAddress;
        }

    function setRegistryOnlyMode(bool _mode)
        external
        onlyAdmin {
            registerOnlyMode = _mode;
    }

    /**
     * @dev function to register carbon credit projects.
     * @param _id Carbon credit project id.
     * @param _name Carbon credit project name.
     * @param _type Carbon credit project type.
     * @param _certifications Carbon credit project certifications.
     */
    function createProject(
        uint256 _id,
        string calldata _name,
        string calldata _type,
        string calldata _certifications
    )
        external
        onlyCertifier {
            require(!projects[_id].created, "Registry: project already created");

            projects[_id] = Project(_name, _type, _certifications, true);

            emit ProjectCreated(_id, _name, _type, _certifications, msg.sender);
    }

    /**
     * @dev function to register carbon credit projects.
     * @param _id Carbon credit project id.
     * @param _name Carbon credit project name.
     * @param _type Carbon credit project type.
     * @param _certifications Carbon credit project certifications.
     */
    function updateProject(
        uint256 _id,
        string calldata _name,
        string calldata _type,
        string calldata _certifications
    )
        external
        onlyCertifier {
            require(projects[_id].created, "Registry: empty project");

            projects[_id] = Project(_name, _type, _certifications, true);

            emit ProjectUpdated(_id, _name, _type, _certifications, msg.sender);
    }

    /**
     * @dev function to register carbon credit batches linked to vintages. Each batch will mint credits on Inventory Control provided that the contract is NOT in register only mode
     * @param _projectId Carbon credit project id
     * @param _vintage Carbon credit vintage
     * @param _serialNumber serial number of Carbon credits delivered on the batch
     * @param _creditType Types of carbon credits delivered on batch
     * @param _units Total units deliverd on batch
     * @param _broker wallets adress of carbon credit broker
     * @param _token Commercial token to delivery credits on market
     * @param _originalTx Optional link to another mint transaction
     */
    function createBatch(
        string calldata _serialNumber,
        uint256 _projectId,
        string calldata _vintage,
        string calldata _creditType,
        uint256 _units,
        address _broker,
        address _token,
        bytes32 _originalTx
    )
        external
        onlyCertifier {
            require(!batches[_serialNumber].created, "Registry: batch already created");

            batches[_serialNumber] = Batch(_serialNumber, _projectId, _vintage, _creditType, _units, _broker, _token, _originalTx, true);

            emit BatchCreated(_serialNumber, _projectId, _vintage, _creditType, _units, _broker, _token, msg.sender);

            if (registerOnlyMode) return;

            CarbonInventoryControl(carbonInventoryControl).mint(_broker, _broker, _units, _token);
    }

    /**
     * @dev function to update a batch. If sender wants the update to mint tokens, should send _mintTokens: true
     * @param _projectId Carbon credit project id
     * @param _vintage Carbon credit vintage
     * @param _serialNumber serial number of Carbon credits delivered on the batch
     * @param _creditType Types of carbon credits delivered on batch
     * @param _units Total units deliverd on batch
     * @param _broker wallets adress of carbon credit broker
     * @param _token Commercial token to delivery credits on market
     * @param _originalTx Optional link to another mint transaction
     * @param _mintTokens Wheter the update should mint credits
     */
    function updateBatch(
        string calldata _serialNumber,
        uint256 _projectId,
        string calldata _vintage,
        string calldata _creditType,
        uint256 _units,
        address _broker,
        address _token,
        bytes32 _originalTx,
        bool _mintTokens
    )
        external
        onlyCertifier {
            require(batches[_serialNumber].created, "Registry: empty batch");

            batches[_serialNumber] = Batch(_serialNumber, _projectId, _vintage, _creditType, _units, _broker, _token, _originalTx, true);

            emit BatchUpdated(_serialNumber, _projectId, _vintage, _creditType, _units, _broker, _token, msg.sender);

            if (_mintTokens) {
                CarbonInventoryControl(carbonInventoryControl).mint(_broker, _broker, _units, _token);
            }
        }

}
function updateBatch(
        string calldata _serialNumber,
        uint256 _projectId,
        string calldata _vintage,
        string calldata _creditType,
        uint256 _units,
        address _broker,
        address _token,
        bytes32 _originalTx,
        bool _mintTokens
    )
        external
        onlyCertifier {
            require(batches[_serialNumber].created, "Registry: empty batch");

            batches[_serialNumber] = Batch(_serialNumber, _projectId, _vintage, _creditType, _units, _broker, _token, _originalTx, true);

            emit BatchUpdated(_serialNumber, _projectId, _vintage, _creditType, _units, _broker, _token, msg.sender);

            if (_mintTokens) {
                CarbonInventoryControl(carbonInventoryControl).mint(_broker, _broker, _units, _token);
            }
        }
/**
 * @dev function to update a batch. If sender wants the update to mint tokens, should send _mintTokens: true
 * @param _projectId Carbon credit project id
 * @param _vintage Carbon credit vintage
 * @param _serialNumber serial number of Carbon credits delivered on the batch
 * @param _creditType Types of carbon credits delivered on batch
 * @param _units Total units deliverd on batch
 * @param _broker wallets adress of carbon credit broker
 * @param _token Commercial token to delivery credits on market
 * @param _originalTx Optional link to another mint transaction
 * @param _mintTokens Wheter the update should mint credits
 */