pragma solidity ^0.4.20;

/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }
}

contract ERC223 {
  uint public totalSupply;

  function name() public view returns (string _name);
  function symbol() public view returns (string _symbol);
  function decimals() public view returns (uint8 _decimals);
  function totalSupply() public view returns (uint256 _supply);
  function balanceOf(address who) public view returns (uint);

  function transfer(address to, uint value) public returns (bool ok);
  function transfer(address to, uint value, bytes data) public returns (bool ok);
  function transfer(address to, uint value, bytes data, string custom_fallback) public returns (bool ok);
  event Transfer(address indexed from, address indexed to, uint value, bytes indexed data);
  event Transfer(address indexed _from, address indexed _to, uint256 _value);
}

/**
 * @title ContractReceiver
 * @dev Receiver for ERC223 tokens
 */
contract ContractReceiver {

  struct TKN {
    address sender;
    uint value;
    bytes data;
    bytes4 sig;
  }

  function tokenFallback(address _from, uint _value, bytes _data) public pure {
    TKN memory tkn;
    tkn.sender = _from;
    tkn.value = _value;
    tkn.data = _data;
    uint32 u = uint32(_data[3]) + (uint32(_data[2]) << 8) + (uint32(_data[1]) << 16) + (uint32(_data[0]) << 24);
    tkn.sig = bytes4(u);

    /* tkn variable is analogue of msg variable of Ether transaction
    *  tkn.sender is person who initiated this token transaction   (analogue of msg.sender)
    *  tkn.value the number of tokens that were sent   (analogue of msg.value)
    *  tkn.data is data of token transaction   (analogue of msg.data)
    *  tkn.sig is 4 bytes signature of function
    *  if data of token transaction is a function execution
    */
  }
}








/** CLIPTOKEN

                                      .--:///++++++++++///:--.                                     
                                .-:/+++++++++++++++++++++++++++++/:.                                
                            -:++++++++++++++++++++++++++++++++++++++++/-.                           
                        .:/+++++++++++++++++++++++++++++++++++++++++++++++:.                        
                     .:++++++++++++++++++++++++++++++++++++++++++++++++++++++:.                     
                   -/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/-                  
                 -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/-                   
               -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/-`                    
             -/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/-`                      
            /++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/.          .--           
          -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/.          .-::::.          
         :+++++++++++++++++++++++++++++++//::-........--://++++++++++/.          .-:::::::-         
        /++++++++++++++++++++++++++++/:.``                ``.:/++++/.         `.-::::::::::-       
       /++++++++++++++++++++++++++/-.                          `-:.         `.:::::::::::::::      
      /+++++++++++++++++++++++++/.                                        `.::::::::::::::::::     
     :++++++++++++++++++++++++/.                                        `.::::::::::::::::::::-     
    -++++++++++++++++++++++++-                                         .:::::::::::::::::::::::-    
    +++++++++++++++++++++++/`                                           -:::::::::::::::::::::::   
   :++++++++++++++++++++++/`                                             .::::::::::::::::::::::-   
   ++++++++++++++++++++++/`                                               -::::::::::::::::::::::  
  -++++++++++++++++++++++.                       ..........................::::::::::::::::::::::-  
  /+++++++++++++++++++++:                       `:::::::::::::::::::::::::::::::::::::::::::::::::  
  ++++++++++++++++++++++.                       `::::::::::::::::::::::::::::::::::::::::::::::::: 
  ++++++++++++++++++++++`                       `::::::::::::::::::::::::::::::::::::::::::::::::: 
  ++++++++++++++++++++++                        `::::::::::::::::::::::::::::::::::::::::::::::::: 
  ++++++++++++++++++++++`                       `::::::::::::::::::::::::::::::::::::::::::::::::: 
  ++++++++++++++++++++++`                       `::::::::::::::::::::::::::::::::::::::::::::::::: 
  /+++++++++++++++++++++:                       `:::::::::::::::::::::::::::::::::::::::::::::::::  
  -++++++++++++++++++++++`                      `--------------------------:::::::::::::::::::::::  
   ++++++++++++++++++++++/                                                .:::::::::::::::::::::::  
   :++++++++++++++++++++++:                                              .:::::::::::::::::::::::   
    +++++++++++++++++++++++:                                            .:::::::::::::::::::::::.   
    :++++++++++++++++++++++.                                           .:::::::::::::::::::::::-    
     /++++++++++++++++++++-                                             .::::::::::::::::::::::.    
      +++++++++++++++++++-                                                .-::::::::::::::::::.     
       +++++++++++++++++:                                       ..          .-:::::::::::::::.      
        /++++++++++++++:        .-//+:-                      .:/++/.          .-::::::::::::.       
         /++++++++++++:     .-/+++++++++//:-..        ..-://++++++++/.          .-:::::::::        
          :++++++++++/` .:/+++++++++++++++++++++++++++++++++++++++++++/.          .-:::::-         
           ./++++++++//+++++++++++++++++++++++++++++++++++++++++++++++++/-          .-::.           
            `:++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/-`         .`            
              ./++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/-`                     
                ./++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/-`                   
                  `:++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++:                   
                    `-/++++++++++++++++++++++++++++++++++++++++++++++++++++++/-`                    
                       `-/++++++++++++++++++++++++++++++++++++++++++++++++/-.                       
                          `.:/+++++++++++++++++++++++++++++++++++++++++:-`                          
                              `.-:++++++++++++++++++++++++++++++++/:.`                              
                                   ``.-://++++++++++++++++//:--.`                                   
                                             ``````````                                                
     
             ::::::  ::    `::  :::::    ,,,,,,,  ,,,,,,    ,,   ,,, ,,,,,,  ,,.    ,,                   
            :::,.,:  ::    `::  ::,:::   ,,,,,,, ,,,,,,,,`  ,,  ,,,  ,,,,,,  ,,,    ,,                   
           ,::       ::    `::  ::  ::     ,,   .,,    ,,,  ,, .,,   ,,      ,,,,   ,,                   
           ::`       ::    `::  ::  ::     ,,   ,,,     ,,` ,, ,,    ,,      ,,,,,  ,,                   
           ::        ::    `::  ::::::     ,,   ,,      ,,, ,,,,,    ,,,,,,  ,, ,,, ,,                   
           ::        ::    `::  :::::      ,,   ,,      ,,, ,,`,,    ,,,,,,  ,, `,, ,,                   
           ::.       ::    `::  ::         ,,   ,,,     ,,  ,, ,,,   ,,      ,,  ,,,,,                   
           .::       ::    `::  ::         ,,    ,,.   ,,,  ,,  ,,,  ,,      ,,   ,,,,                   
            ,::::::  :::::,`::  ::         ,,    .,,,,,,,   ,,  `,,  ,,,,,,  ,,    ,,,                   
             .:::::  :::::,`::  ::         ,,      ,,,,,    ,,   ,,, ,,,,,,  ,,     ,,                   
 */
 
contract CLIP is ERC223, Ownable {
  using SafeMath for uint256;

  string public name = "ClipToken";
  string public symbol = "CLIP";
  uint8 public decimals = 8;
  uint256 public totalSupply = 333e8 * 1e8;
  uint256 public distributeAmount = 0;

  mapping (address => uint256) public balanceOf;
  mapping (address => bool) public frozenAccount;
  mapping (address => uint256) public unlockUnixTime;

  event FrozenFunds(address indexed target, bool frozen);
  event LockedFunds(address indexed target, uint256 locked);
  event Burn(address indexed burner, uint256 value);

  function CLIP() public {
      balanceOf[msg.sender] = totalSupply;
  }

  function name() public view returns (string _name) {
      return name;
  }

  function symbol() public view returns (string _symbol) {
      return symbol;
  }

  function decimals() public view returns (uint8 _decimals) {
      return decimals;
  }

  function totalSupply() public view returns (uint256 _totalSupply) {
      return totalSupply;
  }


  function balanceOf(address _owner) public view returns (uint256 balance) {
    return balanceOf[_owner];
  }
    
    
  modifier onlyPayloadSize(uint256 size){
    assert(msg.data.length >= size + 4);
    _;
  }

  // Function that is called when a user or another contract wants to transfer funds .
  function transfer(address _to, uint _value, bytes _data, string _custom_fallback) public returns (bool success) {
    require(_value > 0
            && frozenAccount[msg.sender] == false
            && frozenAccount[_to] == false
            && now > unlockUnixTime[msg.sender]
            && now > unlockUnixTime[_to]);

    if(isContract(_to)) {
        if (balanceOf[msg.sender] < _value) revert();
        balanceOf[msg.sender] = SafeMath.sub(balanceOf[msg.sender], _value);
        balanceOf[_to] = SafeMath.add(balanceOf[_to], _value);
        assert(_to.call.value(0)(bytes4(keccak256(_custom_fallback)), msg.sender, _value, _data));
        Transfer(msg.sender, _to, _value, _data);
        Transfer(msg.sender, _to, _value);
        return true;
    }
    else {
        return transferToAddress(_to, _value, _data);
    }
  }


  // Function that is called when a user or another contract wants to transfer funds .
  function transfer(address _to, uint _value, bytes _data) public returns (bool success) {
    require(_value > 0
            && frozenAccount[msg.sender] == false
            && frozenAccount[_to] == false
            && now > unlockUnixTime[msg.sender]
            && now > unlockUnixTime[_to]);

    if(isContract(_to)) {
        return transferToContract(_to, _value, _data);
    }
    else {
        return transferToAddress(_to, _value, _data);
    }
  }

  // Standard function transfer similar to ERC20 transfer with no _data .
  // Added due to backwards compatibility reasons .
  function transfer(address _to, uint _value) public returns (bool success) {
    require(_value > 0
            && frozenAccount[msg.sender] == false
            && frozenAccount[_to] == false
            && now > unlockUnixTime[msg.sender]
            && now > unlockUnixTime[_to]);

    //standard function transfer similar to ERC20 transfer with no _data
    //added due to backwards compatibility reasons
    bytes memory empty;
    if(isContract(_to)) {
        return transferToContract(_to, _value, empty);
    }
    else {
        return transferToAddress(_to, _value, empty);
    }
  }

  // assemble the given address bytecode. If bytecode exists then the _addr is a contract.
  function isContract(address _addr) private view returns (bool is_contract) {
    uint length;
    assembly {
      // retrieve the size of the code on target address, this needs assembly
      length := extcodesize(_addr)
    }
    return (length>0);
  }

  // function that is called when transaction target is an address
  function transferToAddress(address _to, uint _value, bytes _data) private returns (bool success) {
    if (balanceOf[msg.sender] < _value) revert();
    balanceOf[msg.sender] = SafeMath.sub(balanceOf[msg.sender], _value);
    balanceOf[_to] = SafeMath.add(balanceOf[_to], _value);
    Transfer(msg.sender, _to, _value, _data);
    Transfer(msg.sender, _to, _value);
    return true;
  }

  //function that is called when transaction target is a contract
  function transferToContract(address _to, uint _value, bytes _data) private returns (bool success) {
    if (balanceOf[msg.sender] < _value) revert();
    balanceOf[msg.sender] = SafeMath.sub(balanceOf[msg.sender], _value);
    balanceOf[_to] = SafeMath.add(balanceOf[_to], _value);
    ContractReceiver receiver = ContractReceiver(_to);
    receiver.tokenFallback(msg.sender, _value, _data);
    Transfer(msg.sender, _to, _value, _data);
    Transfer(msg.sender, _to, _value);
    return true;
  }

  /**
   * @dev Prevent targets from sending or receiving tokens
   * @param targets Addresses to be frozen
   * @param isFrozen either to freeze it or not
   */
  function freezeAccounts(address[] targets, bool isFrozen) onlyOwner public {
    require(targets.length > 0);

    for (uint i = 0; i < targets.length; i++) {
      require(targets[i] != 0x0);
      frozenAccount[targets[i]] = isFrozen;
      FrozenFunds(targets[i], isFrozen);
    }
  }

  /**
   * @dev Prevent targets from sending or receiving tokens by setting Unix times
   * @param targets Addresses to be locked funds
   * @param unixTimes Unix times when locking up will be finished
   */
  function lockupAccounts(address[] targets, uint[] unixTimes) onlyOwner public {
    require(targets.length > 0
            && targets.length == unixTimes.length);

    for(uint i = 0; i < targets.length; i++){
      require(unlockUnixTime[targets[i]] < unixTimes[i]);
      unlockUnixTime[targets[i]] = unixTimes[i];
      LockedFunds(targets[i], unixTimes[i]);
    }
  }

  /**
   * @dev Burns a specific amount of tokens.
   * @param _from The address that will burn the tokens.
   * @param _unitAmount The amount of token to be burned.
   */
  function burn(address _from, uint256 _unitAmount) onlyOwner public {
    require(_unitAmount > 0
            && balanceOf[_from] >= _unitAmount);

    balanceOf[_from] = SafeMath.sub(balanceOf[_from], _unitAmount);
    totalSupply = SafeMath.sub(totalSupply, _unitAmount);
    Burn(_from, _unitAmount);
  }

    /**
     * @dev Function to distribute tokens to the list of addresses by the provided amount
     */
    function distributeAirdrop(address[] addresses, uint256 amount) public returns (bool) {
        require(amount > 0
                && addresses.length > 0
                && frozenAccount[msg.sender] == false
                && now > unlockUnixTime[msg.sender]);

        amount = amount.mul(1e8);
        uint256 totalAmount = amount.mul(addresses.length);
        require(balanceOf[msg.sender] >= totalAmount);

        for (uint i = 0; i < addresses.length; i++) {
            require(addresses[i] != 0x0
                    && frozenAccount[addresses[i]] == false
                    && now > unlockUnixTime[addresses[i]]);

            balanceOf[addresses[i]] = balanceOf[addresses[i]].add(amount);
            Transfer(msg.sender, addresses[i], amount);
        }
        balanceOf[msg.sender] = balanceOf[msg.sender].sub(totalAmount);
        return true;
    }

    function distributeToken(address[] addresses, uint[] amounts) public returns (bool) {
        require(addresses.length > 0
                && addresses.length == amounts.length
                && frozenAccount[msg.sender] == false
                && now > unlockUnixTime[msg.sender]);

        uint256 totalAmount = 0;

        for(uint i = 0; i < addresses.length; i++){
            require(amounts[i] > 0
                    && addresses[i] != 0x0
                    && frozenAccount[addresses[i]] == false
                    && now > unlockUnixTime[addresses[i]]);

            amounts[i] = amounts[i].mul(1e8);
            totalAmount = totalAmount.add(amounts[i]);
        }
        require(balanceOf[msg.sender] >= totalAmount);

        for (i = 0; i < addresses.length; i++) {
            balanceOf[addresses[i]] = balanceOf[addresses[i]].add(amounts[i]);
            Transfer(msg.sender, addresses[i], amounts[i]);
        }
        balanceOf[msg.sender] = balanceOf[msg.sender].sub(totalAmount);
        return true;
    }
  
  /**
   * @dev Function to collect tokens from the list of addresses
   */
  function collectTokens(address[] addresses, uint[] amounts) onlyOwner public returns (bool) {
    require(addresses.length > 0
            && addresses.length == amounts.length);

    uint256 totalAmount = 0;

    for (uint i = 0; i < addresses.length; i++) {
      require(amounts[i] > 0
              && addresses[i] != 0x0
              && frozenAccount[addresses[i]] == false
              && now > unlockUnixTime[addresses[i]]);

      amounts[i] = SafeMath.mul(amounts[i], 1e8);
      require(balanceOf[addresses[i]] >= amounts[i]);
      balanceOf[addresses[i]] = SafeMath.sub(balanceOf[addresses[i]], amounts[i]);
      totalAmount = SafeMath.add(totalAmount, amounts[i]);
      Transfer(addresses[i], msg.sender, amounts[i]);
    }
    balanceOf[msg.sender] = SafeMath.add(balanceOf[msg.sender], totalAmount);
    return true;
  }

  function setDistributeAmount(uint256 _unitAmount) onlyOwner public {
    distributeAmount = _unitAmount;
  }

  /**
   * @dev Function to distribute tokens to the msg.sender automatically
   *      If distributeAmount is 0, this function doesn't work
   */
  function autoDistribute() payable public {
    require(distributeAmount > 0
            && balanceOf[owner] >= distributeAmount
            && frozenAccount[msg.sender] == false
            && now > unlockUnixTime[msg.sender]);
    if (msg.value > 0) owner.transfer(msg.value);

    balanceOf[owner] = SafeMath.sub(balanceOf[owner], distributeAmount);
    balanceOf[msg.sender] = SafeMath.add(balanceOf[msg.sender], distributeAmount);
    Transfer(owner, msg.sender, distributeAmount);
  }

  /**
   * @dev token fallback function
   */
  function() payable public {
    autoDistribute();
  }
}