// SPDX-License-Identifier: MIT

pragma solidity 0.8.13;
// import "openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sol"; // 如果在本地构建，使用这个地址
// import "github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC1155/ERC1155.sol";
import "openzeppelin/contracts/token/ERC1155/ERC1155.sol";

contract ProcessingFactory is ERC1155 {
    uint256 public maxGoodsId; // 当前已经铸币的最大的币 id

    /**
     * 币的属性, 使用Goods 结构体整合
     * goodsID 币的id
     * amount 发行数量
     * publishTime 发行时间, 时间戳
     * publisher 发行方
     * name 通证名称
     * labelID 通证标签ID， 展示用的通证ID
     * goodsType 通证类型  0:FT(ERC20) 1:NFT(ERC721)
     * remark 币的备注
     */
    struct Goods {
        uint256 goodsID;
        uint256 amount;
        int32 goodsType;
        int64 publishTime;
        string publisher;
        string name;
        string labelID;
        string remark;
    }

    /**
     * 余额，地址对应的合约里面币的余额
     * account 用户地址
     * goodsID 币的id
     * balance 余额数量
     */
    struct AccountBalance {
        address account;
        uint256 goodsID;
        uint256 balance;
    }

    // 通证（币）属性
    mapping(uint256 => Goods) attribute;

    // 币 对应的当前hash
    mapping(uint256 => string[]) public goodsHash;

    // 币 对应的来源hash，如果币是由一些溯源来源转换而来，该值记录币的转换之前的来源
    mapping(uint256 => string[]) public sourceHash;

    // 发行币成功后，调用事件通知
    event addNewGoodsResult(
        address owner,
        uint256 goodsID,
        uint256 amount,
        int32 goodsType,
        int64 publishTime,
        string[] hash,
        string[] source,
        string publisher,
        string name,
        string labelID,
        string remark
    );

    // 用户余额变动后，通过event发出事件通知，后续服务记录解析
    event balanceResult(
        AccountBalance[]  balanceList
    );

    // 交易成功后，调用事件通知
    event batchTransferResult(
        address from,
        address to,
        uint256[] ids,
        uint256[] amounts,
        bytes data
    );

    constructor() public ERC1155("") {
        maxGoodsId = 0;
    }


    /**
     * 铸币, 传输完整参数
     * owner 币的属于人
     * goodsID 币的id
     * amount 币的数量，nft=1，ft为实际数量； 给 owner铸造id为goodsID的币，数量为amount, 如果amount=1，为nft，如果大于1，为ft
     * goodsType 通证类型  0:FT(ERC20) 1:NFT(ERC721)
     * publishTime 发行时间, 时间戳
     * hash 币对应的hash
     * source 币对应的来源hash
     * publisher 发行方
     * name 通证名称
     * labelID 通证标签ID
     * remark 币的备注
     */
    function addNewGoodsAssignGoodsID(
        address owner,
        uint256 goodsID,
        uint256 amount,
        int32 goodsType,
        int64 publishTime,
        string[] memory hash,
        string[] memory source,
        string memory publisher,
        string memory name,
        string memory labelID,
        string memory remark
    ) public returns (uint256) {
        // 在发行的币id小于已经发行的最大币id的情况下，自动返回成功
        if (goodsID <= maxGoodsId) {
            return goodsID;
        }

        maxGoodsId = goodsID;
        attribute[goodsID] = Goods(
            goodsID,
            amount,
            goodsType,
            publishTime,
            publisher,
            name,
            labelID,
            remark
        );

        goodsHash[goodsID] = hash;
        sourceHash[goodsID] = source;
        _mint(owner, goodsID, amount, "");

        uint256 balance = balanceOf(owner, goodsID);
        AccountBalance[] memory balanceList = new AccountBalance[](1);
        balanceList[0] = AccountBalance(owner, goodsID, balance);

        emit addNewGoodsResult(
            owner,
            goodsID,
            amount,
            goodsType,
            publishTime,
            hash,
            source,
            publisher,
            name,
            labelID,
            remark);

        emit balanceResult(
            balanceList
        );
        return goodsID;
    }

    /**
     * 铸币，使用自增的goodsId, 推荐使用
     * owner 币的属于人
     * amount 币的数量，nft=1，ft为实际数量
     * goodsType 通证类型  0:FT(ERC20) 1:NFT(ERC721)
     * publishTime 发行时间, 时间戳
     * hash 币对应的hash
     * source 币对应的来源hash
     * publisher 发行方
     * name 通证名称
     * labelID 通证标签ID
     * remark 币的备注
     *
     * return
     * uint256 通证id
     */
    function addNewGoods(
        address owner,
        uint256 amount,
        int32 goodsType,
        int64 publishTime,
        string[] memory hash,
        string[] memory source,
        string memory publisher,
        string memory name,
        string memory labelID,
        string memory remark
    ) public returns (uint256) {
        uint256 goodsID = maxGoodsId + 1;
        return
        addNewGoodsAssignGoodsID(
            owner,
            goodsID,
            amount,
            goodsType,
            publishTime,
            hash,
            source,
            publisher,
            name,
            labelID,
            remark
        );
    }

    /**
     * 交易之后调用event事件，便于chain33 日志解析
     * from 从哪个账户扣款
     * to 给哪个账户增加余额
     * ids 操作币的id
     * amounts 每个币交易的量，位置一一对应
     * data 交易备注
     */
    function BatchTransferWithEvent(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public {
        safeBatchTransferFrom(from, to, ids, amounts, data);

        AccountBalance[] memory balanceList = new AccountBalance[](2 * ids.length);
        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 fromBalance = balanceOf(from, ids[i]);
            balanceList[2*i] = AccountBalance(from, ids[i], fromBalance);

            uint256 toBalance = balanceOf(to, ids[i]);
            balanceList[2*i + 1] = AccountBalance(to, ids[i], toBalance);
        }

        emit batchTransferResult(from, to, ids, amounts, data);
        emit balanceResult(balanceList);
    }

    // 查询币对应的hash
    function getGoodsHash(uint256 goodsID) public view returns (string[] memory) {
        return goodsHash[goodsID];
    }

    // 查询币对应的来源hash
    function getsourceHash(uint256 goodsID) public view returns (string[] memory) {
        return sourceHash[goodsID];
    }

    // 查询币对应的属性
    function getGoodsAttribute(uint256 goodsID) public view returns (Goods memory) {
        return attribute[goodsID];
    }

    // 查询当前已经发行了的最大的币ID
    function getMaxGoodsID() public view returns (uint256) {
        return maxGoodsId;
    }
}

