contract FMC is StandardToken, Ownable {
    using SafeMath for uint256;
    string public constant name = "Fan Mei Chain (FMC)";
    string public constant symbol = "FMC";
    uint8 public constant decimals = 18;
    //总配额2亿
    uint256 constant INITIAL_SUPPLY = 200000000 * (10 ** uint256(decimals));
    //设置代币官网短URL(32字节以内)，供管理平台自动查询
    string public website = "www.fanmeichain.com";
    //设置代币icon短URL(32字节以内)，供管理平台自动查询
    string public icon = "/icon/fmc.png";
    //冻结账户
    address public frozenAddress;
    //锁仓信息
    mapping(address=>Info) internal fellowInfo;
    // fellow info
    struct Info{
        uint256[] defrozenDates;                    //解冻日期
        mapping(uint256=>uint256) frozenValues;     //冻结金额
        uint256 totalFrozenValue;                   //全部冻结资产总额
    }
    // 事件定义
    event Frozen(address user, uint256 value, uint256 defrozenDate, uint256 totalFrozenValue);
    event Defrozen(address user, uint256 value, uint256 defrozenDate, uint256 totalFrozenValue);
    // Constructor that gives msg.sender all of existing tokens.
    constructor(address _frozenAddress) public {
        require(_frozenAddress != address(0) && _frozenAddress != msg.sender);
        frozenAddress = _frozenAddress;
        totalSupply_ = INITIAL_SUPPLY;
        balances[msg.sender] = INITIAL_SUPPLY;
        emit Transfer(0x0, msg.sender, INITIAL_SUPPLY);
    }
    /**
   * @dev Transfer token for a specified address
   * @param _to The address to transfer to.
   * @param _value The amount to be transferred.
   */
    function transfer(address _to, uint256 _value) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[msg.sender]);
        //normal transfer
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(msg.sender, _to, _value);
        if(_to == frozenAddress){
            //defrozing
            Info storage _info = fellowInfo[msg.sender];
            if(_info.totalFrozenValue > 0){
                for(uint i=0; i< _info.defrozenDates.length; i++){
                    uint256 _date0 = _info.defrozenDates[i];
                    if(_info.frozenValues[_date0] > 0 && now >= _date0){
                        //defrozen...
                        uint256 _defrozenValue = _info.frozenValues[_date0];
                        require(balances[frozenAddress] >= _defrozenValue);
                        balances[frozenAddress] = balances[frozenAddress].sub(_defrozenValue);
                        balances[msg.sender] = balances[msg.sender].add(_defrozenValue);
                        _info.totalFrozenValue = _info.totalFrozenValue.sub(_defrozenValue);
                        _info.frozenValues[_date0] = 0;
                        emit Transfer(frozenAddress, msg.sender, _defrozenValue);
                        emit Defrozen(msg.sender, _defrozenValue, _date0, _info.totalFrozenValue);
                    }
                }
            }
        }
        return true;
    }
    // issue in batch with forzen
    function issue(address[] payees, uint256[] values, uint16[] deferDays) public onlyOwner returns(bool) {
        require(payees.length > 0 && payees.length == values.length);
        uint256 _now0 = _getNow0();
        for (uint i = 0; i<payees.length; i++) {
            require(balances[owner] >= values[i], "Issuer balance is insufficient.");
            //地址为空或者发行额度为零
            if (payees[i] == address(0) || values[i] == uint256(0)) {
                continue;
            }
            balances[owner] = balances[owner].sub(values[i]);
            balances[payees[i]] = balances[payees[i]].add(values[i]);
            emit Transfer(owner, payees[i], values[i]);
            uint256 _date0 = _now0.add(deferDays[i]*24*3600);
            //判断是否需要冻结
            if(_date0 > _now0){
                //frozen balance
                Info storage _info = fellowInfo[payees[i]];
                uint256 _fValue = _info.frozenValues[_date0];
                if(_fValue == 0){
                    //_date0 doesn't exist in defrozenDates
                    _info.defrozenDates.push(_date0);
                }
                //冻结总量增加_value
                _info.totalFrozenValue = _info.totalFrozenValue.add(values[i]);
                _info.frozenValues[_date0] = _info.frozenValues[_date0].add(values[i]);

                balances[payees[i]] = balances[payees[i]].sub(values[i]);
                balances[frozenAddress] = balances[frozenAddress].add(values[i]);
                emit Transfer(payees[i], frozenAddress, values[i]);
                emit Frozen(payees[i], values[i], _date0, _info.totalFrozenValue);
            }
        }
        return true;
    }
    // airdrop in with same value and deferDays
    function airdrop(address[] payees, uint256 value, uint16 deferDays) public onlyOwner returns(bool) {
        require(payees.length > 0 && value > 0);
        uint256 _amount = value.mul(payees.length);
        require(balances[owner] > _amount);
        uint256 _now0 = _getNow0();
        uint256 _date0 = _now0.add(deferDays*24*3600);
        for (uint i = 0; i<payees.length; i++) {
            require(balances[owner] >= value, "Issuer balance is insufficient.");
            //地址为空或者发行额度为零
            if (payees[i] == address(0)) {
                _amount = _amount.sub(value);
                continue;
            }
            //circulating
            balances[payees[i]] = balances[payees[i]].add(value);
            emit Transfer(owner, payees[i], value);
            //判断是否需要冻结
            if(_date0 > _now0){
                //frozen balance
                Info storage _info = fellowInfo[payees[i]];
                uint256 _fValue = _info.frozenValues[_date0];
                if(_fValue == 0){
                    //_date0 doesn't exist in defrozenDates
                    _info.defrozenDates.push(_date0);
                }
                //冻结总量增加_value
                _info.totalFrozenValue = _info.totalFrozenValue.add(value);
                _info.frozenValues[_date0] = _info.frozenValues[_date0].add(value);
                balances[payees[i]] = balances[payees[i]].sub(value);
                balances[frozenAddress] = balances[frozenAddress].add(value);
                emit Transfer(payees[i], frozenAddress, value);
                emit Frozen(payees[i], value, _date0, _info.totalFrozenValue);
            }
        }
        balances[owner] = balances[owner].sub(_amount);
        return true;
    }
    // update frozen address
    function updateFrozenAddress(address newFrozenAddress) public onlyOwner returns(bool){
        //要求：
        //1. 新地址不能为空
        //2. 新地址不能为owner
        //3. 新地址不能与旧地址相同
        require(newFrozenAddress != address(0) && newFrozenAddress != owner && newFrozenAddress != frozenAddress);
        //要求：新地址账本为零
        require(balances[newFrozenAddress] == 0);
        //转移冻结账本
        balances[newFrozenAddress] = balances[frozenAddress];
        balances[frozenAddress] = 0;
        emit Transfer(frozenAddress, newFrozenAddress, balances[newFrozenAddress]);
        frozenAddress = newFrozenAddress;
        return true;
    }
    //平台解冻指定资产
    function defrozen(address fellow) public onlyOwner returns(bool){
        require(fellow != address(0));
        Info storage _info = fellowInfo[fellow];
        require(_info.totalFrozenValue > 0);
        for(uint i = 0; i< _info.defrozenDates.length; i++){
            uint256 _date0 = _info.defrozenDates[i];
            if(_info.frozenValues[_date0] > 0 && now >= _date0){
                //defrozen...
                uint256 _defrozenValue = _info.frozenValues[_date0];
                require(balances[frozenAddress] >= _defrozenValue);
                balances[frozenAddress] = balances[frozenAddress].sub(_defrozenValue);
                balances[fellow] = balances[fellow].add(_defrozenValue);
                _info.totalFrozenValue = _info.totalFrozenValue.sub(_defrozenValue);
                _info.frozenValues[_date0] = 0;
                emit Transfer(frozenAddress, fellow, _defrozenValue);
                emit Defrozen(fellow, _defrozenValue, _date0, _info.totalFrozenValue);
            }
        }
        return true;
    }
    // check own assets include: balance, totalForzenValue, defrozenDates, defrozenValues
    function getOwnAssets() public view returns(uint256, uint256, uint256[], uint256[]){
        return getAssets(msg.sender);
    }
    // check own assets include: balance, totalForzenValue, defrozenDates, defrozenValues
    function getAssets(address fellow) public view returns(uint256, uint256, uint256[], uint256[]){
        uint256 _value = balances[fellow];
        Info storage _info = fellowInfo[fellow];
        uint256 _totalFrozenValue = _info.totalFrozenValue;
        uint256 _size = _info.defrozenDates.length;
        uint256[] memory _values = new uint256[](_size);
        for(uint i = 0; i < _size; i++){
            _values[i] = _info.frozenValues[_info.defrozenDates[i]];
        }
        return (_value, _totalFrozenValue, _info.defrozenDates, _values);
    }
    // 设置token官网和icon信息
    function setWebInfo(string _website, string _icon) public onlyOwner returns(bool){
        website = _website;
        icon = _icon;
        return true;
    }
    //返回当前区块链时间: 年月日时
    function getNow() public view returns(uint256){
        return now;
    }
    // @dev An internal pure function to calculate date in XX:00:00
    function _calcDate0(uint256 _timestamp) internal pure returns(uint256){
        return _timestamp.sub(_timestamp % (60*24));
    }
    // 获取当前日期零点时间戳
    function _getNow0() internal view returns(uint256){
        return _calcDate0(now);
    }
}
function getAssets(address fellow) public view returns(uint256, uint256, uint256[], uint256[]){
        uint256 _value = balances[fellow];
        Info storage _info = fellowInfo[fellow];
        uint256 _totalFrozenValue = _info.totalFrozenValue;
        uint256 _size = _info.defrozenDates.length;
        uint256[] memory _values = new uint256[](_size);
        for(uint i = 0; i < _size; i++){
            _values[i] = _info.frozenValues[_info.defrozenDates[i]];
        }
        return (_value, _totalFrozenValue, _info.defrozenDates, _values);
    }
// check own assets include: balance, totalForzenValue, defrozenDates, defrozenValues