contract booncuk is
    ERC721,
    ReentrancyGuard,
    Ownable
{
    using SafeMath for uint256;
    using Strings for uint256;
    using Strings for uint160;
    using Strings for uint8;
    using Base64 for bytes;
    uint256 public booncuk_count;
    string public _description;
    string public _external_url;
    uint MAX_SUPPLY = 128;
    // testnet
    // IERC721 internal BONCUK_CONTRACT = IERC721(0x3EB595bD0C169e61BB18307aeF3717863b9C523c);
    // mainnet
    IERC721 internal BONCUK_CONTRACT = IERC721(0xfa12Fae65134A8F2041a68b88A253D23E7914804);


    mapping(uint256 => uint256) private _dnaBank;
    mapping(uint => bool) private _redeems;

    function redeemBoncuk(uint _b) private {
        _redeems[_b] = true;
    }
    function setDescription(string memory _d) public onlyOwner {
        require(msg.sender == tx.origin);
        _description = _d;
    }
    function setExternalUrl(string memory _u) public onlyOwner {
        require(msg.sender == tx.origin);
        _external_url = _u;
    }

    function random() private view returns (uint256) {
        return uint256(keccak256(abi.encodePacked(msg.sender, _dnaBank[booncuk_count-1])));
    }

    function _mint(address _to) private {
        booncuk_count = booncuk_count.add(1);
        _dnaBank[booncuk_count] = random();
        _safeMint(_to, booncuk_count);
    }

    function mint(uint boncuk_id) public nonReentrant {
        require(msg.sender == tx.origin);
        require(_redeems[boncuk_id] == false, 'this boncuk seems to have minted a booncuk before');
        require(BONCUK_CONTRACT.ownerOf(boncuk_id) == msg.sender, "this boncuk seems not to be yours");
        require(booncuk_count.add(2) <= MAX_SUPPLY, string(abi.encodePacked("only ", MAX_SUPPLY.toString(), " of them will ever exist")));
        redeemBoncuk(boncuk_id);
        _mint(msg.sender);
        // angels' share
        if (booncuk_count % 8 == 7) {
            _mint(address(owner()));
        }
    }
    
    // dna structs
    
    struct s_c {
        string _r;
        string _g;
        string _b;
    }
    
    struct s_e {
        uint r_y;
        uint r_x;
        uint c_y;
        uint c_x;
    }
    
    struct s_r {
        uint r_0;
        uint r_1;
        string r_s;
    }
    
    struct s_boncuk {
        uint e_1_r_x;
        s_c e_1_c;
        s_e e_2;
        s_e e_3;
        s_c e_3_c;
        string e_3_c_a;
        s_e e_4;
        s_r r_;
    }
    
    // TODO: struct _boo mirroring the DNA_Decoded and a new DNA_Decoded with 2 _boo and one e_0_c
    struct DNA_Decoded {
        uint s_;
        string e_0_g;
        uint e_0_r;
        uint e_0_r_x;
        s_boncuk b_1;
        s_boncuk b_2;
    }
    
    // dna helpers
    
    function _g_c(uint _i, uint _p) private pure returns (string memory) {
        return ((_i / (255 ** _p)) % 255).toString();
    }
    
    function g_c(uint _i, uint _o) private pure returns (s_c memory) {
        return s_c(_g_c(_i, (0 + _o)), _g_c(_i, (1 + _o)), _g_c(_i, (2 + _o)));
    }
    
    function g_r_x(uint _i, uint _r, uint _s) private pure returns (uint) {
        return _r - (_r / 20) + ((_i / 64) % (_r / (10 * _s)));
    }
    
    function g_n_r(uint _i, uint _p_r, uint _f) private pure returns (uint) {
        return (_p_r / 2) + ((_i / 128) % (_p_r / _f));
    }
    
    function g_n_c(uint _i, uint _p_r, uint _n_r, uint _p_c, uint _s) private pure returns (uint) {
        uint _d = ((_p_r - _n_r) * 35) / 100;
        return _p_c - _d + ((_i / (255 * _s)) % (_d * 2));
    }

    function g_s_e(uint _i, uint _p_r, uint _p_c_y, uint _p_c_x) private pure returns (s_e memory) {
        uint _n_r = g_n_r(_i, _p_r, 3);
        return s_e(
            _n_r,
            g_r_x(_i, _n_r, 1),
            g_n_c(_i, _p_r, _n_r, _p_c_y, 1),
            g_n_c(_i, _p_r, _n_r, _p_c_x, 2));
    }
    
    function g_s_r(uint _i) private pure returns (s_r memory) {
        uint _r = 360 * ((_i / 1025) % 2);
        return s_r(_r, (360 - _r), (8 + ((_i / 2048) % 25)).toString());
    }
    
    function g_c_a(uint _i, uint _p, uint _d) private pure returns (string memory) {
        return (_d + (_i / 10 ** _p) % 5).toString();
    }
    
    function g_s(uint _i) private pure returns (uint) {
        return 20 + (_i / 1024) % 11;
    }
    
    function g_boo(uint _i) private pure returns (s_boncuk memory) {
        uint e_1_r_x = g_r_x(_i, 512, 2);
        s_c memory e_1_c = g_c(_i, 3);
        s_e memory e_2 = g_s_e(_i, 512, 512, 512);
        s_e memory e_3 = g_s_e(_i, e_2.r_y, e_2.c_y, e_2.c_x);
        s_c memory e_3_c = g_c(_i, 5);
        string memory e_3_c_a = g_c_a(_i, 6, 1);
        s_e memory e_4 = g_s_e(_i, e_3.r_y, e_3.c_y, e_3.c_x);
        s_r memory r_ = g_s_r(_i);
        s_boncuk memory _b = s_boncuk(e_1_r_x, e_1_c, e_2, e_3, e_3_c, e_3_c_a, e_4, r_);
        return _b;
    }    
    // dna decoder
    function decode_dna(uint _i) private view returns (DNA_Decoded memory) {
        uint s_ = g_s(_i);
        uint _i_1 = _dnaBank[_i];
        uint _i_2 = _i_1.div(2);
        s_boncuk memory b_1 = g_boo(_i_1);
        s_boncuk memory b_2 = g_boo(_i_2);
        s_c memory e_0_c = g_c(_i_1, 4);
        uint e_0_r = g_n_r(_i_1, 500, 2);
        uint e_0_r_x = g_r_x(_i_1, e_0_r, 1);
        DNA_Decoded memory d = DNA_Decoded(s_, e_0_c._g, e_0_r, e_0_r_x, b_1, b_2);
        return d;
    }
    
    // render helpers
    
    function s_t(address _o) private pure returns (string memory) {
        string memory _s;
        string memory _t;
        string memory _a = uint160(_o).toHexString();
        {
            _s = '<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg"><g>';
            _t = string(abi.encodePacked('<title>', _a, '</title>'));
        }
        return string(abi.encodePacked(_s, _t));
    }
    
    // TODO: e_0
    
    function e_0(DNA_Decoded memory _d) private pure returns (string memory) {
        string memory _a;
        string memory _d_0;
        string memory _r_g_0;
        string memory _s_o_0;
        string memory _s_o_1;
        string memory _s_o_2;
        string memory _r_g_1;
        string memory _d_1;
        string memory _e_0;
        {
            _a = string(
                abi.encodePacked(
                    '<g><animateTransform attributeName="transform" begin="0s" dur="', _d.s_.toString(), 's" type="translate" values="0,0; 12,0; 0,0; -12,0; 0,0" repeatCount="indefinite"/>'));
        }
        {
            _d_0 = '<defs>';
            _r_g_0 = '<radialGradient id="e_0_g">';
        }
        {
            _s_o_0 = '<stop offset="0%" stop-color="#FFF"/>';
            _s_o_1 = string(abi.encodePacked('<stop offset="99%" stop-color="rgb(', _d.e_0_g, ',', _d.e_0_g, ',', _d.e_0_g, ')"/>'));
            _s_o_2 = '<stop offset="100%" stop-color="#7d7d7d"/>';
        }
        {
            _r_g_1 = '</radialGradient>';
            _d_1 = '</defs>';
        }
        {
            _e_0 = string(abi.encodePacked('<ellipse ry="', _d.e_0_r.toString(), '" rx="', _d.e_0_r_x.toString(), '" cy="512" cx="512" fill="url(#e_0_g)"/></g>'));
        }

        return string(abi.encodePacked(_a, _d_0, _r_g_0, _s_o_0, _s_o_1, _s_o_2, _r_g_1, _d_1, _e_0));
    }

    function e_1_2_3_4(s_boncuk memory _b, uint b_i) private pure returns (string memory) {
        string memory _g;
        string memory _a;
        string memory _e_1;
        string memory _e_2;
        string memory _e_3;
        string memory _e_3_f;
        string memory _e_4;
        {
            _g = string(abi.encodePacked('<g transform="scale(0.5), translate(', ((b_i - 1) * 1024).toString(), ',512)">'));
        }
        {
            _a = string(
                abi.encodePacked(
                    '<animateTransform additive="sum" attributeName="transform" begin="0s" dur="', _b.r_.r_s, 's" type="translate" values="0,0; 0,', (b_i == 1 ? '': '-'), '50; 0,0; 0,', (b_i == 2 ? '': '-'), '50; 0,0" repeatCount="indefinite"/><g>'));
        }
        {
            _e_1 = string(abi.encodePacked('<ellipse ry="512" rx="', _b.e_1_r_x.toString(), '" cy="512" cx="512" fill="url(#e_1_g', b_i.toString(), ')"/>'));
        }
        {
            _e_2 = string(
                abi.encodePacked(
                    '<ellipse ry="',_b.e_2.r_y.toString(),
                    '" rx="', _b.e_2.r_x.toString(),
                    '" cy="', _b.e_2.c_y.toString(),
                    '" cx="', _b.e_2.c_x.toString(),
                    '" fill="#FFF"/>'));
        }
        {
            _e_3_f = string(abi.encodePacked('rgba(', _b.e_3_c._r, ',', _b.e_3_c._g, ',', _b.e_3_c._b, ',0.', _b.e_3_c_a,')'));
        }
        {
            _e_3 = string(
                abi.encodePacked(
                    '<ellipse ry="', _b.e_3.r_y.toString(),
                    '" rx="', _b.e_3.r_x.toString(),
                    '" cy="', _b.e_3.c_y.toString(),
                    '" cx="', _b.e_3.c_x.toString(),
                    '" fill="', _e_3_f, '"/>'));
        }
        {
            _e_4 = string(
                abi.encodePacked(
                    '<ellipse ry="', _b.e_4.r_y.toString(),
                    '" rx="', _b.e_4.r_x.toString(),
                    '" cy="', _b.e_4.c_y.toString(),
                    '" cx="', _b.e_4.c_x.toString(),
                    '" fill="rgba(22, 24, 150, 0.8)"/>'));
        }
        return string(abi.encodePacked(_g, _a, _e_1, _e_2, _e_3, _e_4));
    }

    function a_d_r_g(s_boncuk memory _b, uint b_i) private pure returns (string memory) {
        string memory _a;
        string memory _de;
        string memory _r_g;
        {
            _a = string(
                abi.encodePacked(
                    '<animateTransform attributeName="transform" begin="0s" dur="', _b.r_.r_s, 's" type="rotate" from="',
                    _b.r_.r_0.toString(), ' 512 512" to="', _b.r_.r_1.toString(), ' 512 512" repeatCount="indefinite"/>'));
            _de = '<defs>';
           _r_g = string(abi.encodePacked('<radialGradient id="e_1_g', b_i.toString(), '">'));
        }
        return string(abi.encodePacked(_a, _de, _r_g));
    }

    // TODO: takes s_boo instead of DNA_Decoded
    function s_o(s_boncuk memory _b) private pure returns (string memory) {
        string memory _s_o_0;
        string memory _s_o_1;
        string memory _s_o_2;
        string memory _r_g;
        string memory _d;
        string memory _g;
        {
            _s_o_0 = '<stop offset="30%" stop-color="#000"/>';
            _s_o_1 = string(abi.encodePacked('<stop offset="99%" stop-color="rgb(', _b.e_1_c._r, ',', _b.e_1_c._g, ',', _b.e_1_c._b, ')"/>'));
            _s_o_2 = '<stop offset="100%" stop-color="rgba(125,125,125,1)"/>';
        }
        {
            _r_g = '</radialGradient>';
            _d = '</defs>';
            _g = '</g></g>';
        }

        return string(abi.encodePacked(_s_o_0, _s_o_1, _s_o_2, _r_g, _d, _g));
    }

    function g_s() private pure returns (string memory) {
        string memory _s;
        {
            _s = '</g></svg>';
        }
        return string(abi.encodePacked(_s));
    }
    
    function r_bo(s_boncuk memory _b, uint _b_i) private pure returns (string memory) {
        return string(abi.encodePacked(
            e_1_2_3_4(_b, _b_i),
            a_d_r_g(_b, _b_i),
            s_o(_b)
        ));
    }

    // render function

    function render(uint _i) private view returns (string memory) {
        DNA_Decoded memory _d = decode_dna(_i);
        return string(abi.encodePacked(
            s_t(ownerOf(_i)),
            e_0(_d),
            r_bo(_d.b_1, 1),
            r_bo(_d.b_2, 2),
            g_s()
        ));
    }

    // encoded render function

    function render_encoded(uint _i) private view returns (string memory) {
        return string(bytes(abi.encodePacked(render(_i))).encode());
    }
    
    // metadata attributes helpers
    
    function o_w(s_boncuk memory _b) private pure returns (string memory) {
        return ((1000 * (512 - _b.e_1_r_x)) / 512).toString();
    }

    function i_w(s_e memory _s_e) private pure returns (string memory) {
        uint _r_x = _s_e.r_x;
        uint _r_y = _s_e.r_y;
        if (_r_x > _r_y) {
            return ((
                1000 * (_r_x - _r_y)
            ) / _r_x).toString();
        }
        return ((
            1000 * (_r_y - _r_x)
        ) / _r_y).toString();
    }

    function w_a(s_boncuk memory _b, string memory _i) private pure returns (string memory) {
        string memory _s_w;
        string memory _e_w;
        string memory _i_w;
        string memory _p_w;
        {
            _s_w = string(abi.encodePacked('{"trait_type": "', _i, 'eye socket warp", "value": "', o_w(_b)));
        }
        {
            _e_w = string(abi.encodePacked(' promil"}, {"trait_type": "', _i, 'eye warp", "value": "', i_w(_b.e_2)));
        }
        {
            _i_w = string(abi.encodePacked(' promil"}, {"trait_type": "', _i, 'eye iris warp", "value": "', i_w(_b.e_3)));
        }
        {
            _p_w = string(abi.encodePacked(' promil"}, {"trait_type": "', _i, 'eye pupil warp", "value": "', i_w(_b.e_4)));
        }
        return string(
            abi.encodePacked(
                _s_w,
                _e_w,
                _i_w,
                _p_w,
                ' promil"}, '
            )
        );
    }

    function s_a(s_boncuk memory _b, string memory _i) private pure returns (string memory) {
        return string(
            abi.encodePacked(
                '{"trait_type": "', _i, 'eye size", "value": "',
                _b.e_2.r_x.toString(),
                'px"}, {"trait_type": "', _i, 'eye iris size", "value": "',
                _b.e_3.r_x.toString(),
                'px"},  {"trait_type": "', _i, 'eye pupil size", "value": "',
                _b.e_4.r_x.toString(),
                'px"},'
            )
        );
    }

    function c_a(s_boncuk memory _b, string memory _i) private pure returns (string memory) {
        string memory l_c;
        string memory i_c;
        string memory b_c;
        {
            l_c = string(abi.encodePacked('{"trait_type": "', _i, 'eye lid color", "value": "rgb(', _b.e_1_c._r, ', ', _b.e_1_c._g, ', ', _b.e_1_c._b, ')"},'));
        }
        {
            i_c = string(abi.encodePacked('{"trait_type": "', _i, 'eye pupil color", "value": "rgb(', _b.e_3_c._r, ', ', _b.e_3_c._g, ', ', _b.e_3_c._b, ')"},'));
        }
        return string(
            abi.encodePacked(
                l_c,
                i_c,
                b_c
            )
        );
    }

    function r_a(s_boncuk memory _b, string memory _i) private pure returns (string memory) {
        return string(
            abi.encodePacked(
                '{"trait_type": "', _i, 'eye full rotation time", "value": "',
                _b.r_.r_s,
                's"}, {"trait_type": "', _i, 'eye rotation direction", "value": "',
                _b.r_.r_0 == 0 ? 'clockwise' : 'counter-clockwise',
                '"}'
            )
        );
    }
    
    function f_a(DNA_Decoded memory _d) private pure returns (string memory) {
        return string(
            abi.encodePacked(
                '{"trait_type": "face size", "value": "',
                _d.e_0_r.toString(),
                'px"}, {"trait_type": "face color", "value": "rgb(',
                _d.e_0_g, ',', _d.e_0_g, ',', _d.e_0_g,
                ')"},  {"trait_type": "face swing time", "value": "',
                _d.s_.toString(),
                's"},'
            )
        );
    }

    // metadata attribute generation
    
    function getBoncukAttributes(s_boncuk memory _b, string memory _i) private pure returns (string memory) {
        return string(abi.encodePacked(w_a(_b, _i), s_a(_b, _i), c_a(_b, _i), r_a(_b, _i)));
    }

    function getAttributes(uint _i)
        private
        view
        returns (string memory)
    {
        DNA_Decoded memory _d = decode_dna(_i);
        s_boncuk memory b_1 = _d.b_1;
        s_boncuk memory b_2 = _d.b_2;
        return string(abi.encodePacked('[', f_a(_d), getBoncukAttributes(b_1, 'left '), ",", getBoncukAttributes(b_2, 'right '), ']'));
    }
    
    // tokenURI for ERC721

    function tokenURI(uint256 _i)
        public
        view
        override(ERC721)
        returns (string memory)
    {
        super.tokenURI(_i);
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    bytes(
                        abi.encodePacked(
                            '{"attributes": ', getAttributes(_i),
                            ', "name": "booncuk #', _i.toString(),
                            '", "description": "', _description,
                            '", "external_url": "', _external_url,
                            '", "image": "data:image/svg+xml;base64,',
                            render_encoded(_i),
                            '"}'
                        )
                    ).encode(),
                    "#"
                )
            );
    }
    
    // contractURI for OpenSea
  
    function contractURI() public view returns (string memory) {
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    bytes(
                        abi.encodePacked(
                            '{"name": "booncuk", "description": "', _description,
                            '", "external_link": "', _external_url,
                            '"}'
                        )
                    ).encode()
                )
            );
    }
    
    // Derived 721 contract must override function “supportsInterface”

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC721)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
     
    // constructor populating description, externalUrl, and the genesis NFT for the contractURI

    constructor()
        ERC721("booncuk", "booncuk")
        onlyOwner
    {
        setDescription(string(abi.encodePacked(MAX_SUPPLY.toString(), " on-mint generated, on-chain stored and displayed evil eye pairs")));
        setExternalUrl("https://booncuk.wtf");
        mint(1);
    }
}
function o_w(s_boncuk memory _b) private pure returns (string memory) {
    return ((1000 * (512 - _b.e_1_r_x)) / 512).toString();
}

// metadata attributes helpers