contract CommonTokensale is MultiOwnable, Pausable {

    using SafeMath for uint;

    address public beneficiary;
    uint public refundDeadlineTime;

    // Balances of beneficiaries:
    uint public balance;
    uint public balanceComision;
    uint public balanceComisionHold;
    uint public balanceComisionDone;

    // Token contract reference.
    CommonToken public token;

    uint public minPaymentUSD = 250;

    uint public minCapWei;
    uint public maxCapWei;

    uint public minCapUSD;
    uint public maxCapUSD;

    uint public startTime;
    uint public endTime;

    // Stats for current tokensale:

    uint public totalTokensSold;  // Total amount of tokens sold during this tokensale.
    uint public totalWeiReceived; // Total amount of wei received during this tokensale.
    uint public totalUSDReceived; // Total amount of wei received during this tokensale.

    // This mapping stores info on how many ETH (wei) have been sent to this tokensale from specific address.
    mapping (address => uint256) public buyerToSentWei;
    mapping (address => uint256) public sponsorToComisionDone;
    mapping (address => uint256) public sponsorToComision;
    mapping (address => uint256) public sponsorToComisionHold;
    mapping (address => uint256) public sponsorToComisionFromInversor;
    mapping (address => bool) public kicInversor;
    mapping (address => bool) public validateKYC;
    mapping (address => bool) public comisionInTokens;

    address[] public sponsorToComisionList;

    // TODO: realizar opcion de que el inversor quiera cobrar en ETH o TMSY

    event ReceiveEthEvent(address indexed _buyer, uint256 _amountWei);
    event NewInverstEvent(address indexed _child, address indexed _sponsor);
    event ComisionEvent(address indexed _sponsor, address indexed _child, uint256 _value, uint256 _comision);
    event ComisionPayEvent(address indexed _sponsor, uint256 _value, uint256 _comision);
    event ComisionInversorInTokensEvent(address indexed _sponsor, bool status);
    event ChangeEndTimeEvent(address _sender, uint endTime, uint _date);
    event verifyKycEvent(address _sender, uint _date, bool _status);
    event payComisionSponsorTMSY(address _sponsor, uint _date, uint _value);
    event payComisionSponsorETH(address _sponsor, uint _date, uint _value);
    event withdrawEvent(address _sender, address _to, uint value, uint _date);
    event conversionToUSDEvent(uint _value, uint rateUsd, uint usds);
    event newRatioEvent(uint _value, uint date);
    event conversionETHToTMSYEvent(address _buyer, uint value, uint tokensE18SinBono, uint tokensE18Bono);
    event createContractEvent(address _token, address _beneficiary, uint _startTime, uint _endTime);
    // ratio USD-ETH
    uint public rateUSDETH;

    bool public isSoftCapComplete = false;

    // Array para almacenar los inversores
    mapping(address => bool) public inversors;
    address[] public inversorsList;

    // Array para almacenar los sponsors para hacer reparto de comisiones
    mapping(address => address) public inversorToSponsor;

    constructor (
        address _token,
        address _beneficiary,
        uint _startTime,
        uint _endTime
    ) MultiOwnable() public {

        require(_token != address(0));
        token = CommonToken(_token);

        emit createContractEvent(_token, _beneficiary, _startTime, _endTime);

        beneficiary = _beneficiary;

        startTime = now;
        endTime   = 1544313600;


        minCapUSD = 400000;
        maxCapUSD = 4000000;
    }

    function setRatio(uint _rate) onlyOwner public returns (bool) {
      rateUSDETH = _rate;
      emit newRatioEvent(rateUSDETH, now);
      return true;
    }

    //TODO: validateKYC
    //En el momento que validan el KYC se les entregan los tokens

    function burn(uint _value) onlyOwner public returns (bool) {
      return token.burn(_value);
    }

    function newInversor(address _newInversor, address _sponsor) onlyOwner public returns (bool) {
      inversors[_newInversor] = true;
      inversorsList.push(_newInversor);
      inversorToSponsor[_newInversor] = _sponsor;
      emit NewInverstEvent(_newInversor,_sponsor);
      return inversors[_newInversor];
    }
    function setComisionInvesorInTokens(address _inversor, bool _inTokens) onlyOwner public returns (bool) {
      comisionInTokens[_inversor] = _inTokens;
      emit ComisionInversorInTokensEvent(_inversor, _inTokens);
      return true;
    }
    function setComisionInTokens() public returns (bool) {
      comisionInTokens[msg.sender] = true;
      emit ComisionInversorInTokensEvent(msg.sender, true);
      return true;
    }
    function setComisionInETH() public returns (bool) {
      comisionInTokens[msg.sender] = false;
      emit ComisionInversorInTokensEvent(msg.sender, false);

      return true;
    }
    function inversorIsKyc(address who) public returns (bool) {
      return validateKYC[who];
    }
    function unVerifyKyc(address _inversor) onlyOwner public returns (bool) {
      require(!isSoftCapComplete);

      validateKYC[_inversor] = false;

      address sponsor = inversorToSponsor[_inversor];
      uint balanceHold = sponsorToComisionFromInversor[_inversor];

      //Actualizamos contadores globales
      balanceComision = balanceComision.sub(balanceHold);
      balanceComisionHold = balanceComisionHold.add(balanceHold);

      //Actualizamos contadores del sponsor
      sponsorToComision[sponsor] = sponsorToComision[sponsor].sub(balanceHold);
      sponsorToComisionHold[sponsor] = sponsorToComisionHold[sponsor].add(balanceHold);

      //Actualizamos contador comision por inversor
    //  sponsorToComisionFromInversor[_inversor] = sponsorToComisionFromInversor[_inversor].sub(balanceHold);
      emit verifyKycEvent(_inversor, now, false);
    }
    function verifyKyc(address _inversor) onlyOwner public returns (bool) {
      validateKYC[_inversor] = true;

      address sponsor = inversorToSponsor[_inversor];
      uint balanceHold = sponsorToComisionFromInversor[_inversor];

      //Actualizamos contadores globales
      balanceComision = balanceComision.add(balanceHold);
      balanceComisionHold = balanceComisionHold.sub(balanceHold);

      //Actualizamos contadores del sponsor
      sponsorToComision[sponsor] = sponsorToComision[sponsor].add(balanceHold);
      sponsorToComisionHold[sponsor] = sponsorToComisionHold[sponsor].sub(balanceHold);

      //Actualizamos contador comision por inversor
      //sponsorToComisionFromInversor[_inversor] = sponsorToComisionFromInversor[_inversor].sub(balanceHold);
      emit verifyKycEvent(_inversor, now, true);
      //Enviamos comisiones en caso de tener
      /*uint256 value = sponsorToComision[_inversor];
      sponsorToComision[_inversor] = sponsorToComision[_inversor].sub(value);
      _inversor.transfer(value);*/
      return true;
    }
    function buyerToSentWeiOf(address who) public view returns (uint256) {
      return buyerToSentWei[who];
    }
    function balanceOf(address who) public view returns (uint256) {
      return token.balanceOf(who);
    }
    function balanceOfComision(address who)  public view returns (uint256) {
      return sponsorToComision[who];
    }
    function balanceOfComisionHold(address who)  public view returns (uint256) {
      return sponsorToComisionHold[who];
    }
    function balanceOfComisionDone(address who)  public view returns (uint256) {
      return sponsorToComisionDone[who];
    }

    function isInversor(address who) public view returns (bool) {
      return inversors[who];
    }
    function payComisionSponsor(address _inversor) private {
      //comprobamos que el inversor quiera cobrar en tokens...
      //si es así le pagamos directo y añadimos los tokens a su cuenta
      if(comisionInTokens[_inversor]) {
        uint256 val = 0;
        uint256 valueHold = sponsorToComisionHold[_inversor];
        uint256 valueReady = sponsorToComision[_inversor];

        val = valueReady.add(valueHold);
        //comprobamos que tenga comisiones a cobrar
        if(val > 0) {
          require(balanceComision >= valueReady);
          require(balanceComisionHold >= valueHold);
          uint256 comisionTokens = weiToTokens(val);

          sponsorToComision[_inversor] = 0;
          sponsorToComisionHold[_inversor] = 0;

          balanceComision = balanceComision.sub(valueReady);
          balanceComisionDone = balanceComisionDone.add(val);
          balanceComisionHold = balanceComisionHold.sub(valueHold);

          balance = balance.add(val);

          token.sell(_inversor, comisionTokens);
          emit payComisionSponsorTMSY(_inversor, now, val); //TYPO TMSY
        }
      } else {
        uint256 value = sponsorToComision[_inversor];

        //comprobamos que tenga comisiones a cobrar
        if(value > 0) {
          require(balanceComision >= value);

          //Si lo quiere en ETH
          //comprobamos que hayamos alcanzado el softCap
          assert(isSoftCapComplete);

          //Comprobamos que el KYC esté validado
          assert(validateKYC[_inversor]);

          sponsorToComision[_inversor] = sponsorToComision[_inversor].sub(value);
          balanceComision = balanceComision.sub(value);
          balanceComisionDone = balanceComisionDone.add(value);

          _inversor.transfer(value);
          emit payComisionSponsorETH(_inversor, now, value); //TYPO TMSY

        }

      }
    }
    function payComision() public {
      address _inversor = msg.sender;
      payComisionSponsor(_inversor);
    }
    //Enviamos las comisiones que se han congelado o por no tener kyc o por ser en softcap
    /*function sendHoldComisions() onlyOwner public returns (bool) {
      //repartimos todas las comisiones congeladas hasta ahora
      uint arrayLength = sponsorToComisionList.length;
      for (uint i=0; i<arrayLength; i++) {
        // do something
        address sponsor = sponsorToComisionList[i];

        if(validateKYC[sponsor]) {
          uint256 value = sponsorToComision[sponsor];
          sponsorToComision[sponsor] = sponsorToComision[sponsor].sub(value);
          sponsor.transfer(value);
        }
      }
      return true;
    }*/
    function isSoftCapCompleted() public view returns (bool) {
      return isSoftCapComplete;
    }
    function softCapCompleted() public {
      uint totalBalanceUSD = weiToUSD(balance.div(1e18));
      if(totalBalanceUSD >= minCapUSD) isSoftCapComplete = true;
    }

    function balanceComisionOf(address who) public view returns (uint256) {
      return sponsorToComision[who];
    }
    function getNow() public returns (uint) {
      return now;
    }
    /** The fallback function corresponds to a donation in ETH. */
    function() public payable {
        //sellTokensForEth(msg.sender, msg.value);

        uint256 _amountWei = msg.value;
        address _buyer = msg.sender;
        uint valueUSD = weiToUSD(_amountWei);

        require(now <= endTime, 'endtime');
        require(inversors[_buyer] != false, 'No invest');
        require(valueUSD >= minPaymentUSD, 'Min in USD not allowed');
        //require(totalUSDReceived.add(valueUSD) <= maxCapUSD);
        emit ReceiveEthEvent(_buyer, _amountWei);

        uint tokensE18SinBono = weiToTokens(msg.value);
        uint tokensE18Bono = weiToTokensBono(msg.value);
        emit conversionETHToTMSYEvent(_buyer, msg.value, tokensE18SinBono, tokensE18Bono);

        uint tokensE18 = tokensE18SinBono.add(tokensE18Bono);

        //Ejecutamos la transferencia de tokens y paramos si ha fallado
        require(token.sell(_buyer, tokensE18SinBono), "Falla la venta");
        if(tokensE18Bono > 0)
          assert(token.sell(_buyer, tokensE18Bono));

        //repartimos al sponsor su parte 10%
        uint256 _amountSponsor = (_amountWei * 10) / 100;
        uint256 _amountBeneficiary = (_amountWei * 90) / 100;

        totalTokensSold = totalTokensSold.add(tokensE18);
        totalWeiReceived = totalWeiReceived.add(_amountWei);
        buyerToSentWei[_buyer] = buyerToSentWei[_buyer].add(_amountWei);

        //por cada compra miramos cual es la cantidad actual de USD... si hemos llegado al softcap lo activamos
        if(!isSoftCapComplete) {
          uint256 totalBalanceUSD = weiToUSD(balance);
          if(totalBalanceUSD >= minCapUSD) {
            softCapCompleted();
          }
        }
        address sponsor = inversorToSponsor[_buyer];
        sponsorToComisionList.push(sponsor);

        if(validateKYC[_buyer]) {
          //Añadimos el saldo al sponsor
          balanceComision = balanceComision.add(_amountSponsor);
          sponsorToComision[sponsor] = sponsorToComision[sponsor].add(_amountSponsor);

        } else {
          //Añadimos el saldo al sponsor
          balanceComisionHold = balanceComisionHold.add(_amountSponsor);
          sponsorToComisionHold[sponsor] = sponsorToComisionHold[sponsor].add(_amountSponsor);
          sponsorToComisionFromInversor[_buyer] = sponsorToComisionFromInversor[_buyer].add(_amountSponsor);
        }


        payComisionSponsor(sponsor);


        balance = balance.add(_amountBeneficiary);
    }

    function weiToUSD(uint _amountWei) public view returns (uint256) {
      uint256 ethers = _amountWei;

      uint256 valueUSD = rateUSDETH.mul(_amountWei);

      emit conversionToUSDEvent(_amountWei, rateUSDETH, valueUSD.div(1e18));
      return valueUSD.div(1e18);
    }

    function weiToTokensBono(uint _amountWei) public view returns (uint256) {
      uint bono = 0;

      uint256 valueUSD = weiToUSD(_amountWei);

      // Calculamos bono
      //Tablas de bonos
      if(valueUSD >= uint(500 * 10**18))   bono = 10;
      if(valueUSD >= uint(1000 * 10**18))   bono = 20;
      if(valueUSD >= uint(2500 * 10**18))   bono = 30;
      if(valueUSD >= uint(5000 * 10**18))   bono = 40;
      if(valueUSD >= uint(10000 * 10**18))   bono = 50;


      uint256 bonoUsd = valueUSD.mul(bono).div(100);
      uint256 tokens = bonoUsd.mul(tokensPerUSD());

      return tokens;
    }
    /** Calc how much tokens you can buy at current time. */
    function weiToTokens(uint _amountWei) public view returns (uint256) {

        uint256 valueUSD = weiToUSD(_amountWei);

        uint256 tokens = valueUSD.mul(tokensPerUSD());

        return tokens;
    }

    function tokensPerUSD() public pure returns (uint256) {
        return 65; // Default token price with no bonuses.
    }

    function canWithdraw() public view returns (bool);

    function withdraw(address _to, uint value) public returns (uint) {
        require(canWithdraw(), 'No es posible retirar');
        require(msg.sender == beneficiary, 'Sólo puede solicitar el beneficiario los fondos');
        require(balance > 0, 'Sin fondos');
        require(balance >= value, 'No hay suficientes fondos');
        require(_to.call.value(value).gas(1)(), 'No se que es');

        balance = balance.sub(value);
        emit withdrawEvent(msg.sender, _to, value,now);
        return balance;
    }

    //Manage timelimit. For exception
    function changeEndTime(uint _date) onlyOwner public returns (bool) {
     // require(endTime < _date);
      endTime = _date;
      refundDeadlineTime = endTime + 3 * 30 days;
      emit ChangeEndTimeEvent(msg.sender,endTime,_date);
      return true;
    }
}
function isSoftCapCompleted() public view returns (bool) {
      return isSoftCapComplete;
    }
/*function sendHoldComisions() onlyOwner public returns (bool) {
 //repartimos todas las comisiones congeladas hasta ahora
 uint arrayLength = sponsorToComisionList.length;
 for (uint i=0; i<arrayLength; i++) {
 // do something
 address sponsor = sponsorToComisionList[i];
 
 if(validateKYC[sponsor]) {
 uint256 value = sponsorToComision[sponsor];
 sponsorToComision[sponsor] = sponsorToComision[sponsor].sub(value);
 sponsor.transfer(value);
 }
 }
 return true;
 }*/