contract arbits_presale {

    // libraries
    using topl_database_lib for address;
    using SafeMath for uint256;

    // contract level vars
    address public owner;
    address public db;


    // helpful data structs
    struct participant {
        address eth_address; // your eth address
        uint256 topl_address; // your topl address
        uint256 arbits; // the amount of a arbits you have
        uint256 num_of_pro_rata_tokens_alloted;
        bool arbits_kyc_whitelist; // if you pass arbits level kyc you get this
        uint8 num_of_uses;
    }

    // permissions
    constructor(address __db) public {
        db = __db;
        owner = msg.sender;
    }

    function owner_linkage() public { // must be called after the sale contract has been linked to the database contract via database's add master function
        db.set_sale_owner(owner, true);
    }

    modifier only_owner() {
        require(db.get_sale_owner(msg.sender));
        _;
    }

    function add_owner(address __subject) public only_owner {
        db.set_sale_owner(__subject, true);
        emit e_add_owner(msg.sender, __subject);
    }

    function remove_owner(address __subject) public only_owner {
        db.set_sale_owner(__subject, false);
        emit e_remove_owner(msg.sender, __subject);
    }

    // general modifiers
    modifier presale_open() {
        require(db.get_arbits_presale_open());
        _;
    }

    modifier use_count() {
        uint8 uses = db.get_num_of_uses(msg.sender);
        require(uses < 5);
        db.set_num_of_uses(msg.sender, uses + 1);
        _;
    }

    // functionality
    function participate_in_arbits_presale_crypto() public payable presale_open use_count {
        /////////////////////////////////////////////////////////////////////
                                                                           //
        (                                                                  //
            address p1,                                                    //
            uint256 p2,                                                    // LOAD
            uint256 p3,                                                    // PARTICIPANT
            uint256 p4,                                                    // DATA
            bool p5,                                                       // FROM
            uint8 p6                                                       // DATABASE
        ) = db.get_participant(msg.sender);                                //
        participant memory subject = participant(p1, p2, p3, p4, p5, p6);  //
                                                                           //
        /////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                                          //
        uint256 subject_tokens_to_add = msg.value.mul(db.get_presale_arbits_per_ether()).div(1 ether);                                    //
        if (subject.arbits_kyc_whitelist) {                                                                                               // Arbits
            subject_tokens_to_add = subject_tokens_to_add.mul(db.get_pre_kyc_bonus_numerator()).div(db.get_pre_kyc_bonus_denominator());  // Purchased
        }                                                                                                                                 // Calculation
                                                                                                                                          //
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        // Note: users must send ether in amounts that are evenly divide tokens_per_ether.
        // Ex: If tokens_per_ether = 4 and a user sends .9 ether they will receive 3 tokens and forfeit .15 ether.
        // The correct interaction would be to send some increment of .25 ether.

        //////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                            //
        require(db.get_presale_arbits_total() >= db.get_presale_arbits_sold().add(subject_tokens_to_add));  // Sale
        require(db.get_arbits_max_contribution() >= subject_tokens_to_add); // max                          // Limit
        require(db.get_arbits_min_contribution() <= subject_tokens_to_add); // min                          // Checks
                                                                                                            //
        //////////////////////////////////////////////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////////////////////
                                                                                   //
        db.add_presale_arbits_sold(subject_tokens_to_add); // update sold counter  // Update
        db.add_arbits(msg.sender, subject_tokens_to_add); // update arbits         // Database
                                                                                   //
        /////////////////////////////////////////////////////////////////////////////

        ///////////////////////////////////////////////////////////////////////
                                                                             //
        emit e_participate_in_arbits_presale_crypto(msg.sender, msg.value);  // Event
                                                                             //
        ///////////////////////////////////////////////////////////////////////
    }

    function participate_in_arbits_presale_fiat(address _a, uint256 _t) public only_owner {
        //////////////////////////////////////////////////////////
                                                                //
        db.add_presale_arbits_sold(_t); // update sold counter  // Update
        db.add_arbits(_a, _t); // update arbits                 // Database
                                                                //
        //////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////
                                                            //
        emit e_participate_in_arbits_presale_fiat(_a, _t);  // Event
                                                            //
        //////////////////////////////////////////////////////
    }

    function() public payable {
        participate_in_arbits_presale_crypto(); // allows users to participate without an explicit function call
        emit e_fallback(msg.sender, msg.value);
    }

    // owner withdrawals
    function kill_and_withdraw(address withdraw_to) public only_owner {
        emit e_kill_and_withdraw(withdraw_to);
        selfdestruct(withdraw_to);
    }

    function withdraw_some_amount(address withdraw_to, uint256 amount) public only_owner {
        withdraw_to.transfer(amount); // amount in wei, throws if error
        emit e_withdraw_some_amount(withdraw_to, amount);
    }

    // arbit specific sale settings
    function set_sale_open() public only_owner {
        require(db.get_presale_arbits_per_ether() > 0);
        require(db.get_arbits_max_contribution() > 0);
        require(db.get_arbits_min_contribution() > 0);
        require(db.get_pre_kyc_bonus_numerator() > 0);
        require(db.get_pre_kyc_bonus_denominator() > 0);
        db.set_arbits_presale_open(true);
    }

    function set_sale_closed() public only_owner {
        db.set_arbits_presale_open(false);
    }

    function set_tokens_per_ether(uint256 _v) public only_owner {
        db.set_presale_arbits_per_ether(_v);
    }

    function set_pre_kyc_bonus_numerator(uint256 _v) public only_owner {
        db.set_pre_kyc_bonus_numerator(_v);
    }

    function set_pre_kyc_bonus_denominator(uint256 _v) public only_owner {
        db.set_pre_kyc_bonus_denominator(_v);
    }

    //// This section provides external functionality for modifying the database 
    // whitelist
    function add_to_whitelist(address _a) public only_owner {
        db.set_arbits_whitelist(_a, true);
        emit e_add_to_whitelist(msg.sender, _a);
    }

    function remove_from_whitelist(address _a) public only_owner {
        db.set_arbits_whitelist(_a, false);
        emit e_remove_from_whitelist(msg.sender, _a);
    }

    // general sale settings
    function set_max_contribution(uint256 _v) public only_owner {
        db.set_arbits_max_contribution(_v);
    }

    function set_min_contribution(uint256 _v) public only_owner {
        db.set_arbits_min_contribution(_v);
    }

    function set_tokens_total(uint256 _v) public only_owner {
        db.set_presale_arbits_total(_v);
    }

    function set_tokens_sold(uint256 _v) public only_owner {
        db.set_presale_arbits_sold(_v);
    }

    // helpers
    function is_presale_open() public view returns(bool) {
        return db.get_arbits_presale_open();
    }

    function am_i_on_the_whitelist() public view returns(bool) {
        return db.get_arbits_whitelist(msg.sender);
    }

    function how_many_arbits_do_i_have() public view returns(uint256) {
        return db.get_arbits(msg.sender);
    }

    // events
    //
    // All storage calls are logged via events emitted in the library functions.
    // Because web3 bugs out when when libraries call events that aren't defined in
    // the parent contract. We redefine them here.
    //
    // contract level events
    event e_add_owner(address addres1, address address2); // adder, addie <-------- These are words now!
    event e_remove_owner(address addres1, address address2); // remover, removie <_/
    event e_add_to_whitelist(address, address); // adder, addie
    event e_remove_from_whitelist(address, address); // remover, removie
    event e_participate_in_arbits_presale_fiat(address, uint256); // person getting arbits, number of arbits
    event e_participate_in_arbits_presale_crypto(address, uint256); // msg.sender, msg.value
    event e_fallback(address, uint256); // msg.sender, msg.value (used to gather data on what %
    // of people just send ether vs sending a function call
    event e_kill_and_withdraw(address); // person that just took all our money
    event e_withdraw_some_amount(address, uint256); // withdrawal address, amount withdrawn
}
function set_sale_open() public only_owner {
    require(db.get_presale_arbits_per_ether() > 0);
    require(db.get_arbits_max_contribution() > 0);
    require(db.get_arbits_min_contribution() > 0);
    require(db.get_pre_kyc_bonus_numerator() > 0);
    require(db.get_pre_kyc_bonus_denominator() > 0);
    db.set_arbits_presale_open(true);
}

// arbit specific sale settings