contract Production is Child {

    uint32 constant STUFF_IDX_POINT = 22001;
    uint32 constant STUFF_IDX_GENRAL = 21000;
    uint32 constant STUFF_IDX_EXTRA = 21004;

    uint32 constant PERMISSION_1 = 23002;
    uint32 constant PERMISSION_2 = 23003;

    uint constant BASERATIO = 10000;
    uint constant BOILER_FREE_IDX = 0;
    uint constant BOILER_MAX_NUM = 3;

    uint constant CREATE_COST_MIN = 30;
    uint constant CREATE_COST_FEW = 60;
    uint constant CREATE_COST_MANY = 150;
    uint constant CREATE_COST_MAX = 300;

    uint constant COOLTIME_MIN = 60 * 60;
    uint constant COOLTIME_MID = 60 * 60 * 2;
    uint constant COOLTIME_MAX = 60 * 60 * 4;

    uint constant ACCELERATE_UNITTIME = 60 * 5;
    uint constant ACCELERATE_UNITFEE = 0.0013 ether;
    uint constant BONUS_PERCENT_ACCELERATE = 80;

    ProductionBoiler g_Boilers;

    constructor(Main main, ProductionBoiler pb) public Child(main)
    {
        g_Boilers = pb;
        g_Boilers.SetAuth(this);
    }

    function kill() external MasterAble
    {
        g_Boilers.ClearAuth(this);
    }

    function GenChipIndex(
        uint seed,
        uint8 level,
        uint[] extWeight3,
        uint[] extWeight4,
        uint[] extWeight5
    ) internal returns(uint32,uint)
    {
        uint random;
        (random,seed) = GenRandom(seed,0);
        if (level==1 || level==2) {
            return (g_Main.GenChipByRandomWeight(random,level,extWeight3),seed);
        }
        else if (level==3 || level==4) {
            return (g_Main.GenChipByRandomWeight(random,level,extWeight4),seed);
        }
        else {
            return (g_Main.GenChipByRandomWeight(random,level,extWeight5),seed);
        }
    }

    function GenChipLevel_Special(uint costAll, uint x, uint seed) internal view returns(uint8,uint)
    {
        uint8 outLv;
        uint random;
        if (costAll <= CREATE_COST_FEW) {
            outLv = 3;
        }
        else {
            (random,seed) = GenRandom(seed,BASERATIO);
            uint baseR = BASERATIO*x/100;
            if (costAll <= CREATE_COST_MANY) {
                baseR /= 10;
                if (random <= BASERATIO*80/100-baseR) {
                    outLv = 3;
                }
                else if (random <= BASERATIO-baseR/4) {
                    outLv = 4;
                }
                else {
                    outLv = 5;
                }
            }
            else {
                baseR /= 10;
                if (random <= BASERATIO*70/100-baseR) {
                    outLv = 3;
                }
                else if (random <= BASERATIO*95/100-baseR/5) {
                    outLv = 4;
                }
                else {
                    outLv = 5;
                }
            }
        }
        return (outLv,seed);
    }

    function GenChipLevel_Extra(uint costAll, uint x, uint seed) internal view returns(uint8,uint)
    {
        uint8 outLv;
        uint random;
        uint baseR = BASERATIO*x/100;
        (random,seed) = GenRandom(seed,BASERATIO);
        if (costAll <= CREATE_COST_FEW) {
            baseR /= 4;
            if (random <= BASERATIO*80/100-baseR) {
                outLv = 1;
            }
            else if (random <= BASERATIO*98/100-baseR*3/4) {
                outLv = 2;
            }
            else if (random <= BASERATIO-baseR/4) {
                outLv = 3;
            }
            else {
                outLv = 4;
            }
        }
        else if (costAll <= CREATE_COST_MANY) {
            baseR /= 10;
            if (random <= BASERATIO*55/100-baseR) {
                outLv = 1;
            }
            else if (random <= BASERATIO*85/100-baseR*4/5) {
                outLv = 2;
            }
            else if (random <= BASERATIO*95/100-baseR*2/5) {
                outLv = 3;
            }
            else if (random <= BASERATIO-baseR/5) {
                outLv = 4;
            }
            else {
                outLv = 5;
            }
        }
        else {
            baseR /= 10;
            if (random <= BASERATIO*30/100-baseR/2) {
                outLv = 1;
            }
            else if (random <= BASERATIO*75/100-baseR) {
                outLv = 2;
            }
            else if (random <= BASERATIO*88/100-baseR*4/7) {
                outLv = 3;
            }
            else if (random <= BASERATIO*97/100-baseR/7) {
                outLv = 4;
            }
            else {
                outLv = 5;
            }
        }
        return (outLv,seed);
    }

    function GenChipLevel_General(uint costAll, uint x, uint seed) internal view returns(uint8,uint)
    {
        uint8 outLv;
        uint random;
        uint baseR = BASERATIO*x/100;
        (random,seed) = GenRandom(seed,BASERATIO);
        if (costAll <= CREATE_COST_FEW) {
            baseR /= 2;
            if (random <= BASERATIO - baseR) {
                outLv = 1;
            }
            else {
                outLv = 2;
            }
        }
        else if (costAll <= CREATE_COST_MANY) {
            baseR = baseR*14/100;
            if (random <= BASERATIO*70/100-baseR) {
                outLv = 1;
            }
            else if (random <= BASERATIO*95/100-baseR/4) {
                outLv = 2;
            }
            else {
                outLv = 3;
            }
        }
        else {
            baseR = baseR*11/100;
            if (random <= BASERATIO*50/100-baseR) {
                outLv = 1;
            }
            else if (random <= BASERATIO*90/100-baseR/3) {
                outLv = 2;
            }
            else {
                outLv = 3;
            }
        }
        return (outLv,seed);
    }

    function GenOutChipsNum(uint seed, uint costAll, uint x) internal view returns(uint,uint)
    {
        uint amount;
        uint random;
        uint baseR = BASERATIO*x/100;
        (random,seed) = GenRandom(seed,BASERATIO);
        if (costAll <= CREATE_COST_FEW) {
            if (random <= BASERATIO - baseR) {
                amount = 3;
            }
            else {
                amount = 4;
            }
        }
        else {
            baseR /= 10;
            if (costAll <= CREATE_COST_MANY) {
                if (random <= BASERATIO*7/10 - baseR*2) {
                    amount = 3;
                }
                else if (random <= BASERATIO*3/10 + baseR) {
                    amount = 4;
                }
                else {
                    amount = 5;
                }
            }
            else {
                if (random <= BASERATIO*7/10 - baseR) {
                    amount = 4;
                }
                else {
                    amount = 5;
                }
            }
        }
        return (amount,seed);
    }

    function GetMinCost(uint a, uint b, uint c) internal pure returns(uint)
    {
        if (a>b) {
            if (a>c) return a;
        }
        else if (b>c) return b;
        else return c;
    }

    function GenExtWeightList(uint costA, uint costB, uint costC) internal pure returns(uint[],uint[],uint[])
    {
        uint min = GetMinCost(costA,costB,costC);
        uint[] memory extWeight3 = new uint[](3);
        uint[] memory extWeight4 = new uint[](4);
        uint[] memory extWeight5 = new uint[](5);
        extWeight3[0] = costA;
        extWeight4[0] = costA;
        extWeight5[0] = costA;
        extWeight3[1] = costB;
        extWeight4[1] = costB;
        extWeight5[1] = costB;
        extWeight3[2] = costC;
        extWeight4[2] = costC;
        extWeight5[2] = costC;
        extWeight5[3] = min;
        min = min/2;
        extWeight4[3] = min;
        extWeight5[4] = min;
        return (extWeight3,extWeight4,extWeight5);
    }

    function GenChipsLevel(uint costAll,bool bUseX) internal view returns(uint8[] lvList, uint seed)
    {
        // calculate amount, chips by random
        uint x = costAll - CREATE_COST_MIN;
        uint i;
        uint amount;
        // cal chips amount
        (amount,seed) = GenOutChipsNum(0,costAll,x);
        lvList = new uint8[](amount);
        if (bUseX) {
            (lvList[0], seed) = GenChipLevel_Special(costAll,x,seed);
            for (i=1; i<amount; i++)
            {
                (lvList[i], seed) = GenChipLevel_Extra(costAll,x,seed);
            }
        }
        else {
            for (i=0; i<amount; i++)
            {
                (lvList[i], seed) = GenChipLevel_General(costAll,x,seed);
            }
        }

    }

    function CreateChips(uint costAll, uint costA, uint costB, uint costC, bool bUseX) internal returns(uint32[])
    {
        (uint[] memory ext3,
         uint[] memory ext4,
         uint[] memory ext5
        ) = GenExtWeightList(costA,costB,costC);

        (uint8[] memory lvList, uint seed) = GenChipsLevel(costAll,bUseX);
        uint32[] memory chips = new uint32[](lvList.length);
        for (uint i=0; i<lvList.length; i++)
        {
            uint8 chipLv = lvList[i];
            (chips[i], seed) = GenChipIndex(seed,chipLv,ext3,ext4,ext5);
        }
        return chips;
    }

    function GetPermissionIdx(uint idx) internal pure returns(uint32)
    {
        if (idx == 1) return PERMISSION_1;
        else if (idx == 2) return PERMISSION_2;
        return 0;
    }

    function IsBoilerValid(uint idx) internal view returns(bool)
    {
        if (idx != BOILER_FREE_IDX) {
            uint32 iStuff = GetPermissionIdx(idx);
            if (iStuff == 0) return false;
            if (g_Main.GetTempStuffExpire(msg.sender,iStuff) < now) return false;
        }
        return g_Boilers.IsBoilerValid(msg.sender,idx);
    }

    function CollectChips(uint idx) internal
    {
        uint32[] memory chips = g_Boilers.CollectChips(msg.sender,idx);
        for (uint i=0; i<chips.length; i++)
        {
            g_Main.GainChip(msg.sender,chips[i],true);
        }
    }

    function GetExchangePoint(uint8 chipLv) internal pure returns(uint)
    {
        if (chipLv == 1) return 1;
        else if (chipLv == 2) return 3;
        else if (chipLv == 3) return 10;
        else if (chipLv == 4) return 30;
        else if (chipLv == 5) return 120;
        return 0;
    }

    //=========================================================================

    function Create(uint idx,uint costA, uint costB, uint costC, bool bUseX) external
    {
        require(costA <= CREATE_COST_MAX);
        require(costB <= CREATE_COST_MAX);
        require(costC <= CREATE_COST_MAX);
        uint costAll = costA+costB+costC;
        require(costAll>=CREATE_COST_MIN && costAll<=CREATE_COST_MAX);

        require(IsBoilerValid(idx));

        g_Main.CostStuff(msg.sender,STUFF_IDX_GENRAL,costAll);
        if (bUseX) g_Main.CostStuff(msg.sender,STUFF_IDX_EXTRA,1);
        uint CD;
        if (costAll <= CREATE_COST_FEW) {
            CD = COOLTIME_MIN;
        }
        else if (costAll <= CREATE_COST_MANY) {
            CD = COOLTIME_MID;
        }
        else {
            CD = COOLTIME_MAX;
        }

        uint32[] memory chips = CreateChips(costAll,costA,costB,costC,bUseX);
        g_Boilers.GenerateChips(msg.sender,idx,CD,chips);
    }

    function GetBoilersInfo() external view returns(uint[], uint32[], uint32[], uint32[] )
    {
        uint[] memory expireList = new uint[](BOILER_MAX_NUM);
        uint32[][] memory allChips = new uint32[][](BOILER_MAX_NUM);
        for (uint i=BOILER_FREE_IDX; i<BOILER_MAX_NUM; i++)
        {
            (uint expire, uint32[] memory chips) = g_Boilers.GetBoilerInfo(msg.sender,i);
            expireList[i] = expire;
            allChips[i] = chips;
        }
        return (
            expireList,
            allChips[0],
            allChips[1],
            allChips[2]
        );
    }

    function ResolveChips(uint32[] chips) external
    {
        for (uint i=0; i<chips.length; i++)
        {
            uint32 iChip = chips[i];
            g_Main.CostChip(msg.sender,iChip);
            (,uint8 lv,,,,) = g_Main.GetChipInfo(iChip);
            uint point = GetExchangePoint(lv);
            g_Main.GainStuff(msg.sender,STUFF_IDX_POINT,point);
        }
    }

    function Collect(uint idx) external
    {
        require(g_Boilers.IsBoilerExpire(msg.sender,idx));
        CollectChips(idx);
    }

    function Accelerate(uint idx) external payable
    {
        (uint expire,) = g_Boilers.GetBoilerInfo(msg.sender,idx);
        require(expire > now);
        uint remain = expire-now;
        uint num = remain/ACCELERATE_UNITTIME;
        if (remain != num*ACCELERATE_UNITTIME) {
            num++;
        }
        uint fee = num*ACCELERATE_UNITFEE;
        require(fee == msg.value);
        AddBonus(BONUS_PERCENT_ACCELERATE);
        CollectChips(idx);
    }

}
function Create(uint idx,uint costA, uint costB, uint costC, bool bUseX) external
{
    require(costA <= CREATE_COST_MAX);
    require(costB <= CREATE_COST_MAX);
    require(costC <= CREATE_COST_MAX);
    uint costAll = costA+costB+costC;
    require(costAll>=CREATE_COST_MIN && costAll<=CREATE_COST_MAX);

    require(IsBoilerValid(idx));

    g_Main.CostStuff(msg.sender,STUFF_IDX_GENRAL,costAll);
    if (bUseX) g_Main.CostStuff(msg.sender,STUFF_IDX_EXTRA,1);
    uint CD;
    if (costAll <= CREATE_COST_FEW) {
        CD = COOLTIME_MIN;
    }
    else if (costAll <= CREATE_COST_MANY) {
        CD = COOLTIME_MID;
    }
    else {
        CD = COOLTIME_MAX;
    }

    uint32[] memory chips = CreateChips(costAll,costA,costB,costC,bUseX);
    g_Boilers.GenerateChips(msg.sender,idx,CD,chips);
}

//=========================================================================