// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;
import '../storage/RendererPropsStorage.sol';
import '../storage/ChunkedDataStorage.sol';
import '../Merge.sol';
import '../MergeMana.sol';
import './RarityCompositingEngine.sol';
import '../libraries/DecimalUtils.sol';
import '../libraries/NftMetadataUtils.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
import '@openzeppelin/contracts/utils/Base64.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@abf-monorepo/protocol/contracts/renderers/LayerCompositeRenderer.sol';
import '@abf-monorepo/protocol/contracts/libraries/BytesUtils.sol';

contract LiveServiceRarityCompositingEngine is Ownable {
  using Strings for uint256;

  RarityCompositingEngine public rce;

  // image renderer contracts
  address public compositingRenderer;

  mapping(uint256 => address) visualLayersRenderers;
  uint256 public maxVisualLayersIndex;

  mapping(uint256 => uint256) visualLayerIndexRemap;

  // metadata renderer contracts
  mapping(uint256 => address) genericMetadataRenderers;
  uint256 public maxGenericRenderersIndex;

  mapping(uint256 => address) attributeMetadataRenderers;
  uint256 public maxAttributeRenderersIndex;

  constructor(address _rce, address _compositingRenderer) {
    rce = RarityCompositingEngine(_rce);
    compositingRenderer = _compositingRenderer;
  }

  function setCompositingRenderer(address _compositingRenderer)
    public
    onlyOwner
  {
    compositingRenderer = _compositingRenderer;
  }

  function setVisualLayerRenderers(
    uint256[] memory indexes,
    address[] memory renderers
  ) public onlyOwner {
    for (uint256 i = 0; i < indexes.length; ++i) {
      visualLayersRenderers[indexes[i]] = renderers[i];
    }
  }

  function setVisualLayerIndexRemap(
    uint256[] memory indexes,
    uint256[] memory attributeIndexes
  ) public onlyOwner {
    for (uint256 i = 0; i < indexes.length; ++i) {
      visualLayerIndexRemap[indexes[i]] = attributeIndexes[i];
    }
  }

  function setAttributeMetadataRenderers(
    uint256[] memory indexes,
    address[] memory renderers
  ) public onlyOwner {
    for (uint256 i = 0; i < indexes.length; ++i) {
      attributeMetadataRenderers[indexes[i]] = renderers[i];
    }
  }

  function setGenericMetadataRenderers(
    uint256[] memory indexes,
    address[] memory renderers
  ) public onlyOwner {
    for (uint256 i = 0; i < indexes.length; ++i) {
      genericMetadataRenderers[indexes[i]] = renderers[i];
    }
  }

  function setMaxVisualLayersIndex(uint256 _maxVisualLayersIndex)
    public
    onlyOwner
  {
    maxVisualLayersIndex = _maxVisualLayersIndex;
  }

  function setMaxAttributeRenderersIndex(uint256 _maxAttributeRenderersIndex)
    public
    onlyOwner
  {
    maxAttributeRenderersIndex = _maxAttributeRenderersIndex;
  }

  function setMaxGenericRenderersIndex(uint256 _maxGenericRenderersIndex)
    public
    onlyOwner
  {
    maxGenericRenderersIndex = _maxGenericRenderersIndex;
  }

  function encodeAttributeIndexesWithLiveServiceData(
    bytes memory seed,
    uint16[] memory attributeIndexes
  ) public pure returns (uint16[] memory encodedAttributeIndexes) {
    encodedAttributeIndexes = new uint16[](attributeIndexes.length + 1);
    encodedAttributeIndexes[0] = uint16(BytesUtils.toUint256(seed, 32));
    for (uint256 i = 1; i < encodedAttributeIndexes.length; ++i) {
      encodedAttributeIndexes[i] = attributeIndexes[i - 1];
    }
  }

  function decodeAttributeIndexesWithLiveServiceData(
    uint16[] memory encodedAttributeIndexes
  ) public pure returns (uint256 tokenId, uint16[] memory attributeIndexes) {
    tokenId = uint256(encodedAttributeIndexes[0]);
    attributeIndexes = new uint16[](encodedAttributeIndexes.length - 1);
    for (uint256 i = 0; i < attributeIndexes.length; ++i) {
      attributeIndexes[i] = encodedAttributeIndexes[i + 1];
    }
  }

  function getRarity(uint256 rarityMultiplier, bytes memory seed)
    public
    view
    returns (uint256[] memory layerIndexes, uint16[] memory attributeIndexes)
  {
    (
      uint256[] memory rceLayerIndexes,
      uint16[] memory rceAttributeIndexes
    ) = rce.getRarity(rarityMultiplier, seed);

    layerIndexes = rceLayerIndexes;
    attributeIndexes = encodeAttributeIndexesWithLiveServiceData(
      seed,
      rceAttributeIndexes
    );
  }

  function getRendererProps(uint16[] memory encodedAttributeIndexes)
    public
    view
    returns (address[] memory renderers, bytes[] memory rendererProps)
  {
    (
      uint256 tokenId,
      uint16[] memory rceAttributeIndexes
    ) = decodeAttributeIndexesWithLiveServiceData(encodedAttributeIndexes);

    renderers = new address[](maxVisualLayersIndex);
    rendererProps = new bytes[](maxVisualLayersIndex);

    for (uint256 i = 0; i < maxVisualLayersIndex; ++i) {
      renderers[i] = visualLayersRenderers[i];
      uint256 remapIndex = visualLayerIndexRemap[i];
      // if remapIndex bigger than the original rce's max layer is considered void
      if (remapIndex > rceAttributeIndexes.length) {
        rendererProps[i] = abi.encode(i, tokenId, rceAttributeIndexes);
      } else {
        RarityCompositingEngine.AttributeData memory ad = rce
          .decodeAttributeData(
            rce.attributeStorage().indexToData(rceAttributeIndexes[remapIndex])
          );
        if (ad.shouldShowInRendererProps) {
          rendererProps[i] = abi.encode(
            i,
            tokenId,
            rceAttributeIndexes[remapIndex]
          );
        } else {
          renderers[i] = address(0);
        }
      }
    }
  }

  function getAttributesJSON(uint16[] memory attributeIndexes)
    public
    view
    returns (string memory)
  {
    bytes memory props = abi.encode(attributeIndexes);
    string[] memory attributesArray = new string[](maxAttributeRenderersIndex);
    for (uint256 i = 0; i < maxAttributeRenderersIndex; ++i) {
      IRenderer renderer = IRenderer(attributeMetadataRenderers[i]);
      attributesArray[i] = renderer.render(props);
    }
    string[] memory metadataComponents = new string[](
      1 + maxGenericRenderersIndex
    );
    metadataComponents[0] = NftMetadataUtils.array(attributesArray);
    for (uint256 i = 0; i < maxGenericRenderersIndex; ++i) {
      IRenderer renderer = IRenderer(genericMetadataRenderers[i]);
      metadataComponents[i + 1] = NftMetadataUtils.keyValue(
        renderer.renderAttributeKey(),
        renderer.render(props)
      );
    }
    return NftMetadataUtils.delimit(metadataComponents);
  }

  function getRender(uint16[] memory attributeIndexes)
    public
    view
    returns (string memory)
  {
    LayerCompositeRenderer renderer = LayerCompositeRenderer(
      compositingRenderer
    );
    (
      address[] memory renderers,
      bytes[] memory rendererProps
    ) = getRendererProps(attributeIndexes);
    return renderer.render(renderer.encodeProps(renderers, rendererProps));
  }

  function getRenderRaw(uint16[] memory attributeIndexes)
    public
    view
    returns (bytes memory)
  {
    LayerCompositeRenderer renderer = LayerCompositeRenderer(
      compositingRenderer
    );
    (
      address[] memory renderers,
      bytes[] memory rendererProps
    ) = getRendererProps(attributeIndexes);
    return renderer.renderRaw(renderer.encodeProps(renderers, rendererProps));
  }
}
[{"inputs":[{"internalType":"address","name":"_rce","type":"address"},{"internalType":"address","name":"_compositingRenderer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"compositingRenderer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"encodedAttributeIndexes","type":"uint16[]"}],"name":"decodeAttributeIndexesWithLiveServiceData","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint16[]","name":"attributeIndexes","type":"uint16[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"seed","type":"bytes"},{"internalType":"uint16[]","name":"attributeIndexes","type":"uint16[]"}],"name":"encodeAttributeIndexesWithLiveServiceData","outputs":[{"internalType":"uint16[]","name":"encodedAttributeIndexes","type":"uint16[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"attributeIndexes","type":"uint16[]"}],"name":"getAttributesJSON","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rarityMultiplier","type":"uint256"},{"internalType":"bytes","name":"seed","type":"bytes"}],"name":"getRarity","outputs":[{"internalType":"uint256[]","name":"layerIndexes","type":"uint256[]"},{"internalType":"uint16[]","name":"attributeIndexes","type":"uint16[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"attributeIndexes","type":"uint16[]"}],"name":"getRender","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"attributeIndexes","type":"uint16[]"}],"name":"getRenderRaw","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"encodedAttributeIndexes","type":"uint16[]"}],"name":"getRendererProps","outputs":[{"internalType":"address[]","name":"renderers","type":"address[]"},{"internalType":"bytes[]","name":"rendererProps","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAttributeRenderersIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxGenericRenderersIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxVisualLayersIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rce","outputs":[{"internalType":"contract RarityCompositingEngine","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes","type":"uint256[]"},{"internalType":"address[]","name":"renderers","type":"address[]"}],"name":"setAttributeMetadataRenderers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_compositingRenderer","type":"address"}],"name":"setCompositingRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes","type":"uint256[]"},{"internalType":"address[]","name":"renderers","type":"address[]"}],"name":"setGenericMetadataRenderers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxAttributeRenderersIndex","type":"uint256"}],"name":"setMaxAttributeRenderersIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxGenericRenderersIndex","type":"uint256"}],"name":"setMaxGenericRenderersIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxVisualLayersIndex","type":"uint256"}],"name":"setMaxVisualLayersIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes","type":"uint256[]"},{"internalType":"uint256[]","name":"attributeIndexes","type":"uint256[]"}],"name":"setVisualLayerIndexRemap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes","type":"uint256[]"},{"internalType":"address[]","name":"renderers","type":"address[]"}],"name":"setVisualLayerRenderers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]