// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;


/* 
        BACKGROUNDS

    01: "rocknroll"
    02: "fibonacci"
    03: "clash"
    04: "river"
    05: "decide"
    06: "everlast"
    07: "vibe"
    08: "motion"
    09: "A village"
    10: "jump in"
    11: "walk"
    12: "underwater"
    13: "vision"
    14: "dreamy"
    15: "roundnround"
    16: "joanna"
    17: "dna1"
    18: "yes and no"
    19: "love"
    20: "wild"
    21: "seek"
    22: "foundations"
    23: "i will not"
    24: "feel"
    25: "flow"
    26: "landing"
    27: "japan"
    28: "teamwork"
    29: "Jelly"
    30: "joa1"
    31: "joa2"
    32: "tiger roar"
    33: "shadows"
    34: "catch"
    35: "passion"
    36: "cylces"
    37: "poetry"
    38: "lucky"
    39: "classical"
    40: "trust"
    41: "circles"
    42: "live"
    43: "around the world"
    44: "now is it"
    45: "wants"
    46: "h20"
    47: "rotations"
    48: "lust"
    49: "bloom"
    50: "desires"
    51: "eyes"
    52: "dna2"
    53: "triple"
    54: "virgin"
    55: "steal"
    56: "spiral"
    57: "monaco"
    58: "growl33"
    59: "bigmac"
    60: "loyalty"
    61: "jazz"

        BODIES

    01: "film"
    02: "juice"
    03: "power"
    04: "trip"
    05: "smooth"
    06: "electric"
    07: "scarecrow"
    08: "boring"
    09: "stinna"
    10: "whawha"
    11: "shimmer"
    12: "space"
    13: "gold"
    14: "rock out"
    15: "style"
    16: "blueberry"
    17: "bronze"
    18: "chalk"
    19: "positivity"
    20: "orange juice"
    21: "favorite"
    22: "pompom"
    23: "fruity"
    24: "spacemen"
    25: "maroon"
    26: "black"
    27: "poem"
    28: "reckless"
    29: "solve"
    30: "victory"
    31: "launch"
    32: "greenboy"
    33: "flowers"
    34: "spacegirl"
    35: "chance"
    36: "raspberry"
    37: "again"
    38: "family"
    39: "round about"
    40: "goldandblue"
    41: "give"
    42: "take"
    43: "want"
    44: "taste"
    45: "lebron"
    46: "pinnochio"
    47: "celtic"
    48: "stay"
    49: "yellow"
    50: "impossible"
    51: "goldy "
    52: "mission"
    53: "blackberry"
    54: "gorgy"
    55: "gardens"
    56: "recent"
    57: "loop"
    58: "emerald"
    59: "teach"
    60: "home"
    61: "new"
    62: "rivers"

        EARS

    01: "ruby"
    02: "on"
    03: "turquoise"
    04: "hotpink"
    05: "blueboy"
    06: "tight"
    07: "squeeze"
    08: "send"
    09: "trips"
    10: "lemonade"
    11: "solve it"
    12: "balls"
    13: "floppy"
    14: "relax"
    15: "exits"
    16: "lust"
    17: "Love"
    18: "weak"
    19: "ask"
    20: "jingle"
    21: "vox"
    22: "dizzy"
    23: "black"
    24: "sweep"
    25: "commit"
    26: "harvard"
    27: "ucla"
    28: "stanford"
    29: "boston"
    30: "please"
    31: "tricks"
    32: "treats"
    33: "play"
    34: "electric"
    35: "soul"
    36: "heart"
    37: "intense"
    38: "inject"
    39: "eject"
    40: "creamy"
    41: "drive"
    42: "fly"
    43: "cruise"
    44: "glide"
    45: "work"
    46: "wonder"
    47: "please"
    48: "visions"
    49: "fantsies"
    50: "molasses"
    
        VISORS
    
    01: "crush"
    02: "rubies"
    03: "emeralds"
    04: "sapphire"
    05: "wings"
    06: "midnight"
    07: "teeth"
    08: "matisse"
    09: "chrome"
    10: "spaceboy"
    11: "roll"
    12: "Sirrichard"
    13: "pinky"
    14: "rush"
    15: "onyx"
    16: "when"
    17: "treat"
    18: "marble"
    19: "reruns"
    20: "tiptoe"
    21: "meadow"
    22: "modesty"
    23: "smooth"
    24: "more"
    25: "fluff"
    26: "lightning"
    27: "prince"
    28: "windows"
    29: "girlz"
    30: "dexter"
    31: "blossom"
    32: "straight"
    33: "growling"
    34: "chalkboards"
*/


contract Generator {

    uint[] public backgroundWeights = [
        0.0161e18,
        0.0161e18,
        0.008e18,
        0.0161e18,
        0.0241e18,
        0.0161e18,
        0.0241e18,
        0.008e18,
        0.0161e18,
        0.008e18,
        0.0241e18,
        0.0241e18,
        0.008e18,
        0.0161e18,
        0.0241e18,
        0.0241e18,
        0.0161e18,
        0.0161e18,
        0.008e18,
        0.004e18,
        0.0241e18,
        0.0241e18,
        0.0161e18,
        0.008e18,
        0.0161e18,
        0.0161e18,
        0.0241e18,
        0.008e18,
        0.0161e18,
        0.0161e18,
        0.0241e18,
        0.008e18,
        0.008e18,
        0.0264e18,
        0.0241e18,
        0.008e18,
        0.0161e18,
        0.0241e18,
        0.0161e18,
        0.0161e18,
        0.0161e18,
        0.0161e18,
        0.0161e18,
        0.0161e18,
        0.0241e18,
        0.0241e18,
        0.008e18,
        0.0241e18,
        0.0241e18,
        0.0241e18,
        0.0002e18,
        0.0161e18,
        0.0241e18,
        0.004e18,
        0.0161e18,
        0.0241e18,
        0.0161e18,
        0.0161e18,
        0.0161e18,
        0.0002e18,
        0.0161e18
    ];
    uint public backgroundTotalWeight;

    uint[] public bodiesWeights = [
        0.0074e18,
        0.0222e18,
        0.0074e18,
        0.0222e18,
        0.0074e18,
        0.0222e18,
        0.0074e18,
        0.0074e18,
        0.0222e18,
        0.0222e18,
        0.0222e18,
        0.0074e18,
        0.0222e18,
        0.0074e18,
        0.0148e18,
        0.0222e18,
        0.0222e18,
        0.0222e18,
        0.0148e18,
        0.0222e18,
        0.0222e18,
        0.0148e18,
        0.0148e18,
        0.0222e18,
        0.0222e18,
        0.0037e18,
        0.0037e18,
        0.0222e18,
        0.0222e18,
        0.0222e18,
        0.0222e18,
        0.0222e18,
        0.0037e18,
        0.0222e18,
        0.0037e18,
        0.0074e18,
        0.0222e18,
        0.0222e18,
        0.0222e18,
        0.0148e18,
        0.0222e18,
        0.0148e18,
        0.0222e18,
        0.0222e18,
        0.0037e18,
        0.0222e18,
        0.0148e18,
        0.0148e18,
        0.0074e18,
        0.0037e18,
        0.0074e18,
        0.0222e18,
        0.0148e18,
        0.0222e18,
        0.0074e18,
        0.0222e18,
        0.0074e18,
        0.0222e18,
        0.0148e18,
        0.0074e18,
        0.0222e18,
        0.0222e18
    ];
    uint public bodiesTotalWeight;
    
    uint[] public earsWeights = [
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18,
        0.02e18
    ];
    uint public earsTotalWeight;

    uint[] public visorsWeights = [
        0.0072e18,
        0.0072e18,
        0.0144e18,
        0.0144e18,
        0.0072e18,
        0.0144e18,
        0.0144e18,
        0.0144e18,
        0.0144e18,
        0.0432e18,
        0.0432e18,
        0.0144e18,
        0.0144e18,
        0.0432e18,
        0.0432e18,
        0.0144e18,
        0.0144e18,
        0.0432e18,
        0.0144e18,
        0.0432e18,
        0.0719e18,
        0.0719e18,
        0.0072e18,
        0.0719e18,
        0.0432e18,
        0.0144e18,
        0.0432e18,
        0.0144e18,
        0.0432e18,
        0.0072e18,
        0.0432e18,
        0.0432e18,
        0.0432e18,
        0.0432e18
    ];
    uint public visorsTotalWeight;
    
    uint public constant LAST_NFT = 11111;
    uint public constant ONE_OF_ONE_COUNT = 25;

    uint[] public oneOfOne;
    
    uint public seedBlock;
    uint public seed;
    uint nonce;

    constructor() {
        
        seedBlock = block.number + 10;
        
        uint backgroundBuf;
        for(uint i = 0; i < backgroundWeights.length; i++) {
            backgroundBuf+= backgroundWeights[i];
        }

        backgroundTotalWeight = backgroundBuf;

        uint bodiesBuf;
        for(uint i = 0; i < bodiesWeights.length; i++) {
            bodiesBuf+= bodiesWeights[i];
        }

        bodiesTotalWeight = bodiesBuf;

        uint earsBuf;
        for(uint i = 0; i < earsWeights.length; i++) {
            earsBuf+= earsWeights[i];
        }

        earsTotalWeight = earsBuf;

        uint visorsBuf;
        for(uint i = 0; i < visorsWeights.length; i++) {
            visorsBuf+= visorsWeights[i];
        }

        visorsTotalWeight = visorsBuf;
    }

    function generateOneOfOne() public {
        require(blockhash(seedBlock) != bytes32(0), "too early or expired");
        require(oneOfOne.length == 0, "already generated");
        
        seed = uint(keccak256(abi.encodePacked(blockhash(seedBlock), tx.gasprice, block.timestamp)));

        for(uint i = 0; i < ONE_OF_ONE_COUNT; i++) {
            nonce++;
            uint _random = uint(keccak256(abi.encode(seed, nonce))) % LAST_NFT + 1;
            
            //exclude lost NFT 
            if(_random == 6127) {
                i--; 
                continue;
            }
            
            oneOfOne.push(_random);
        }

        // check for duplicates
        for(uint i = 0; i < ONE_OF_ONE_COUNT; i++) {
            uint item = oneOfOne[i];
            uint count = 0;
            for(uint j = 0; j < ONE_OF_ONE_COUNT; j++) {
                if(oneOfOne[j] == item) {
                    count++;
                }
                if(count > 1) {
                    //remove all array and try again
                    oneOfOne = new uint[](0);
                    return;
                }
            }
        }
    }

    function getNFTData(uint _tokenId) public view returns(uint[] memory res) {
        res = new uint[](5);
        
        for(uint i = 0; i < oneOfOne.length; i++) {
            if(_tokenId == oneOfOne[i]) {
                res[4] = i + 1;
                return res;
            }
        }
        //0 - background
        //1 - body
        //2 - ear
        //3 - visor
        //4 - oneOfOne
        
        res[0] = getRandomBackground(_tokenId) + 1;
        res[1] = getRandomBody(_tokenId) + 1;
        res[2] = getRandomEar(_tokenId) + 1;
        res[3] = getRandomVisor(_tokenId) + 1;
        res[4] = 0;
    }
    
    function getRandomBackground(uint _tokenId) public view returns(uint) {
        uint rnd = random(_tokenId, backgroundTotalWeight);

        for(uint i = 0; i < backgroundWeights.length; i++) {
          if(rnd < backgroundWeights[i]) {
            return i;
          }

          rnd -= backgroundWeights[i];
        }

        return 0;
    }

    function getRandomBody(uint _tokenId) public view returns(uint) {
        uint rnd = random(_tokenId, bodiesTotalWeight);

        for(uint i = 0; i < bodiesWeights.length; i++) {
          if(rnd < bodiesWeights[i]) {
            return i;
          }

          rnd -= bodiesWeights[i];
        }

        return 0;
    }

    function getRandomEar(uint _tokenId) public view returns(uint) {
        uint rnd = random(_tokenId, earsTotalWeight);

        for(uint i = 0; i < earsWeights.length; i++) {
          if(rnd < earsWeights[i]) {
            return i;
          }

          rnd -= earsWeights[i];
        }

        return 0;
    }

    function getRandomVisor(uint _tokenId) public view returns(uint) {
        uint rnd = random(_tokenId, visorsTotalWeight);

        for(uint i = 0; i < visorsWeights.length; i++) {
          if(rnd < visorsWeights[i]) {
            return i;
          }

          rnd -= visorsWeights[i];
        }

        return 0;
    }
    
    function random(uint _tokenId, uint _maximum) public view returns (uint) {
        return uint(keccak256(abi.encode(seed, _tokenId))) % _maximum;
    }

}