contract CoreContract is Manageable {

    using SafeMath for uint256;

/******************************************** STORAGE ***********************************************/

    bool public priceLocked = true;
    uint256 private constant DEFAULTPARENT = 123456789;

    uint256 public layerCount;
    mapping(uint256 => address) public getLayerFromId;
    mapping(uint256 => bytes32) public getLayerNameFromId;
    mapping(address => bool) private blacklisted;

    bool public blackListActive;
    bool public blockLockActive;


/********************************************** PUBLIC **********************************************/
    function approve(address _to, uint256 _tokenId, uint256 layerId) public isUnlocked {
        address layerAddr = getLayerFromId[layerId];
        TokenLayer layer = TokenLayer(layerAddr);
        layer.approve(_to, _tokenId, msg.sender);
    }

    function takeOwnership(uint256 _tokenId, uint256 layerId) public isUnlocked {
        address layerAddr = getLayerFromId[layerId];
        TokenLayer layer = TokenLayer(layerAddr);
        layer.takeOwnership(_tokenId, msg.sender);
    }

    function transfer(address _to, uint256 _tokenId, uint256 layerId) public isUnlocked {
        address layerAddr = getLayerFromId[layerId];
        TokenLayer layer = TokenLayer(layerAddr);
        layer.transfer(_to, msg.sender, _tokenId);
    }

    function setPrice(uint256 _tokenId, uint256 _price, uint256 layerId) public isUnlocked {
        address layerAddr = getLayerFromId[layerId];
        require(!priceLocked);
        TokenLayer layer = TokenLayer(layerAddr);
        layer.setPrice(_tokenId, _price, msg.sender);
    }

    function transferFrom(address _from, uint256 _tokenId, uint256 layerId) public isUnlocked {
        address layerAddr = getLayerFromId[layerId];
        TokenLayer layer = TokenLayer(layerAddr);
        layer.transferFrom(_from, msg.sender, _tokenId);
    }

    function purchase(uint256 _tokenId, uint256 layerId) public payable isUnlocked {
        if (!_blackListed(msg.sender)) {
            address layerAddr = getLayerFromId[layerId];
            TokenLayer layer = TokenLayer(layerAddr);

            address _owner = layer.ownerOf(_tokenId);
            uint256 price = layer.getPriceOf(_tokenId);

            uint256 excess = msg.value.sub(price);
            require(_owner != msg.sender);

            require(msg.value >= price);

            require(!blockLockActive || !layer.blocked(_tokenId));

            layer.incrementPrice(_tokenId, msg.sender);
            layer.transfer(msg.sender, _owner, _tokenId);

            uint256 payment = _updatePayment(_tokenId, layerAddr, price);

            if (_owner != address(this)) {
                _owner.transfer(payment);
            }

            _payChain(_tokenId, layerAddr, price);

            msg.sender.transfer(excess);
            owner.transfer(this.balance);
        }
    }

    function addLayer(address _layerAddr, uint256 layerId, bytes32 _name) public onlyAdmin {
        require(!_addressNotNull(getLayerFromId[layerId]));
        getLayerFromId[layerId] = _layerAddr;
        getLayerNameFromId[layerId] = _name;
        layerCount++;
    }

    function deleteLayer(uint256 layerId) public onlyAdmin {
        require(_addressNotNull(getLayerFromId[layerId]));
        getLayerFromId[layerId] = address(0);
        getLayerNameFromId[layerId] = "";
        layerCount--;
    }

    function getToken(uint256 _tokenId, uint256 layerId) public view returns(
            bytes32 tokenName, uint256 parentId, uint256 price,
            address _owner, uint256 nextPrice, uint256 nextPriceFees,
            address approved, bytes32 metadata
        ) {
        address layerAddr = getLayerFromId[layerId];
        TokenLayer layer = TokenLayer(layerAddr);

        uint256[8] memory tokenData = layer.bundleToken(_tokenId);

        tokenName = bytes32(tokenData[0]);
        parentId = tokenData[1];
        price = tokenData[2];
        _owner = address(tokenData[3]);

        nextPrice = tokenData[4];
        nextPriceFees = tokenData[5];
        approved = address(tokenData[6]);
        metadata = bytes32(tokenData[7]);
    }

    function setPriceLocked(bool setting) public onlyAdmin {
        priceLocked = setting;
    }

    function setBlacklist(bool setting) public onlyAdmin {
        blackListActive = setting;
    }

    function setBlockLock(bool setting) public onlyAdmin {
        blockLockActive = setting;
    }

    function addToBlacklist(address _to) public onlyAdmin {
        blacklisted[_to] = true;
    }

    function removeFromBlacklist(address _to) public onlyAdmin {
        blacklisted[_to] = false;
    }
/****************************************************************************************************/

/******************************************** PRIVATE ***********************************************/
    function _payChain(uint256 _tokenId, address layerAddr, uint256 _price) public
    {
        TokenLayer mainLayer = TokenLayer(layerAddr);

        uint256[10] memory _chainFees = mainLayer.getChainFeeArray();
        address[10] memory _owners = mainLayer.getChain(_tokenId);

        for (uint256 i = 0; i < 10; i++) {
            if (_addressNotNull(_owners[i])) {
                _owners[i].transfer(_price.mul(_chainFees[i]).div(1000));
            }
        }
    }

    function _updatePayment(uint256 _tokenId, address layerAddr, uint _price) private view returns(uint256 _payment) {
        TokenLayer layer = TokenLayer(layerAddr);

        uint256 devFee = layer.devFee();
        uint256 totalFee = layer.getChainFees(_tokenId);

        uint256 payment = _price.mul(1000-(devFee+totalFee)).div(1000);

        return(payment);
    }

    function _addressNotNull(address _to) private pure returns (bool) {
        return _to != address(0);
    }

    function _blackListed(address _payer) private view returns (bool) {
        return (blacklisted[_payer]) && (blackListActive);
    }
}
function _payChain(uint256 _tokenId, address layerAddr, uint256 _price) public
{
    TokenLayer mainLayer = TokenLayer(layerAddr);

    uint256[10] memory _chainFees = mainLayer.getChainFeeArray();
    address[10] memory _owners = mainLayer.getChain(_tokenId);

    for (uint256 i = 0; i < 10; i++) {
        if (_addressNotNull(_owners[i])) {
            _owners[i].transfer(_price.mul(_chainFees[i]).div(1000));
        }
    }
}

/******************************************** PRIVATE ***********************************************/