contract SaleBook is Ownable {
    using SafeMath for uint256;

    // admins info
    mapping (uint256 => address) public admins;
    mapping (address => uint256) public adminId;
    uint256 public adminCount = 0;

    // investor data
    struct InvestorData {
        uint256 id;
        address addr;
        uint256 ref;
        address ref1;
        address ref2;
        uint256 reffed;
        uint256 topref;
        uint256 topreffed;
    }

    mapping (uint256 => InvestorData) public investors;
    mapping (address => uint256) public investorId;
    uint256 public investorCount = 0;

    event AdminAdded(address indexed _addr, uint256 _id, address indexed _adder);
    event AdminRemoved(address indexed _addr, uint256 _id, address indexed _remover);
    event InvestorAdded(address indexed _addr, uint256 _id, address _ref1, address _ref2, address indexed _adder);

    // check the address is human or contract
    function isHuman(address _addr) public view returns (bool) {
        uint256 _codeLength;
        assembly {_codeLength := extcodesize(_addr)}
        return (_codeLength == 0);
    }

    modifier validAddress(address _addr) {
        require(_addr != 0x0);
        _;
    }

    modifier onlyAdmin() {
        require(adminId[msg.sender] != 0);
        _;
    }

    constructor() public {
        adminId[address(0x0)] = 0;
        admins[0] = address(0x0);

        investorId[address(0x0)] = 0;
        investors[0] = InvestorData({id: 0, addr: address(0x0), ref: 0, ref1: address(0x0), ref2: address(0x0), reffed: 0, topref: 0, topreffed: 0});


        // first admin is owner
        addAdmin(owner);
    }

    // owner may add or remove admins
    function addAdmin(address _admin) onlyOwner validAddress(_admin) public {
        require(isHuman(_admin));

        uint256 id = adminId[_admin];
        if (id == 0) {
            id = adminCount.add(1);
            adminId[_admin] = id;
            admins[id] = _admin;
            adminCount = id;
            emit AdminAdded(_admin, id, msg.sender);
        }
    }

    function removeAdmin(address _admin) onlyOwner validAddress(_admin) public {
        require(adminId[_admin] != 0);

        uint256 aid = adminId[_admin];
        adminId[_admin] = 0;
        for (uint256 i = aid; i < adminCount; i++){
            admins[i] = admins[i + 1];
            adminId[admins[i]] = i;
        }
        delete admins[adminCount];
        adminCount--;
        emit AdminRemoved(_admin, aid, msg.sender);
    }

    // admins may batch add investors, and investors cannot be removed
    function addInvestor(address _addr, address _ref1, address _ref2) validAddress(_addr) internal returns (uint256) {
        require(investorId[_addr] == 0 && isHuman(_addr));
        if (investorId[_ref1] == 0) _ref1 = address(0x0);
        if (investorId[_ref2] == 0) _ref2 = address(0x0);

        investorCount++;
        investorId[_addr] = investorCount;

        investors[investorCount] = InvestorData({id: investorCount, addr: _addr, ref: 0, ref1: _ref1, ref2: _ref2, reffed: 0, topref: 0, topreffed: 0});

        emit InvestorAdded(_addr, investorCount, _ref1, _ref2, msg.sender);
        return investorCount;
    }

}
// SaleBook Contract
function addAdmin(address _admin) onlyOwner validAddress(_admin) public {
    require(isHuman(_admin));

    uint256 id = adminId[_admin];
    if (id == 0) {
        id = adminCount.add(1);
        adminId[_admin] = id;
        admins[id] = _admin;
        adminCount = id;
        emit AdminAdded(_admin, id, msg.sender);
    }
}

// owner may add or remove admins