contract tokenswap is multisig, multiowned {
    Token public tokenCtr;
    bool public tokenSwap;
    uint public constant PRESALE_LENGTH = 3 days;
    uint public constant TRANSITION_WINDOW = 3 hours; // We will turn on tokenSwap in this period and it will 120 FYN / ETH
    uint public constant SWAP_LENGTH = PRESALE_LENGTH + TRANSITION_WINDOW + 6 weeks + 6 days + 3 hours;
    uint public constant MAX_ETH = 75000 ether; // Hard cap, capped otherwise by total tokens sold (max 7.5M FYN)
    uint public amountRaised;

    modifier isUnderPresaleMinimum {
        if (tokenCtr.creationTime() + PRESALE_LENGTH > now) {
            if (msg.value < 20 ether) throw;
        }
        _;
    }

    modifier isZeroValue {
        if (msg.value == 0) throw;
        _;
    }

    modifier isOverCap {
    	if (amountRaised + msg.value > MAX_ETH) throw;
        _;
    }

    modifier isOverTokenCap {
        if (!safeToMultiply(tokenCtr.currentSwapRate(), msg.value)) throw;
        uint tokensAmount = tokenCtr.currentSwapRate() * msg.value;
        if(!safeToAdd(tokenCtr.totalSupply(),tokensAmount)) throw;
        if (tokenCtr.totalSupply() + tokensAmount > tokenCtr.tokenCap()) throw;
        _;

    }

    modifier isSwapStopped {
        if (!tokenSwap) throw;
        _;
    }

    modifier areConditionsSatisfied {
        _;
        // End token swap if sale period ended
        // We can't throw to reverse the amount sent in or we will lose state
        // , so we will accept it even though if it is after crowdsale
        if (tokenCtr.creationTime() + SWAP_LENGTH < now) {
            tokenCtr.disableTokenSwapLock();
            tokenSwap = false;
        }
        // Check if cap has been reached in this tx
        if (amountRaised == MAX_ETH) {
            tokenCtr.disableTokenSwapLock();
            tokenSwap = false;
        }

        // Check if token cap has been reach in this tx
        if (tokenCtr.totalSupply() == tokenCtr.tokenCap()) {
            tokenCtr.disableTokenSwapLock();
            tokenSwap = false;
        }
    }

    // A helper to notify if overflow occurs for addition
    function safeToAdd(uint a, uint b) private constant returns (bool) {
      return (a + b >= a && a + b >= b);
    }
  
    // A helper to notify if overflow occurs for multiplication
    function safeToMultiply(uint _a, uint _b) private constant returns (bool) {
      return (_b == 0 || ((_a * _b) / _b) == _a);
    }


    function startTokenSwap() onlyowner {
        tokenSwap = true;
    }

    function stopTokenSwap() onlyowner {
        tokenSwap = false;
    }

    function setTokenContract(address newTokenContractAddr) onlyowner {
        if (newTokenContractAddr == address(0x0)) throw;
        // Allow setting only once
        if (tokenCtr != address(0x0)) throw;

        tokenCtr = Token(newTokenContractAddr);
    }

    function buyTokens(address _beneficiary)
    payable
    isUnderPresaleMinimum
    isZeroValue
    isOverCap
    isOverTokenCap
    isSwapStopped
    areConditionsSatisfied {
        Deposit(msg.sender, msg.value);
        tokenCtr.mintTokens(_beneficiary, msg.value);
        if (!safeToAdd(amountRaised, msg.value)) throw;
        amountRaised += msg.value;
    }

    function withdrawReserve(address _beneficiary) onlyowner {
	    if (tokenCtr.creationTime() + SWAP_LENGTH < now) {
            tokenCtr.mintReserve(_beneficiary);
        }
    } 
}
function safeToAdd(uint a, uint b) private constant returns (bool) {
  return (a + b >= a && a + b >= b);
}

// A helper to notify if overflow occurs for addition