// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.7.1;
import { ABDKMath64x64 } from "abdk-libraries-solidity/ABDKMath64x64.sol";
import "./BaseRestorableSalary.sol";
import "./DAOInterface.sol";

/// Salary system with a "DAO" that can assign attorneys to restore lost Ethereum accounts.
/// @author Victor Porton
/// @notice Not audited, not enough tested.
contract SalaryWithDAO is BaseRestorableSalary {
    using ABDKMath64x64 for int128;

    /// The DAO interface.
    DAOInterface public daoPlugin;

    /// When set to true, your account can't be moved to new address (by the DAO).
    ///
    /// By default new users are not under DAO control to avoid front-running of resigning control
    /// by an evil DAO.
    ///
    /// Mapping (current address => under control)
    mapping (address => bool) public underDAOControl;

    /// Mapping (current address => account has at least one salary).
    mapping (address => bool) public accountHasSalary;

    // DAO share will be zero to prevent theft by voters and because it can be done instead by future voting.
    // int128 public daoShare = int128(0).div(1); // zero by default

    /// Constructor.
    /// @param _daoPlugin The DAO interface.
    /// @param _uri The ERC-1155 token URI.
    constructor(DAOInterface _daoPlugin, string memory _uri)
        BaseRestorableSalary(_uri)
    {
        daoPlugin = _daoPlugin;
    }

    /// Create an oracle for caclcualting salary amounts.
    function createOracle() external returns (uint64) {
        return _createOracle();
    }

    /// Register a salary recipient.
    ///
    /// Can be called both before or after the oracle finish. However registering after the finish is useless.
    ///
    /// Anyone can register anyone (useful for robots registering a person).
    ///
    /// Registering another person is giving him money against his will (forcing to hire bodyguards, etc.),
    /// but if one does not want, he can just not associate this address with his identity in his publications.
    /// @param _customer The original address.
    /// @param _oracleId The oracle ID.
    /// @param _underDAOControl If the registered address will be under DAO control.
    /// @param _data The current data.
    function registerCustomer(address _customer, uint64 _oracleId, bool _underDAOControl, bytes calldata _data)
        virtual public returns (uint256)
    {
        address _orig = _originalAddress(_customer);
        // Auditor: Check that this value is set to false, when (and if) necessary.
        accountHasSalary[_customer] = true;
        underDAOControl[_customer] = _underDAOControl; // We don't trigger and event to reduce gas usage.
        return super._registerCustomer(_orig, _oracleId, _data);
    }

    /// A user can agree for DAO control. Then his account can be restored by DAO for the expense
    /// of the DAO assigned personnel or software being able to steal his funds.
    ///
    /// Be exteremely careful calling this method: If you refuse and lose your key, your funds are lost!
    ///
    /// Fishers may trick one to resign mistakenly. However, it's no much worse than just fishing for
    /// withdrawing the salary token, because a user could just register anew and notify traders/oracles
    /// that it's the same person.
    function setDAOControl(bool _underControl) public {
        address _orig = _originalAddress(msg.sender);
        require(accountHasSalary[_orig], "Cannot resign account receiving a salary.");
        underDAOControl[_orig] = _underControl; // We don't trigger and event to reduce gas usage.
    }

    /// The DAO can replace itself.
    function setDAO(DAOInterface _daoPlugin) public onlyDAO {
        daoPlugin = _daoPlugin;
    }

    /// Set the token URI.
    function setURI(string memory _newuri) public onlyDAO {
        _setURI(_newuri);
    }

    // Overrides ///

    function checkAllowedRestoreAccount(address _oldAccount, address _newAccount)
        public virtual override isUnderDAOControl(_oldAccount)
    {
        daoPlugin.checkAllowedRestoreAccount(_oldAccount, _newAccount);
    }

    /// Allow the user to unrestore by himself?
    /// We won't not allow it to `_oldAccount` because it may be a stolen private key.
    /// We could allow it to `_newAccount`, but this makes no much sense, because
    /// it would only prevent the user to do a theft by himself, let only DAO could be allowed to do.
    function checkAllowedUnrestoreAccount(address _oldAccount, address _newAccount)
        public virtual override isUnderDAOControl(_oldAccount)
    {
        daoPlugin.checkAllowedUnrestoreAccount(_oldAccount, _newAccount);
    }

    // Internal //

    function _isDAO() internal view returns (bool) {
        return msg.sender == address(daoPlugin);
    }

    // Modifiers //

    modifier onlyDAO() {
        require(_isDAO(), "Only DAO can do.");
        _;
    }

    /// @param _customer The current address.
    modifier isUnderDAOControl(address _customer) {
        require(underDAOControl[_customer], "Not under DAO control.");
        _;
    }
}
