contract CXTCContract is StandardToken {

    string public constant name = "Culture eXchange Token Chain"; // solium-disable-line uppercase
    string public constant symbol = "CXTC"; // solium-disable-line uppercase
    uint8 public constant decimals = 8; // solium-disable-line uppercase

    uint256 public constant freeSupply = 21000000 * (10 ** uint256(decimals)); // 10%自由量
    uint256 public constant frozenSupply = 189000000 * (10 ** uint256(decimals)); // 90%冻结量

    address[] parterAcc;

    struct ArtInfo {
        string idtReport;
        string evtReport;
        string escReport;
        string regReport;
    }

    mapping (string => ArtInfo) internal artInfos;
    mapping (address => mapping (uint256 => uint256)) public freezeRecord;

    event Freeze(address indexed _addr, uint256 indexed _amount, uint256 indexed _timestamp);
    event Defreeze(address indexed _addr, uint256 indexed _amount, uint256 indexed _timestamp);
    event Release(address indexed _addr, uint256 indexed _amount);
    event SetParter(address indexed _addr, uint256 indexed _amount);
    event SetSysAcc(address indexed _addr);
    event NewArt(string indexed _id);
    event SetArtIdt(string indexed _id, string indexed _idtReport);
    event SetArtEvt(string indexed _id, string indexed _evtReport);
    event SetArtEsc(string indexed _id, string indexed _escReport);
    event SetArtReg(string indexed _id, string indexed _regReport);

    /**
     * @dev Constructor
     */
    function CXTCContract() public {
        owner = msg.sender;
        totalSupply_ = freeSupply + frozenSupply;
        freeBalances[owner] = freeSupply;
        frozenBalances[owner] = frozenSupply;
    }

    /**
     * init parter
     */
    function setParter(address _parter, uint256 _amount, uint256 _timestamp) public onlyOwner {
        parterAcc.push(_parter);
        frozenBalances[owner] = frozenBalances[owner].sub(_amount);
        frozenBalances[_parter] = frozenBalances[_parter].add(_amount);
        freezeRecord[_parter][_timestamp] = freezeRecord[_parter][_timestamp].add(_amount);
        Freeze(_parter, _amount, _timestamp);
        SetParter(_parter, _amount);
    }

    /**
     * set systemAccount
     */
    function setSysAcc(address _sysAcc) public onlyOwner returns (bool) {
        systemAcc = _sysAcc;
        SetSysAcc(_sysAcc);
        return true;
    }

    /**
     * new art hash info
     */
    function newArt(string _id, string _regReport) public onlySys returns (bool) {
        ArtInfo memory info = ArtInfo({idtReport: "", evtReport: "", escReport: "", regReport: _regReport});
        artInfos[_id] = info;
        NewArt(_id);
        return true;
    }

    /**
     * get artInfo
     */
    function getArt(string _id) public view returns (string, string, string, string) {
        ArtInfo memory info = artInfos[_id];
        return (info.regReport, info.idtReport, info.evtReport, info.escReport);
    }

    /**
     * set art idtReport
     */
    function setArtIdt(string _id, string _idtReport) public onlySys returns (bool) {
        string idtReport = artInfos[_id].idtReport;
        bytes memory idtReportLen = bytes(idtReport);
        if (idtReportLen.length == 0){
            artInfos[_id].idtReport = _idtReport;
            SetArtIdt(_id, _idtReport);
            return true;
        } else {
            return false;
        }
    }

    /**
     * set art evtReport
     */
    function setArtEvt(string _id, string _evtReport) public onlySys returns (bool) {
        string evtReport = artInfos[_id].evtReport;
        bytes memory evtReportLen = bytes(evtReport);
        if (evtReportLen.length == 0){
            artInfos[_id].evtReport = _evtReport;
            SetArtEvt(_id, _evtReport);
            return true;
        } else {
            return false;
        }
    }

    /**
     * set art escrow report
     */
    function setArtEsc(string _id, string _escReport) public onlySys returns (bool) {
        string escReport = artInfos[_id].escReport;
        bytes memory escReportLen = bytes(escReport);
        if (escReportLen.length == 0){
            artInfos[_id].escReport = _escReport;
            SetArtEsc(_id, _escReport);
            return true;
        } else {
            return false;
        }
    }

    /**
     * issue art coin to user.
     */
    function issue(address _addr, uint256 _amount, uint256 _timestamp) public onlySys returns (bool) {
        // 2018/03/23 = 1521734400
        require(frozenBalances[owner] >= _amount);
        frozenBalances[owner] = frozenBalances[owner].sub(_amount);
        frozenBalances[_addr]= frozenBalances[_addr].add(_amount);
        freezeRecord[_addr][_timestamp] = freezeRecord[_addr][_timestamp].add(_amount);
        Freeze(_addr, _amount, _timestamp);
        return true;
    }

    /**
     * distribute
     */
    function distribute(address _to, uint256 _amount, uint256 _timestamp, address[] _addressLst, uint256[] _amountLst) public onlySys returns(bool) {
        frozenBalances[_to]= frozenBalances[_to].add(_amount);
        freezeRecord[_to][_timestamp] = freezeRecord[_to][_timestamp].add(_amount);
        for(uint i = 0; i < _addressLst.length; i++) {
            frozenBalances[_addressLst[i]] = frozenBalances[_addressLst[i]].sub(_amountLst[i]);
            Defreeze(_addressLst[i], _amountLst[i], _timestamp);
        }
        Freeze(_to, _amount, _timestamp);
        return true;
    }

    /**
     * send with charge fee
     */
    function send(address _to, uint256 _amount, uint256 _fee, uint256 _timestamp) public whenNotPaused returns (bool) {
        require(freeBalances[msg.sender] >= _amount);
        require(_amount >= _fee);
        require(_to != address(0));
        uint256 toAmt = _amount.sub(_fee);
        freeBalances[msg.sender] = freeBalances[msg.sender].sub(_amount);
        freeBalances[_to] = freeBalances[_to].add(toAmt);
        // systemAcc
        frozenBalances[systemAcc] = frozenBalances[systemAcc].add(_fee);
        freezeRecord[systemAcc][_timestamp] = freezeRecord[systemAcc][_timestamp].add(_fee);
        Transfer(msg.sender, _to, toAmt);
        Freeze(systemAcc, _fee, _timestamp);
        return true;
    }

    /**
     * user freeze free balance
     */
    function freeze(uint256 _amount, uint256 _timestamp) public whenNotPaused returns (bool) {
        require(freeBalances[msg.sender] >= _amount);
        freeBalances[msg.sender] = freeBalances[msg.sender].sub(_amount);
        frozenBalances[msg.sender] = frozenBalances[msg.sender].add(_amount);
        freezeRecord[msg.sender][_timestamp] = freezeRecord[msg.sender][_timestamp].add(_amount);
        Freeze(msg.sender, _amount, _timestamp);
        return true;
    }

    /**
     * auto release
     */
    function release(address[] _addressLst, uint256[] _amountLst) public onlySys returns (bool) {
        require(_addressLst.length == _amountLst.length);
        for(uint i = 0; i < _addressLst.length; i++) {
            freeBalances[_addressLst[i]] = freeBalances[_addressLst[i]].add(_amountLst[i]);
            frozenBalances[_addressLst[i]] = frozenBalances[_addressLst[i]].sub(_amountLst[i]);
            Release(_addressLst[i], _amountLst[i]);
        }
        return true;
    }

    /**
     * bonus shares
     */
    function bonus(uint256 _sum, address[] _addressLst, uint256[] _amountLst) public onlySys returns (bool) {
        require(frozenBalances[systemAcc] >= _sum);
        require(_addressLst.length == _amountLst.length);
        for(uint i = 0; i < _addressLst.length; i++) {
            freeBalances[_addressLst[i]] = freeBalances[_addressLst[i]].add(_amountLst[i]);
            Transfer(systemAcc, _addressLst[i], _amountLst[i]);
        }
        frozenBalances[systemAcc].sub(_sum);
        Release(systemAcc, _sum);
        return true;
    }
}
/**
 * @title CXTCToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `StandardToken` functions.
 */
function setParter(address _parter, uint256 _amount, uint256 _timestamp) public onlyOwner {
    parterAcc.push(_parter);
    frozenBalances[owner] = frozenBalances[owner].sub(_amount);
    frozenBalances[_parter] = frozenBalances[_parter].add(_amount);
    freezeRecord[_parter][_timestamp] = freezeRecord[_parter][_timestamp].add(_amount);
    Freeze(_parter, _amount, _timestamp);
    SetParter(_parter, _amount);
}

/**
 * init parter
 */