contract PassTokenManager is PassTokenManagerInterface {

// Constant functions

    function Client() constant returns (address) {
        return passDao.ActualCommitteeRoom();
    }
   
    function totalSupply() constant external returns (uint256) {
        return totalTokenSupply;
    }
    
    function balanceOf(address _owner) constant external returns (uint256 balance) {
        return balances[_owner];
    }
     
    function Transferable() constant external returns (bool) {
        return transferable;
    }
 
    function allowance(
        address _owner, 
        address _spender) constant external returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }

    function FundedAmount(uint _proposalID) constant external returns (uint) {
        return fundings[_proposalID].fundedAmount;
    }
  
    function AmountToFund(uint _proposalID) constant external returns (uint) {

        if (now > fundings[_proposalID].closingTime
            || now < fundings[_proposalID].startTime) {
            return 0;   
            } else return fundings[_proposalID].amountToFund;
    }
    
    function priceMultiplier(uint _proposalID) constant internal returns (uint) {
        return fundings[_proposalID].initialPriceMultiplier;
    }
    
    function priceDivisor(uint _proposalID, uint _saleDate) constant internal returns (uint) {
        uint _date = _saleDate;
        
        if (_saleDate > fundings[_proposalID].closingTime) _date = fundings[_proposalID].closingTime;
        if (_saleDate < fundings[_proposalID].startTime) _date = fundings[_proposalID].startTime;

        return 100 + 100*fundings[_proposalID].inflationRate*(_date - fundings[_proposalID].startTime)/(100*365 days);
    }
    
    function actualPriceDivisor(uint _proposalID) constant internal returns (uint) {
        return priceDivisor(_proposalID, now);
    }
    
    function TokenAmount(
        uint _weiAmount, 
        uint _priceMultiplier, 
        uint _priceDivisor) constant internal returns (uint) {
        
        uint _a = _weiAmount*_priceMultiplier;
        uint _multiplier = 100*_a;
        uint _amount = _multiplier/_priceDivisor;
        if (_a/_weiAmount != _priceMultiplier
            || _multiplier/100 != _a) return 0; 
        
        return _amount;
    }
    
    function weiAmount(
        uint _tokenAmount, 
        uint _priceMultiplier, 
        uint _priceDivisor) constant internal returns (uint) {
        
        uint _multiplier = _tokenAmount*_priceDivisor;
        uint _divisor = 100*_priceMultiplier;
        uint _amount = _multiplier/_divisor;
        if (_multiplier/_tokenAmount != _priceDivisor
            || _divisor/100 != _priceMultiplier) return 0; 

        return _amount;
    }
    
    function TokenPriceInWei(uint _tokenAmount, uint _proposalID) constant returns (uint) {
        return weiAmount(_tokenAmount, priceMultiplier(_proposalID), actualPriceDivisor(_proposalID));
    }
    
    function LastProposalID() constant returns (uint) {
        return lastProposalID;
    }
    
    function numberOfHolders() constant returns (uint) {
        return holders.length - 1;
    }
    
    function HolderAddress(uint _index) constant external returns (address) {
        return holders[_index];
    }

// Modifiers

    // Modifier that allows only the client ..
    modifier onlyClient {if (msg.sender != Client()) throw; _;}
      
    // Modifier for share Manager functions
    modifier onlyShareManager {if (token) throw; _;}

    // Modifier for token Manager functions
    modifier onlyTokenManager {if (!token) throw; _;}
  
// Constructor function

    function PassTokenManager(
        PassDao _passDao,
        address _clonedFrom,
        string _tokenName,
        string _tokenSymbol,
        uint8 _tokenDecimals,
        bool _token,
        bool _transferable,
        uint _initialPriceMultiplier,
        uint _inflationRate) {

        passDao = _passDao;
        creator = msg.sender;
        
        clonedFrom = _clonedFrom;            

        name = _tokenName;
        symbol = _tokenSymbol;
        decimals = _tokenDecimals;

        token = _token;
        transferable = _transferable;

        fundings[0].initialPriceMultiplier = _initialPriceMultiplier;
        fundings[0].inflationRate = _inflationRate;

        holders.length = 1;
    }

// Setting functions

    function initialTokenSupply(
        address _recipient, 
        uint _quantity,
        bool _last) returns (bool success) {

        if (initialTokenSupplyDone) throw;
        
        addHolder(_recipient);
        if (_recipient != 0 && _quantity != 0) createTokens(_recipient, _quantity);
        
        if (_last) initialTokenSupplyDone = true;
        
        return true;
    }

    function cloneTokens(
        uint _from,
        uint _to) returns (bool success) {
        
        initialTokenSupplyDone = true;
        if (_from == 0) _from = 1;
        
        PassTokenManager _clonedFrom = PassTokenManager(clonedFrom);
        uint _numberOfHolders = _clonedFrom.numberOfHolders();
        if (_to == 0 || _to > _numberOfHolders) _to = _numberOfHolders;
        
        address _holder;
        uint _balance;

        for (uint i = _from; i <= _to; i++) {
            _holder = _clonedFrom.HolderAddress(i);
            _balance = _clonedFrom.balanceOf(_holder);
            if (balances[_holder] == 0 && _balance != 0) {
                addHolder(_holder);
                createTokens(_holder, _balance);
            }
        }
    }
        
// Token creation

    function addHolder(address _holder) internal {
        
        if (holderID[_holder] == 0) {
            
            uint _holderID = holders.length++;
            holders[_holderID] = _holder;
            holderID[_holder] = _holderID;
        }
    }

    function createTokens(
        address _holder, 
        uint _tokenAmount) internal {

        balances[_holder] += _tokenAmount; 
        totalTokenSupply += _tokenAmount;
        TokensCreated(msg.sender, _holder, _tokenAmount);
    }
    
    function rewardTokensForClient(
        address _recipient, 
        uint _amount
        ) external onlyClient returns (uint) {

        uint _tokenAmount = TokenAmount(_amount, priceMultiplier(0), actualPriceDivisor(0));
        if (_tokenAmount == 0) throw;

        addHolder(_recipient);
        createTokens(_recipient, _tokenAmount);

        return _tokenAmount;
    }
    
    function setFundingRules(
        address _moderator,
        uint _initialPriceMultiplier,
        uint _amountToFund,
        uint _minutesFundingPeriod, 
        uint _inflationRate,
        uint _proposalID
    ) external onlyClient {

        if (_moderator == address(this)
            || _moderator == Client()
            || _amountToFund == 0
            || _minutesFundingPeriod == 0
            || fundings[_proposalID].totalWeiGiven != 0
            ) throw;
            
        fundings[_proposalID].moderator = _moderator;

        fundings[_proposalID].amountToFund = _amountToFund;
        fundings[_proposalID].fundedAmount = 0;

        if (_initialPriceMultiplier == 0) {
            if (now < fundings[0].closingTime) {
                fundings[_proposalID].initialPriceMultiplier = 100*priceMultiplier(lastProposalID)/actualPriceDivisor(lastProposalID);
            } else {
                fundings[_proposalID].initialPriceMultiplier = 100*priceMultiplier(lastFueledFundingID)/actualPriceDivisor(lastFueledFundingID);
            }
            fundings[0].initialPriceMultiplier = fundings[_proposalID].initialPriceMultiplier;
        }
        else {
            fundings[_proposalID].initialPriceMultiplier = _initialPriceMultiplier;
            fundings[0].initialPriceMultiplier = _initialPriceMultiplier;
        }
        
        if (_inflationRate == 0) fundings[_proposalID].inflationRate = fundings[0].inflationRate;
        else {
            fundings[_proposalID].inflationRate = _inflationRate;
            fundings[0].inflationRate = _inflationRate;
        }
        
        fundings[_proposalID].startTime = now;
        fundings[0].startTime = now;
        
        fundings[_proposalID].closingTime = now + _minutesFundingPeriod * 1 minutes;
        fundings[0].closingTime = fundings[_proposalID].closingTime;
        
        fundings[_proposalID].totalWeiGiven = 0;
        
        lastProposalID = _proposalID;
        
        FundingRulesSet(_moderator, _proposalID,  _amountToFund, fundings[_proposalID].startTime, fundings[_proposalID].closingTime);
    } 
    
    function sale(
        uint _proposalID,
        address _recipient, 
        uint _amount,
        uint _saleDate,
        bool _presale) internal returns (bool success) {

        if (_saleDate == 0) _saleDate = now;

        if (_saleDate > fundings[_proposalID].closingTime
            || _saleDate < fundings[_proposalID].startTime
            || fundings[_proposalID].totalWeiGiven + _amount > fundings[_proposalID].amountToFund) return;

        uint _tokenAmount = TokenAmount(_amount, priceMultiplier(_proposalID), priceDivisor(_proposalID, _saleDate));
        if (_tokenAmount == 0) return;
        
        addHolder(_recipient);
        if (_presale) {
            Given[_proposalID][_recipient].tokenAmount += _tokenAmount;
        }
        else createTokens(_recipient, _tokenAmount);

        return true;
    }

    function closeFunding(uint _proposalID) internal {
        fundings[_proposalID].fundedAmount = fundings[_proposalID].totalWeiGiven;
        lastFueledFundingID = _proposalID;
        fundings[_proposalID].closingTime = now;
        FundingFueled(_proposalID, fundings[_proposalID].fundedAmount);
    }

    function sendPendingAmounts(        
        uint _from,
        uint _to,
        address _buyer) returns (bool) {
        
        if (_from == 0) _from = 1;
        if (_to == 0) _to = lastProposalID;
        if (_buyer == 0) _buyer = msg.sender;

        uint _amount;
        uint _tokenAmount;
        
        for (uint i = _from; i <= _to; i++) {

            if (now > fundings[i].closingTime && Given[i][_buyer].weiAmount != 0) {
                
                if (fundings[i].fundedAmount == 0) _amount += Given[i][_buyer].weiAmount;
                else _tokenAmount += Given[i][_buyer].tokenAmount;

                fundings[i].totalWeiGiven -= Given[i][_buyer].weiAmount;
                Given[i][_buyer].tokenAmount = 0;
                Given[i][_buyer].weiAmount = 0;
            }
        }

        if (_tokenAmount > 0) {
            createTokens(_buyer, _tokenAmount);
            return true;
        }
        
        if (_amount > 0) {
            if (!_buyer.send(_amount)) throw;
            Refund(_buyer, _amount);
        } else return true;
    }
    

    function withdrawPendingAmounts() returns (bool) {
        
        return sendPendingAmounts(0, 0, msg.sender);
    }        

// Funding Moderator functions

    function setFundingStartTime(uint _proposalID, uint _startTime) external {
        if ((msg.sender !=  fundings[_proposalID].moderator) || now > fundings[_proposalID].closingTime) throw;
        fundings[_proposalID].startTime = _startTime;
    }

    function setFundingFueled(uint _proposalID) external {

        if ((msg.sender !=  fundings[_proposalID].moderator) || now > fundings[_proposalID].closingTime) throw;

        closeFunding(_proposalID);
    }
    
// Tokens transfer management    
    
    function ableTransfer() onlyClient {
        if (!transferable) {
            transferable = true;
            TransferAble();
        }
    }

    function disableTransfer() onlyClient {
        if (transferable) {
            transferable = false;
            TransferDisable();
        }
    }
    
    function blockTransfer(address _shareHolder, uint _deadLine) external onlyClient onlyShareManager {
        if (_deadLine > blockedDeadLine[_shareHolder]) {
            blockedDeadLine[_shareHolder] = _deadLine;
        }
    }
    
    function transferFromTo(
        address _from,
        address _to, 
        uint256 _value
        ) internal returns (bool success) {  

        if ((transferable)
            && now > blockedDeadLine[_from]
            && now > blockedDeadLine[_to]
            && _to != address(this)
            && balances[_from] >= _value
            && balances[_to] + _value > balances[_to]) {

            addHolder(_to);
            balances[_from] -= _value;
            balances[_to] += _value;
            Transfer(_from, _to, _value);
            return true;

        } else return false;
    }
    
    function transfer(address _to, uint256 _value) returns (bool success) {  
        if (!transferFromTo(msg.sender, _to, _value)) throw;
        return true;
    }

    function transferFrom(
        address _from, 
        address _to, 
        uint256 _value
        ) returns (bool success) { 
        
        if (allowed[_from][msg.sender] < _value
            || !transferFromTo(_from, _to, _value)) throw;
            
        allowed[_from][msg.sender] -= _value;
        return true;
    }

    function approve(address _spender, uint256 _value) returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        return true;
    }
    
}
function initialTokenSupply(
        address _recipient, 
        uint _quantity,
        bool _last) returns (bool success) {

        if (initialTokenSupplyDone) throw;
        
        addHolder(_recipient);
        if (_recipient != 0 && _quantity != 0) createTokens(_recipient, _quantity);
        
        if (_last) initialTokenSupplyDone = true;
        
        return true;
    }
// Setting functions