additionally store information on every token redeemer in a seperate struct mapping identified by index

pragma solidity ^0.4.17;

import "./StandardToken.sol";

/**
 * @title The Pre-Leo Token contract.
 *
 * Credit: Taking ideas from BAT token and NET token
 */
contract PXLToken is StandardToken {

    // Token metadata
    string public constant name = "Pre-Leo Token";
    string public constant symbol = "PXLT";
    uint256 public constant decimals = 18;
    string public constant version = "0.9";

    // Fundraising parameters
    enum ContractState { Fundraising, Finalized, Redeeming, Paused }
    struct redeemer {                                // pair ETH address with LEO Address)
        address ethAddress;
        string leoAddress;
    }
    mapping (uint256 => redeemer) public redeemers;  // pair index with user information

    ContractState public state;           // Current state of the contract
    ContractState private savedState;     // State of the contract before pause

    uint256 public fundingStartBlock;        // These two blocks need to be chosen to comply with the
    uint256 public fundingEndBlock;          // start date and 28 day duration requirements
    uint256 public exchangeRateChangesBlock; // block number that triggers the exchange rate change
    uint256 public redeemerCount;            // index counting redeemers

    address public admin1; // First administrator for multi-sig mechanism
    address public admin2; // Second administrator for multi-sig mechanism

    uint256 public constant TOKEN_FIRST_EXCHANGE_RATE = 200; // 200 PXLTs per 1 ETH
    uint256 public constant TOKEN_SECOND_EXCHANGE_RATE = 175; // 175 PXLTs per 1 ETH
    uint256 public constant TOKEN_CREATION_CAP = 10 * (10**6) * 10**decimals; // 10 million PXLTs
    uint256 public constant ETH_RECEIVED_CAP = 50 * (10**3) * 10**decimals; // 50 000 ETH
    uint256 public constant ETH_RECEIVED_MIN = 10 * (10**3) * 10**decimals; // 10 000 ETH
    uint256 public constant TOKEN_MIN = 1 * 10**decimals; // 1 PXLT

    // We need to keep track of how much ether have been contributed, since we have a cap for ETH too
    uint256 public totalReceivedEth = 0;

    // Since we have different exchange rates at different stages, we need to keep track
    // of how much ether each contributed in case that we need to issue a refund
    mapping (address => uint256) private ethBalances;
    mapping (address => bytes32) private multiSigHashes; // store the hashes of admins' msg.data

    // Events used for logging
    event LogRefund(address indexed _to, uint256 _value);
    event LogCreatePXLT(address indexed _to, uint256 _value);
    event LogRedeemPXLT(address indexed _from, uint256 _value, string _leoAddress);

    modifier isFinalized() {
        require(state == ContractState.Finalized);
        _;
    }

    modifier isFundraising() {
        require(state == ContractState.Fundraising);
        _;
    }

    modifier isRedeeming() {
        require(state == ContractState.Redeeming);
        _;
    }

    modifier isPaused() {
        require(state == ContractState.Paused);
        _;
    }

    modifier notPaused() {
        require(state != ContractState.Paused);
        _;
    }

    modifier isFundraisingIgnorePaused() {
        require(state == ContractState.Fundraising || (state == ContractState.Paused && savedState == ContractState.Fundraising));
        _;
    }

    modifier onlyOwner() {
        // check if transaction sender is admin.
        require (msg.sender == admin1 || msg.sender == admin2);
        // if yes, store his msg.data.
        multiSigHashes[msg.sender] = keccak256(msg.data);
        // check if his stored msg.data hash equals to the one of the other admin
        if ((multiSigHashes[admin1]) == (multiSigHashes[admin2])) {
            // if yes, both admins agreed - continue.
            _;

            // Reset hashes after successful execution
            multiSigHashes[admin1] = 0x0;
            multiSigHashes[admin2] = 0x0;
        } else {
            // if not (yet), return.
            return;
        }
    }

    modifier minimumReached() {
        require(totalReceivedEth >= ETH_RECEIVED_MIN);
        _;
    }

    /**
     * @dev Create a new PXLToken contract.
     *
     * @param _fundingStartBlock The starting block of the fundraiser (has to be in the future).
     * @param _fundingEndBlock The end block of the fundraiser (has to be after _fundingStartBlock).
     * @param _exchangeRateChangesBlock The block that changes the exchange rate (has to be between _fundingStartBlock and _fundingEndBlock).
     * @param _admin1 The first admin account that owns this contract.
     * @param _admin2 The second admin account that owns this contract.
     */
    function PXLToken(
        uint256 _fundingStartBlock,
        uint256 _fundingEndBlock,
        uint256 _exchangeRateChangesBlock,
        address _admin1,
        address _admin2)
    public
    {
        // Check that the parameters make sense
        require(block.number <= _fundingStartBlock); // The start of the fundraising should happen in the future
        require(_fundingStartBlock <= _exchangeRateChangesBlock); // The exchange rate change should happen after the start of the fundraising
        require(_exchangeRateChangesBlock <= _fundingEndBlock); // And the end of the fundraising should happen after the exchange rate change
        require (_admin1 != 0x0); // admin1 address must be set
        require (_admin2 != 0x0); // admin2 address must be set
        require (_admin1 != _admin2); // Ensure that admin accounts are different

        // Contract state
        state = ContractState.Fundraising;
        savedState = ContractState.Fundraising;
        fundingStartBlock = _fundingStartBlock;
        fundingEndBlock = _fundingEndBlock;
        exchangeRateChangesBlock = _exchangeRateChangesBlock;
        totalSupply = 0;

        admin1 = _admin1;
        admin2 = _admin2;
    }

    // Overridden method to check for end of fundraising before allowing transfer of tokens
    function transfer(address _to, uint256 _value)
    public
    isFinalized // Only allow token transfer after the fundraising has ended
    onlyPayloadSize(2)
    returns (bool success)
    {
        return super.transfer(_to, _value);
    }

    // Overridden method to check for end of fundraising before allowing transfer of tokens
    function transferFrom(address _from, address _to, uint256 _value)
    public
    isFinalized // Only allow token transfer after the fundraising has ended
    onlyPayloadSize(3)
    returns (bool success)
    {
        return super.transferFrom(_from, _to, _value);
    }

    /// @dev Accepts ether and creates new PXLT tokens
    function createTokens()
    payable
    external
    isFundraising
    {
        require(block.number >= fundingStartBlock);
        require(block.number <= fundingEndBlock);
        require(msg.value > 0);

        // First we check the ETH cap, as it's easier to calculate, return
        // the contribution if the cap has been reached already
        uint256 checkedReceivedEth = SafeMath.add(totalReceivedEth, msg.value);
        require(checkedReceivedEth <= ETH_RECEIVED_CAP);

        // If all is fine with the ETH cap, we continue to check the
        // minimum amount of tokens and the cap for how many tokens
        // have been generated so far
        uint256 tokens = SafeMath.mul(msg.value, getCurrentTokenPrice());
        require(tokens >= TOKEN_MIN);
        uint256 checkedSupply = SafeMath.add(totalSupply, tokens);
        require(checkedSupply <= TOKEN_CREATION_CAP);

        // Only when all the checks have passed, then we update the state (ethBalances,
        // totalReceivedEth, totalSupply, and balances) of the contract
        ethBalances[msg.sender] = SafeMath.add(ethBalances[msg.sender], msg.value);
        totalReceivedEth = checkedReceivedEth;
        totalSupply = checkedSupply;
        balances[msg.sender] += tokens;  // safeAdd not needed; bad semantics to use here

        // Log the creation of this tokens
        LogCreatePXLT(msg.sender, tokens);
    }

    /// @dev Returns the current token price
    function getCurrentTokenPrice()
    private
    constant
    returns (uint256 currentPrice)
    {
        if (block.number < exchangeRateChangesBlock) {
            return TOKEN_FIRST_EXCHANGE_RATE;
        } else {
            return TOKEN_SECOND_EXCHANGE_RATE;
        }
    }

    /// @dev Redeems PXLTs and records the LEO address of the sender
    function redeemTokens(string _leoAddress)
    external
    isRedeeming
    {
        uint256 PXLTVal = balances[msg.sender];
        require(PXLTVal >= TOKEN_MIN); // At least TOKEN_MIN tokens have to be redeemed

        redeemers[redeemerCount] = redeemer ({
            ethAddress : msg.sender,
            leoAddress : _leoAddress
        });

        redeemerCount++;
        // Burn Tokens on redemption
        require(super.transfer(0x0, PXLTVal));
        // Log the redeeming of this tokens
        LogRedeemPXLT(msg.sender, PXLTVal, _leoAddress);
    }

    /// @dev Allows to transfer ether from the contract as soon as the minimum is reached
    function retrieveEth(uint256 _value, address _safe)
    external
    minimumReached
    onlyOwner
    {
        require(_value <= this.balance);
        // make sure a recipient was defined !
        require (_safe != 0x0);

        // send the eth to where admins agree upon
        _safe.transfer(_value);
    }

    /// @dev Ends the fundraising period and sends the ETH to wherever the admins agree upon
    function finalize(address _safe)
    external
    isFundraising
    minimumReached
    onlyOwner  // Only the admins calling this method exactly the same way can finalize the sale.
    {
        // Only allow to finalize the contract before the ending block if we already reached any of the two caps
        require(block.number > fundingEndBlock || totalSupply >= TOKEN_CREATION_CAP || totalReceivedEth >= ETH_RECEIVED_CAP);
        // make sure a recipient was defined !
        require (_safe != 0x0);

        // Move the contract to Finalized state
        state = ContractState.Finalized;
        savedState = ContractState.Finalized;

        // Send the ETH to where admins agree upon.
        _safe.transfer(this.balance);
    }

    /// @dev Starts the redeeming period
    function startRedeeming()
    external
    isFinalized // The redeeming period can only be started after the contract is finalized
    onlyOwner   // Only both admins calling this method can initiate the redeeming period
    {
        // Move the contract to Redeeming state
        state = ContractState.Redeeming;
        savedState = ContractState.Redeeming;
    }

    /// @dev Pauses the contract
    function pause()
    external
    notPaused   // Prevent the contract getting stuck in the Paused state
    onlyOwner   // Only both admins calling this method can pause the contract
    {
        // Move the contract to Paused state
        savedState = state;
        state = ContractState.Paused;
    }

    /// @dev Proceeds with the contract
    function proceed()
    external
    isPaused
    onlyOwner   // Only both admins calling this method can proceed with the contract
    {
        // Move the contract to the previous state
        state = savedState;
    }

    /// @dev Allows contributors to recover their ether in case the minimum funding goal is not reached
    function refund()
    external
    isFundraisingIgnorePaused // Refunding is only possible in the fundraising phase (no matter if paused) by definition
    {
        require(block.number > fundingEndBlock); // Prevents refund until fundraising period is over
        require(totalReceivedEth < ETH_RECEIVED_MIN);  // No refunds if the minimum has been reached

        uint256 PXLTVal = balances[msg.sender];
        require(PXLTVal > 0);
        uint256 ethVal = ethBalances[msg.sender];
        require(ethVal > 0);

        // Update the state only after all the checks have passed
        balances[msg.sender] = 0;
        ethBalances[msg.sender] = 0;
        totalSupply = SafeMath.sub(totalSupply, PXLTVal); // Extra safe

        // Log this refund
        LogRefund(msg.sender, ethVal);

        // Send the contributions only after we have updated all the balances
        // If you're using a contract, make sure it works with .transfer() gas limits
        msg.sender.transfer(ethVal);
    }
}

pragma solidity ^0.4.17;

import "./StandardToken.sol";

/**
 * @title The Pre-Leo Token contract.
 *
 * Credit: Taking ideas from BAT token and NET token
 */
contract PXLToken is StandardToken {

    // Token metadata
    string public constant name = "Pre-Leo Token";
    string public constant symbol = "PXLT";
    uint256 public constant decimals = 18;
    string public constant version = "0.9";

    // Fundraising parameters
    enum ContractState { Fundraising, Finalized, Redeeming, Paused }
    ContractState public state;           // Current state of the contract
    ContractState private savedState;     // State of the contract before pause

    uint256 public fundingStartBlock;        // These two blocks need to be chosen to comply with the
    uint256 public fundingEndBlock;          // start date and 28 day duration requirements
    uint256 public exchangeRateChangesBlock; // block number that triggers the exchange rate change

    address public admin1; // First administrator for multi-sig mechanism
    address public admin2; // Second administrator for multi-sig mechanism

    uint256 public constant TOKEN_FIRST_EXCHANGE_RATE = 200; // 200 PXLTs per 1 ETH
    uint256 public constant TOKEN_SECOND_EXCHANGE_RATE = 175; // 175 PXLTs per 1 ETH
    uint256 public constant TOKEN_CREATION_CAP = 10 * (10**6) * 10**decimals; // 10 million PXLTs
    uint256 public constant ETH_RECEIVED_CAP = 50 * (10**3) * 10**decimals; // 50 000 ETH
    uint256 public constant ETH_RECEIVED_MIN = 10 * (10**3) * 10**decimals; // 10 000 ETH
    uint256 public constant TOKEN_MIN = 1 * 10**decimals; // 1 PXLT

    // We need to keep track of how much ether have been contributed, since we have a cap for ETH too
    uint256 public totalReceivedEth = 0;

    // Since we have different exchange rates at different stages, we need to keep track
    // of how much ether each contributed in case that we need to issue a refund
    mapping (address => uint256) private ethBalances;
    mapping (address => bytes32) private multiSigHashes; // store the hashes of admins' msg.data

    // Events used for logging
    event LogRefund(address indexed _to, uint256 _value);
    event LogCreatePXLT(address indexed _to, uint256 _value);
    event LogRedeemPXLT(address indexed _from, uint256 _value, string _leoAddress);

    modifier isFinalized() {
        require(state == ContractState.Finalized);
        _;
    }

    modifier isFundraising() {
        require(state == ContractState.Fundraising);
        _;
    }

    modifier isRedeeming() {
        require(state == ContractState.Redeeming);
        _;
    }

    modifier isPaused() {
        require(state == ContractState.Paused);
        _;
    }

    modifier notPaused() {
        require(state != ContractState.Paused);
        _;
    }

    modifier isFundraisingIgnorePaused() {
        require(state == ContractState.Fundraising || (state == ContractState.Paused && savedState == ContractState.Fundraising));
        _;
    }

    modifier onlyOwner() {
        // check if transaction sender is admin.
        require (msg.sender == admin1 || msg.sender == admin2);
        // if yes, store his msg.data.
        multiSigHashes[msg.sender] = keccak256(msg.data);
        // check if his stored msg.data hash equals to the one of the other admin
        if ((multiSigHashes[admin1]) == (multiSigHashes[admin2])) {
            // if yes, both admins agreed - continue.
            _;

            // Reset hashes after successful execution
            multiSigHashes[admin1] = 0x0;
            multiSigHashes[admin2] = 0x0;
        } else {
            // if not (yet), return.
            return;
        }
    }

    modifier minimumReached() {
        require(totalReceivedEth >= ETH_RECEIVED_MIN);
        _;
    }

    /**
     * @dev Create a new PXLToken contract.
     *
     * @param _fundingStartBlock The starting block of the fundraiser (has to be in the future).
     * @param _fundingEndBlock The end block of the fundraiser (has to be after _fundingStartBlock).
     * @param _exchangeRateChangesBlock The block that changes the exchange rate (has to be between _fundingStartBlock and _fundingEndBlock).
     * @param _admin1 The first admin account that owns this contract.
     * @param _admin2 The second admin account that owns this contract.
     */
    function PXLToken(
        uint256 _fundingStartBlock,
        uint256 _fundingEndBlock,
        uint256 _exchangeRateChangesBlock,
        address _admin1,
        address _admin2)
    public
    {
        // Check that the parameters make sense
        require(block.number <= _fundingStartBlock); // The start of the fundraising should happen in the future
        require(_fundingStartBlock <= _exchangeRateChangesBlock); // The exchange rate change should happen after the start of the fundraising
        require(_exchangeRateChangesBlock <= _fundingEndBlock); // And the end of the fundraising should happen after the exchange rate change
        require (_admin1 != 0x0); // admin1 address must be set
        require (_admin2 != 0x0); // admin2 address must be set
        require (_admin1 != _admin2); // Ensure that admin accounts are different

        // Contract state
        state = ContractState.Fundraising;
        savedState = ContractState.Fundraising;
        fundingStartBlock = _fundingStartBlock;
        fundingEndBlock = _fundingEndBlock;
        exchangeRateChangesBlock = _exchangeRateChangesBlock;
        totalSupply = 0;

        admin1 = _admin1;
        admin2 = _admin2;
    }

    // Overridden method to check for end of fundraising before allowing transfer of tokens
    function transfer(address _to, uint256 _value)
    public
    isFinalized // Only allow token transfer after the fundraising has ended
    onlyPayloadSize(2)
    returns (bool success)
    {
        return super.transfer(_to, _value);
    }

    // Overridden method to check for end of fundraising before allowing transfer of tokens
    function transferFrom(address _from, address _to, uint256 _value)
    public
    isFinalized // Only allow token transfer after the fundraising has ended
    onlyPayloadSize(3)
    returns (bool success)
    {
        return super.transferFrom(_from, _to, _value);
    }

    /// @dev Accepts ether and creates new PXLT tokens
    function createTokens()
    payable
    external
    isFundraising
    {
        require(block.number >= fundingStartBlock);
        require(block.number <= fundingEndBlock);
        require(msg.value > 0);

        // First we check the ETH cap, as it's easier to calculate, return
        // the contribution if the cap has been reached already
        uint256 checkedReceivedEth = SafeMath.add(totalReceivedEth, msg.value);
        require(checkedReceivedEth <= ETH_RECEIVED_CAP);

        // If all is fine with the ETH cap, we continue to check the
        // minimum amount of tokens and the cap for how many tokens
        // have been generated so far
        uint256 tokens = SafeMath.mul(msg.value, getCurrentTokenPrice());
        require(tokens >= TOKEN_MIN);
        uint256 checkedSupply = SafeMath.add(totalSupply, tokens);
        require(checkedSupply <= TOKEN_CREATION_CAP);

        // Only when all the checks have passed, then we update the state (ethBalances,
        // totalReceivedEth, totalSupply, and balances) of the contract
        ethBalances[msg.sender] = SafeMath.add(ethBalances[msg.sender], msg.value);
        totalReceivedEth = checkedReceivedEth;
        totalSupply = checkedSupply;
        balances[msg.sender] += tokens;  // safeAdd not needed; bad semantics to use here

        // Log the creation of this tokens
        LogCreatePXLT(msg.sender, tokens);
    }

    /// @dev Returns the current token price
    function getCurrentTokenPrice()
    private
    constant
    returns (uint256 currentPrice)
    {
        if (block.number < exchangeRateChangesBlock) {
            return TOKEN_FIRST_EXCHANGE_RATE;
        } else {
            return TOKEN_SECOND_EXCHANGE_RATE;
        }
    }

    /// @dev Redeems PXLTs and records the LEO address of the sender
    function redeemTokens(string leoAddress)
    external
    isRedeeming
    {
        uint256 PXLTVal = balances[msg.sender];
        require(PXLTVal >= TOKEN_MIN); // At least TOKEN_MIN tokens have to be redeemed

        // Burn Tokens on redemption
        require(super.transfer(0x0, PXLTVal));
        // Log the redeeming of this tokens
        LogRedeemPXLT(msg.sender, PXLTVal, leoAddress);
    }

    /// @dev Allows to transfer ether from the contract as soon as the minimum is reached
    function retrieveEth(uint256 _value, address _safe)
    external
    minimumReached
    onlyOwner
    {
        require(_value <= this.balance);
        // make sure a recipient was defined !
        require (_safe != 0x0);

        // send the eth to where admins agree upon
        _safe.transfer(_value);
    }

    /// @dev Ends the fundraising period and sends the ETH to wherever the admins agree upon
    function finalize(address _safe)
    external
    isFundraising
    minimumReached
    onlyOwner  // Only the admins calling this method exactly the same way can finalize the sale.
    {
        // Only allow to finalize the contract before the ending block if we already reached any of the two caps
        require(block.number > fundingEndBlock || totalSupply >= TOKEN_CREATION_CAP || totalReceivedEth >= ETH_RECEIVED_CAP);
        // make sure a recipient was defined !
        require (_safe != 0x0);

        // Move the contract to Finalized state
        state = ContractState.Finalized;
        savedState = ContractState.Finalized;

        // Send the ETH to where admins agree upon.
        _safe.transfer(this.balance);
    }

    /// @dev Starts the redeeming period
    function startRedeeming()
    external
    isFinalized // The redeeming period can only be started after the contract is finalized
    onlyOwner   // Only both admins calling this method can initiate the redeeming period
    {
        // Move the contract to Redeeming state
        state = ContractState.Redeeming;
        savedState = ContractState.Redeeming;
    }

    /// @dev Pauses the contract
    function pause()
    external
    notPaused   // Prevent the contract getting stuck in the Paused state
    onlyOwner   // Only both admins calling this method can pause the contract
    {
        // Move the contract to Paused state
        savedState = state;
        state = ContractState.Paused;
    }

    /// @dev Proceeds with the contract
    function proceed()
    external
    isPaused
    onlyOwner   // Only both admins calling this method can proceed with the contract
    {
        // Move the contract to the previous state
        state = savedState;
    }

    /// @dev Allows contributors to recover their ether in case the minimum funding goal is not reached
    function refund()
    external
    isFundraisingIgnorePaused // Refunding is only possible in the fundraising phase (no matter if paused) by definition
    {
        require(block.number > fundingEndBlock); // Prevents refund until fundraising period is over
        require(totalReceivedEth < ETH_RECEIVED_MIN);  // No refunds if the minimum has been reached

        uint256 PXLTVal = balances[msg.sender];
        require(PXLTVal > 0);
        uint256 ethVal = ethBalances[msg.sender];
        require(ethVal > 0);

        // Update the state only after all the checks have passed
        balances[msg.sender] = 0;
        ethBalances[msg.sender] = 0;
        totalSupply = SafeMath.sub(totalSupply, PXLTVal); // Extra safe

        // Log this refund
        LogRefund(msg.sender, ethVal);

        // Send the contributions only after we have updated all the balances
        // If you're using a contract, make sure it works with .transfer() gas limits
        msg.sender.transfer(ethVal);
    }
}


Sorry. Fixed variables names and added assessment contracts

contract Master
{
  //Initialize master data store
  mapping (address => uint) tokenBalance;
  mapping (address => string) tagName;
  mapping (string => address) tagAddressFromName;
  mapping (address => address[]) achievements;
  mapping (address => bool) availability;
  mapping (address => address) users;

  function Master()
  {
    tokenBalance[address(this)] = 1;
    address[] a;
    uint useless = addTag("account", a);
  }

  function mapTokenBalance(address user, uint balance)
  {
    tokenBalance[user] = balance;
  }
  function mapTagName(address tagAddress, string name)
  {
    tagName[tagAddress] = name;
  }
  function mapTagAddressFromName(string name, address tagAddress)
  {
    tagAddressFromName[name] = tagAddress;
  }
  function mapAchievement(address user, address achievment)
  {
    achievements[user].push(achievment);
  }
  function mapAvailability(address user, bool availability)
  {
    availability[user] = availability;
  }

  function getTokenBalance(address user) returns(uint)
  {
    return tokenBalance[user];
  }
  function getTagName(address tagAddress) returns(string)
  {
    return tagName[tagAddress];
  }
  function getTagAddressFromName(string name) returns(address)
  {
    return tagAddressFromName[name];
  }
  function getAchievement(address user) returns(address[])
  {
    return achievements[user];
  }
  function getNumberOfachievments(address user) returns(uint)
  {
    return achievments[user].length;
  }
  function getAvailability(address user) returns(bool)
  {
    return availability[user] = true;
  }

  function addUser()
  {
    User newUser = new User(msg.sender, address(this));
    Tag(tagAddressFromName["account"]).startAssessment(address(newUser),5);
  }

  function addTag(string name, address[] parentList) returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] parents;
    if(tokenBalance[msg.sender] < 1)
    {
      response += 1;
    }
    if(tagAddressFromName[name] == 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i<= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents.push(parentList[i]);
        }
      }
      Tag newTag = new Tag(name, parents, address(this));
      address newTagAddress = address(newTag);
      tagName[newTagAddress] = name;
      tagAddressFromName[name] = newTagAddress;
      tokenBalance[msg.sender] -= 1;
    }
    return response;
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parentTags;
  address master;
  string name;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores

  function Tag(string tagName, address[] parents, address masterAddress)
  {
    name = tagName;
    parentTags = parents;
    master = masterAddress;
  }

  function setAssessorPool(address tagAddress, address assessment)
  {
    for(uint i = 0; i < Tag(tagAddress).owners.length && Assessment(assessment).poolSizeRemaining != 0; i++)
    {
      if(pool.length < .1*Tag(tagAddress).owners.length)
      {
        address random = Tag(tagAddress).owners[getRandom(Tag(tagAddress).owners.length)];
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool();
          Assessment(assessment).poolSizeRemaining--;
        }
      }
      else
      {
        for(uint j = 0; j < Tag(tagAddress).parentTags.length; j++)
        {
          setAssessorPool(Tag(tagAddress).parentTags[j], assessment);
        }
      }
      if(Assessment(assessment).poolSizeRemaining =< 0)
      {
        Assessment(assessment).setAssessors();
      }
    }
  }

  function startAssessment(address assessee, uint size)
  {
    Assessment newAssessment = new Assessment(assessee, address.this);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20)
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool result, uint score, address assessee, address assessment) returns(bool)
  {
    if(result == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapAchievement(assessee,address(this));
    }
    assessmentHistory[assessee].push(assessment);
    return result;
  }
  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  address[] assessors;
  address tag;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  struct Results
  {
    bool pass;
    uint score;
  }
  mapping(address => string) assessmentQuestions; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentAnswers; //Given by the assessee as IPFS hashes
  mapping(address => Results) assessmentResults; //Pass/Fail and Score given by assessors
  uint finalScore;
  bool finalResult;

  function Assessment(address assesseeAddress, address tagAddress)
  {
    assessee = assesseeAddress;
    tag = tagAddress;
  }

  function setNumberOfAssessors(uint number)
  {
    numberOfAssessors = number;
  }

  function setAssessmentPoolSize(uint sizeRemaining)
  {
    poolSizeRemaining = sizeRemaining;
  }
  function addToAssessorPool(address potentialAddress)
  {
    assessorPool.push(potentialAddress);
  }

  function setAssessors()
  {
    for(uint i = 0, i < numberOfAssessors, i++)
    {
      assessors.push(assessorPool[getRandom(assessorPool.length)]);
    }
  }
<<<<<<< HEAD

  function setQuestion(address assessorAddress, uint[] data)
=======
  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
  function setQuestion(address a, uint[] data)
>>>>>>> 61c1a726e1d14df3ac21e8924e1029ac230d9559
  {
    assessmentQuestions[assessorAddress] = data;
  }

  function getQuestion(address assessorAddress) returns(uint[])
  {
    return assessmentQuestions[assessorAddress];
  }

  function setAnswer(address assesseeAddress, uint[] data)
  {
    assessmentAnswers[assesseeAddress] = data;
  }

  function getAnswer(address assesseeAddress) returns(uint[])
  {
    return assessmentAnswers[assesseeAddress];
  }

  function setResult(address assessorAddress, bool pass, uint score)
  {
    Results memory results;
    results.pass = pass;
    results.score = score;
    assessmentResults[a] = results;
  }

  function assess()
  {

  }

  function calculateResult()
  {

  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function cashout()
  {

  }
}

contract User
{
  address user;
  address master;
  address[] acheivements;
  string userData;

<<<<<<< HEAD
  function User(address userAddress, address masterAddress, string ipfsHash)
  {
    user = userAddress;
    master = masterAddress;
    userData = ipfsHash;
  }

=======
  function User(address u, address m)
  {
    user = u;
    master = m;
  }

  function setUserData(string hash)
  {
    userData hash;
  }

  function getUserData() returns(string)
  {
      return userData;
  }
>>>>>>> 61c1a726e1d14df3ac21e8924e1029ac230d9559
}

contract Master
{
  //Initialize master data store
  mapping (address => uint) tokenBalance;
  mapping (address => string) tagName;
  mapping (string => address) tagAddressFromName;
  mapping (address => address[]) achievements;
  mapping (address => bool) availability;
  mapping (address => address) users;

  function Master()
  {

  }

  function mapTokenBalance(address a, uint b)
  {
    tokenBalance[a] = b;
  }
  function mapTagName(address a, string n)
  {
    tagName[a] = n;
  }
  function mapTagAddressFromName(string n, address a)
  {
    tagAddressFromName[n] = a;
  }
  function mapAchievement(address a, address t)
  {
    achievements[a].push(t);
  }
  function mapAvailability(address a, bool b)
  {
    availability[a] = b;
  }

  function getTokenBalance(address a) returns(uint)
  {
    return tokenBalance[a];
  }
  function getTagName(address a) returns(string)
  {
    return tagName[a];
  }
  function getTagAddressFromName(string n) returns(address)
  {
    return tagAddressFromName[n];
  }
  function getAchievement(address a) returns(address[])
  {
    return achievements[a];
  }
  function getNumberOfachievments(address a) returns(uint)
  {
    return achievments[a].length;
  }
  function getAvailability(address a) returns(bool)
  {
    return availability[a] = true;
  }

  function addTag(string name, address[] parentList) returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] parents;
    if(tokenBalance[msg.sender] < 1)
    {
      response += 1;
    }
    if(tagAddressFromName[name] == 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i<= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents.push(parentList[i]);
        }
      }
      Tag newTag = new Tag(name, parents, address(this));
      address newTagAddress = address(newTag);
      tagName[newTagAddress] = name;
      tagAddressFromName[name] = newTagAddress;

    }
    return response;
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parentTags;
  address master;
  string name;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores

  function Tag(string n, address[] p, address m)
  {
    name = n;
    parentTags = p;
    master = m;
  }

  function setAssessorPool(address a, address assessment)
  {
    for(uint i = 0; i < Tag(a).owners.length && Assessment(assessment).poolSizeRemaining != 0; i++)
    {
      if(pool.length < .1*Tag(a).owners.length)
      {
        address random = Tag(a).owners[getRandom(Tag(a).owners.length)];
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool();
          Assessment(assessment).poolSizeRemaining--;
        }
      }
      else
      {
        for(uint j = 0; j < Tag(a).parentTags.length; j++)
        {
          setAssessorPool(s, Tag(a).parentTags[j], assessment);
        }
      }
      if(Assessment(assessment).poolSizeRemaining =< 0)
      {
        Assessment(assessment).setAssessors();
      }
    }
  }

  function startAssessment(address assessee, uint size)
  {
    Assessment newAssessment = new Assessment(assessee, address.this);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20)
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool result, uint score, address assessee, address assessment) returns(bool)
  {
    if(result == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapachievment(assessee,address(this));
    }
    assessmentHistory[assessee].push(assessment);
    return result;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  address[] assessors;
  address tag;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  struct Results
  {
    bool pass;
    uint score;
  }
  mapping(address => string) assessmentQuestions; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentAnswers; //Given by the assessee as IPFS hashes
  mapping(address => Results) assessmentResults; //Pass/Fail and Score given by assessors
  uint finalScore;
  bool finalResult;

  function Assessment(address a, address t)
  {
    assessee = a;
    tag = t;
  }

  function setNumberOfAssessors(uint n)
  {
    numberOfAssessors = n;
  }

  function setAssessmentPoolSize(uint n)
  {
    poolSizeRemaining = n;
  }
  function addToAssessorPool(address a)
  {
    assessorPool.push(a);
  }

  function setAssessors()
  {
    for(uint i = 0, i < n, i++)
    {
      assessors.push(assessorPool[getRandom(assessorPool.length)]);
    }
  }

  function setQuestion(address a, uint[] data)
  {
    assessmentQuestions[a] = data;
  }

  function getQuestion(address a) returns(uint[])
  {
    return assessmentQuestions[a];
  }

  function setAnswer(address a, uint[] data)
  {
    assessmentAnswers[a] = data;
  }

  function getAnswer(address a) returns(uint[])
  {
    return assessmentAnswers[a];
  }

  function setResult(address a, bool b, uint s)
  {
    Results memory results;
    results.pass = b;
    results.score = s;
    assessmentResults[a] = results;
  }

  function assess()
  {

  }

  function calculateResult()
  {

  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function cashout()
  {

  }
}

contract User
{
  address user;
  uint balance;
  address master;
  address[] acheivements;
  string userData;

  function User(address u, address m, string s)
  {
    user = u;
    master = m;
    userData = s;
  }

}


Try again...

Try again...

pragma solidity ^0.4.11;

import "MiniMeToken.sol";

contract ERC20Token {
    function transfer(address _to, uint256 _value) returns (bool success);
    function balanceOf(address _owner) constant returns (uint256 balance);
}

contract Owned {
    address public owner;

    function Owned() {
        owner = msg.sender;
    }

    function transferOwnership (address _newOwner) onlyOwner {
        owner = _newOwner;
    }

    modifier onlyOwner() {
        if(msg.sender != owner) throw;
        _;
    }
}

/*
    Copyright 2017, Joseph Reed

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/// @title MeDao Contract
/// @author Joseph Reed
/// @dev A MeDao aims to let anyone issue time shares for themselves. The issuer
///      should talk to shareholders for advice on how to maximize value for
///      themselves and for shareholders. Up to 40 hours worth of time are
///      auctioned off each week. The Owner of the MeDao is expected to burn
///      a certain amount of tokens each week negotiated between the Owner
///      and the Shareholders and thus will be diffrent for every MeDao. For
///      more info go to <github link here>.

contract Tokenized is Owned {

    MiniMeToken public Token;
    address public withdraw_address;

    function setupToken (
        address withdrawAddress,
        MiniMeToken CloneableToken,
        string _tokenName,
        uint8 _decimals,
        string _symbol,
        uint _snapshotBlock,
        bool _transfersEnabled
    ) onlyOwner {
        withdraw_address = withdrawAddress;
        address token = CloneableToken.createCloneToken(
            _tokenName,
            _decimals,
            _symbol,
            _snapshotBlock,
            _transfersEnabled
        );

        Token = MiniMeToken(token);
    }

    function withdrawEther () onlyOwner {
        var balance = this.balance;
        withdraw_address.transfer(balance);

        WithdrawEther_event(withdraw_address,balance);
    }

    function withdrawERC20Token (address Token) onlyOwner {
        uint tokenBalance = ERC20Token(Token).balanceOf(this);
        ERC20Token(Token).transfer(withdraw_address,tokenBalance);

        WithdrawToken_event(withdraw_address,Token,tokenBalance);
    }

    function updateDepositAddress (address _newDepositAddress) onlyOwner {
        withdraw_address = _newDepositAddress;

        NewWithdrawAddress_event(withdraw_address);
    }

    function () payable {
        withdraw_address.transfer(msg.value);

        Payable_event(msg.sender,msg.value);
    }

    modifier onlyTokenHolders (uint _minimum_needed) {
        uint tokens = Token.balanceOf(msg.sender);
        if(tokens < _minimum_needed) throw;
        _;
    }

    event Payable_event(address sender, uint amount);
    event WithdrawEther_event(address withdrawAddress, uint amount);
    event WithdrawToken_event(address withdrawAddress, address token, uint amount);
    event NewWithdrawAddress_event(address withdraw_address);
}

contract OngoingAuction is Owned {

    uint public current_bids_open;
    uint public total_bids_made;
    mapping (uint => Bid) public bids;
    mapping (address => Bidder) public bidders;

    uint public top_teir;
    uint public bottom_teir;
    uint public total_teirs;
    mapping (uint => Teir) public teirs; //DLL

////////////////
// Auction Functions
////////////////

    function placeBid (uint touchingTeir) payable {
        if(msg.value == 0) throw;

        if(teirs[msg.value].line_length == 0) {
            if(teirs[touchingTeir].line_length == 0 && top_teir != 0) throw;

            addNewTeir_internal(touchingTeir);
        }

        addNewBid_internal();
    }

    function removeBid (uint bid_id) {
        if (msg.sender != bids[bid_id].owner) throw;

        removeBid_internal(bid_id);
        bids[bid_id].cancelled = true;

        if (!bids[bid_id].owner.send(bids[bid_id].value)) throw;
    }

    function acceptHighestBid (address deposit_address) onlyOwner returns (uint) {
        if(top_teir == 0) throw;

        uint winning_bid_id = teirs[top_teir].front_of_line_id;
        bids[winning_bid_id].accepted = true;
        removeBid_internal(winning_bid_id);

        uint bidValue = bids[winning_bid_id].value;
        deposit_address.transfer(bidValue);

        return winning_bid_id;
    }

    function getBids (address bidder) constant returns (uint[]) {
        uint total_bids = bidders[bidder].total_bids_made;
        uint[] memory all_bids = new uint[](total_bids);

        for(uint i = 0; i < total_bids; i++)
            all_bids[i] = bidders[bidder].bids[i+1];

        return all_bids;
    }

    function getTeirs () constant returns (uint[]) {
        uint[] memory all_teirs = new uint[](total_teirs);
        uint current_teir = teirs[top_teir].value;
        for (uint i = 0; i < total_teirs; i++) {
            all_teirs[i] = current_teir;
            current_teir = teirs[current_teir].teir_below;
        }

        return all_teirs;
    }

    function getBidOwner (uint bid_id) constant returns (address) {
        return bids[bid_id].owner;
    }

    function getBidValue (uint bid_id) constant returns (uint) {
        return bids[bid_id].value;
    }

////////////////
// Internal Functions
////////////////

    function addNewTeir_internal (uint touching) internal {
        uint value = msg.value;

        if (top_teir == 0) {
            teirs[value] = Teir(0,value,0,0,0,0);
            top_teir = value;
            bottom_teir = value;
        } else if (value > top_teir && touching == top_teir) {
            teirs[value] = Teir(0,value,top_teir,0,0,0);
            teirs[top_teir].teir_above = value;
            top_teir = value;
        } else if (value < bottom_teir && touching == bottom_teir) {
            teirs[value] = Teir(bottom_teir,value,0,0,0,0);
            teirs[bottom_teir].teir_below = value;
            bottom_teir = value;
        } else {
            uint above;
            uint below;
            if(value > touching) {
                above = teirs[touching].teir_above;
                below = touching;
                if(value > above) throw;
            } else if(value < touching) {
                above = touching;
                below = teirs[touching].teir_below;
                if(value < below) throw;
            }

            teirs[value] = Teir(above,value,below,0,0,0);
            teirs[above].teir_below = value;
            teirs[below].teir_above = value;
        }

        total_teirs++;
    }

    function removeTeir_internal (uint value) internal {
        uint teir_below = teirs[value].teir_below;
        uint teir_above = teirs[value].teir_above;

        if (value == top_teir && value == bottom_teir) {
            top_teir = 0;
            bottom_teir = 0;
        } else if (value == top_teir) {
            teirs[teir_below].teir_above = 0;
            top_teir = teir_below;
        } else if (value == bottom_teir) {
            teirs[teir_above].teir_below = 0;
            bottom_teir = teir_above;
        } else {
            teirs[teir_above].teir_below = teir_below;
            teirs[teir_below].teir_above = teir_above;
        }

        delete teirs[value];
        total_teirs--;
    }

    function addNewBid_internal () internal {
        current_bids_open++;
        total_bids_made++;
        uint new_bid_id = total_bids_made;

        teirs[msg.value].line[new_bid_id] = SpotInLine(back_of_line_id,new_bid_id,0);

        if (teirs[msg.value].line_length == 0)
            teirs[msg.value].front_of_line_id = new_bid_id;

        uint back_of_line_id = teirs[msg.value].back_of_line_id;
        if (back_of_line_id != 0)
            teirs[msg.value].line[back_of_line_id].prev = new_bid_id;

        teirs[msg.value].back_of_line_id = new_bid_id;
        teirs[msg.value].line_length++;

        bids[new_bid_id] = Bid(new_bid_id,msg.sender,msg.value,false,false);
        bidders[msg.sender].current_bids_open++;
        bidders[msg.sender].total_bids_made++;
        uint total_bids = bidders[msg.sender].total_bids_made;
        bidders[msg.sender].bids[total_bids] = new_bid_id;

        NewBid_event(msg.sender,new_bid_id,msg.value);
    }

    function removeBid_internal (uint bid_id) internal {
        Bid bid = bids[bid_id];
        if(teirs[bid.value].line_length == 0) throw;
        if(bid.accepted || bid.cancelled) throw;

        uint front_of_line_id = teirs[bid.value].front_of_line_id;
        uint back_of_line_id = teirs[bid.value].back_of_line_id;
        if (bid.id == front_of_line_id) {
            uint prev = teirs[bid.value].line[bid.id].prev;
            teirs[bid.value].front_of_line_id = prev;
            teirs[bid.value].line[prev].prev = 0;
        } else if (bid.id == back_of_line_id) {
            uint next = teirs[bid.value].line[bid.id].next;
            teirs[bid.value].back_of_line_id = next;
            teirs[bid.value].line[next].prev = 0;
        } else {
            uint front_id = teirs[bid.value].line[bid.id].next;
            uint back_id = teirs[bid.value].line[bid.id].prev;
            teirs[bid.value].line[front_id].prev = back_id;
            teirs[bid.value].line[back_id].next = front_id;
        }

        delete teirs[bid.value].line[bid_id];
        teirs[bid.value].line_length--;

        bidders[msg.sender].current_bids_open--;
        current_bids_open--;

        if(teirs[bid.value].line_length == 0)
            removeTeir_internal(bid.value);
    }

////////////////
// Structs and Events
////////////////

    struct SpotInLine {
        uint next;
        uint bid_id;
        uint prev;
    }

    struct Bidder {
        uint current_bids_open;
        uint total_bids_made;
        mapping (uint => uint) bids;
    }

    struct Bid {
        uint id;
        address owner;
        uint value;
        bool cancelled;
        bool accepted;
    }

    struct Teir {
        uint teir_above;
        uint value;
        uint teir_below;

        uint front_of_line_id;
        uint back_of_line_id;
        uint line_length;
        mapping (uint => SpotInLine) line; //FIFO DLL
    }

    event NewBid_event(address bidder, uint bid_id, uint value);
}

contract MeDao is Tokenized {

    string public version = '0.0.2';

    address public Founder;

    OngoingAuction public Auction;

    uint public weekly_auction_reward;
    uint public scheduled_auction_timestamp;
    uint public auction_period;

    uint public total_proof_of_work;

    uint public cooldown_timestamp;
    uint burn_minimum = 1;

////////////////
// MeDao Functions
////////////////

    function MeDao (address founder, OngoingAuction auction) {
        Founder = founder;
        Auction = auction;
    }

    function startAuction () isScheduled {
        uint winning_bid_id = Auction.acceptHighestBid(withdraw_address);
        address winner = Auction.getBidOwner(winning_bid_id);
        uint winning_bid_value = Auction.getBidValue(winning_bid_id);

        Token.generateTokens(winner, 1 hours);

        AuctionWinner_event(winner,winning_bid_value);
    }

    function submitProofOfWork (uint burnAmount, string metadataHash)
    onlyTokenHolders(burn_minimum) {
        if(Token.balanceOf(msg.sender) < burnAmount) throw;

        Token.destroyTokens(msg.sender,burnAmount);
        total_proof_of_work += burnAmount;

        ProofOfWork_event(msg.sender,burnAmount,metadataHash);
    }

////////////////
// Owner Only
////////////////

    function setWeeklyAuctionReward (uint8 reward) onlyOwner
    hasCooldown(7 days) {
        if(reward > 40 hours || reward == 0) throw;

        weekly_auction_reward = reward;
        auction_period = 5 minutes; //set to 5 minutes for testing normally use:
                                    //7 days / auction_reward;
        scheduled_auction_timestamp = now + auction_period;

        NewWeeklyAuctionReward_event(weekly_auction_reward);
    }

    function setBurnMinimum (uint burnMinimum) onlyOwner {
        burn_minimum = burnMinimum;
    }

////////////////
// Events and Modifiers
////////////////

    modifier isScheduled () {
        if (scheduled_auction_timestamp == 0) throw;
        if (now < scheduled_auction_timestamp) throw;

        if(now < scheduled_auction_timestamp + auction_period)
            scheduled_auction_timestamp += auction_period;
        else
            scheduled_auction_timestamp = now + auction_period;

        _;
    }

    modifier onlyAuction () {
        if (msg.sender != address(Auction)) throw;

        _;
    }

    modifier hasCooldown (uint cooldown) {
        if(now < cooldown_timestamp) throw;

        cooldown_timestamp = now + cooldown;
        _;
    }

    event AuctionWinner_event (address winner, uint ether_bid);
    event ProofOfWork_event(address sender, uint amount, string metadataHash);
    event NewDepositAddress_event(address deposit_address);
    event NewWeeklyAuctionReward_event(uint new_auction_reward);
}

contract MeDaoRegistry {

    MiniMeToken PrimeToken;

    uint public total_medaos;
    mapping (uint => address) public founders;
    mapping (address => MeDao ) public medaos;

    function MeDaoRegistry (MiniMeToken Token) {
        PrimeToken = Token;
    }

    function register (string name) {
        if(medaos[msg.sender] != address(0x0)) throw;

        uint next_id = total_medaos++;
        founders[next_id] = msg.sender;

        OngoingAuction Auction = new OngoingAuction();

        medaos[msg.sender] = new MeDao(msg.sender,Auction);
        medaos[msg.sender].setupToken(msg.sender,PrimeToken,name,0,'seconds',0,true);
        medaos[msg.sender].transferOwnership(msg.sender);
        Auction.transferOwnership(medaos[msg.sender]);

        NewMeDao_event(msg.sender, medaos[msg.sender]);
    }

    event NewMeDao_event(address founder, address medao);
}

pragma solidity ^0.4.11;

import "MiniMeToken.sol";

contract ERC20Token {
    function transfer(address _to, uint256 _value) returns (bool success);
    function balanceOf(address _owner) constant returns (uint256 balance);
}

contract Owned {
    address public owner;

    function Owned() {
        owner = msg.sender;
    }

    function transferOwnership (address _newOwner) onlyOwner {
        owner = _newOwner;
    }

    modifier onlyOwner() {
        if(msg.sender != owner) throw;
        _;
    }
}

/*
    Copyright 2017, Joseph Reed

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/// @title MeDao Contract
/// @author Joseph Reed
/// @dev A MeDao aims to let anyone issue time shares for themselves. The issuer
///      should talk to shareholders for advice on how to maximize value for
///      themselves and for shareholders. Up to 40 hours worth of time are
///      auctioned off each week. The Owner of the MeDao is expected to burn
///      a certain amount of tokens each week negotiated between the Owner
///      and the Shareholders and thus will be diffrent for every MeDao. For
///      more info go to <github link here>.

contract Tokenized is Owned {

    MiniMeToken public Token;
    address public withdraw_address;

    function setupToken (
        address withdrawAddress,
        MiniMeToken CloneableToken,
        string _tokenName,
        uint8 _decimals,
        string _symbol,
        uint _snapshotBlock,
        bool _transfersEnabled
    ) onlyOwner {
        withdraw_address = withdrawAddress;
        address token = CloneableToken.createCloneToken(
            _tokenName,
            _decimals,
            _symbol,
            _snapshotBlock,
            _transfersEnabled
        );

        Token = MiniMeToken(token);
    }

    function withdrawEther () onlyOwner {
        var balance = this.balance;
        withdraw_address.transfer(balance);

        WithdrawEther_event(withdraw_address,balance);
    }

    function withdrawERC20Token (address Token) onlyOwner {
        uint tokenBalance = ERC20Token(Token).balanceOf(this);
        ERC20Token(Token).transfer(withdraw_address,tokenBalance);

        WithdrawToken_event(withdraw_address,Token,tokenBalance);
    }

    function updateDepositAddress (address _newDepositAddress) onlyOwner {
        withdraw_address = _newDepositAddress;

        NewWithdrawAddress_event(withdraw_address);
    }

    function () payable {
        withdraw_address.transfer(msg.value);

        Payable_event(msg.sender,msg.value);
    }

    modifier onlyTokenHolders (uint _minimum_needed) {
        uint tokens = Token.balanceOf(msg.sender);
        if(tokens < _minimum_needed) throw;
        _;
    }

    event Payable_event(address sender, uint amount);
    event WithdrawEther_event(address withdrawAddress, uint amount);
    event WithdrawToken_event(address withdrawAddress, address token, uint amount);
    event NewWithdrawAddress_event(address withdraw_address);
}

contract OngoingAuction is Owned {

    uint public current_bids_open;
    uint public total_bids_made;
    mapping (uint => Bid) public bids;
    mapping (address => Bidder) public bidders;

    uint public top_teir;
    uint public bottom_teir;
    uint public total_teirs;
    mapping (uint => Teir) public teirs; //DLL

////////////////
// Auction Functions
////////////////

    function placeBid (uint touchingTeir) payable {
        if(msg.value == 0) throw;

        if(teirs[msg.value].line_length == 0) {
            if(teirs[touchingTeir].line_length == 0 && top_teir != 0) throw;

            addNewTeir_internal(touchingTeir);
        }

        addNewBid_internal();
    }

    function removeBid (uint bid_id) {
        if (msg.sender != bids[bid_id].owner) throw;

        removeBid_internal(bid_id);
        bids[bid_id].cancelled = true;

        if (!bids[bid_id].owner.send(bids[bid_id].value)) throw;
    }

    function startAuction (address deposit_address) onlyOwner returns (uint) {
        uint winning_bid_id = acceptHighestBid();
        uint bidValue = bids[winning_bid_id].value;

        deposit_address.transfer(bidValue);

        return winning_bid_id;
    }

    function getBids (address bidder) constant returns (uint[]) {
        uint total_bids = bidders[bidder].total_bids_made;
        uint[] memory all_bids = new uint[](total_bids);

        for(uint i = 0; i < total_bids; i++)
            all_bids[i] = bidders[bidder].bids[i+1];

        return all_bids;
    }

    function getTeirs () constant returns (uint[]) {
        uint[] memory all_teirs = new uint[](total_teirs);
        uint current_teir = teirs[top_teir].value;
        for (uint i = 0; i < total_teirs; i++) {
            all_teirs[i] = current_teir;
            current_teir = teirs[current_teir].teir_below;
        }

        return all_teirs;
    }

    function getBidOwner (uint bid_id) constant returns (address) {
        return bids[bid_id].owner;
    }

    function getBidValue (uint bid_id) constant returns (uint) {
        return bids[bid_id].value;
    }

////////////////
// Internal Functions
////////////////

    function acceptHighestBid () internal returns (uint) {
        if(top_teir == 0) throw;

        uint highest_bid_id = teirs[top_teir].front_of_line_id;
        bids[highest_bid_id].accepted = true;
        removeBid_internal(highest_bid_id);

        return highest_bid_id;
    }

    function addNewTeir_internal (uint touching) internal {
        uint value = msg.value;

        if (top_teir == 0) {
            teirs[value] = Teir(0,value,0,0,0,0);
            top_teir = value;
            bottom_teir = value;
        } else if (value > top_teir && touching == top_teir) {
            teirs[value] = Teir(0,value,top_teir,0,0,0);
            teirs[top_teir].teir_above = value;
            top_teir = value;
        } else if (value < bottom_teir && touching == bottom_teir) {
            teirs[value] = Teir(bottom_teir,value,0,0,0,0);
            teirs[bottom_teir].teir_below = value;
            bottom_teir = value;
        } else {
            uint above;
            uint below;
            if(value > touching) {
                above = teirs[touching].teir_above;
                below = touching;
                if(value > above) throw;
            } else if(value < touching) {
                above = touching;
                below = teirs[touching].teir_below;
                if(value < below) throw;
            }

            teirs[value] = Teir(above,value,below,0,0,0);
            teirs[above].teir_below = value;
            teirs[below].teir_above = value;
        }

        total_teirs++;
    }

    function removeTeir_internal (uint value) internal {
        uint teir_below = teirs[value].teir_below;
        uint teir_above = teirs[value].teir_above;

        if (value == top_teir && value == bottom_teir) {
            top_teir = 0;
            bottom_teir = 0;
        } else if (value == top_teir) {
            teirs[teir_below].teir_above = 0;
            top_teir = teir_below;
        } else if (value == bottom_teir) {
            teirs[teir_above].teir_below = 0;
            bottom_teir = teir_above;
        } else {
            teirs[teir_above].teir_below = teir_below;
            teirs[teir_below].teir_above = teir_above;
        }

        delete teirs[value];
        total_teirs--;
    }

    function addNewBid_internal () internal {
        current_bids_open++;
        total_bids_made++;
        uint new_bid_id = total_bids_made;

        teirs[msg.value].line[new_bid_id] = SpotInLine(back_of_line_id,new_bid_id,0);

        if (teirs[msg.value].line_length == 0)
            teirs[msg.value].front_of_line_id = new_bid_id;

        uint back_of_line_id = teirs[msg.value].back_of_line_id;
        if (back_of_line_id != 0)
            teirs[msg.value].line[back_of_line_id].prev = new_bid_id;

        teirs[msg.value].back_of_line_id = new_bid_id;
        teirs[msg.value].line_length++;

        bids[new_bid_id] = Bid(new_bid_id,msg.sender,msg.value,false,false);
        bidders[msg.sender].current_bids_open++;
        bidders[msg.sender].total_bids_made++;
        uint total_bids = bidders[msg.sender].total_bids_made;
        bidders[msg.sender].bids[total_bids] = new_bid_id;

        NewBid_event(msg.sender,new_bid_id,msg.value);
    }

    function removeBid_internal (uint bid_id) internal {
        Bid bid = bids[bid_id];
        if(teirs[bid.value].line_length == 0) throw;
        if(bid.accepted || bid.cancelled) throw;

        uint front_of_line_id = teirs[bid.value].front_of_line_id;
        uint back_of_line_id = teirs[bid.value].back_of_line_id;
        if (bid.id == front_of_line_id) {
            uint prev = teirs[bid.value].line[bid.id].prev;
            teirs[bid.value].front_of_line_id = prev;
            teirs[bid.value].line[prev].prev = 0;
        } else if (bid.id == back_of_line_id) {
            uint next = teirs[bid.value].line[bid.id].next;
            teirs[bid.value].back_of_line_id = next;
            teirs[bid.value].line[next].prev = 0;
        } else {
            uint front_id = teirs[bid.value].line[bid.id].next;
            uint back_id = teirs[bid.value].line[bid.id].prev;
            teirs[bid.value].line[front_id].prev = back_id;
            teirs[bid.value].line[back_id].next = front_id;
        }

        delete teirs[bid.value].line[bid_id];
        teirs[bid.value].line_length--;

        bidders[msg.sender].current_bids_open--;
        current_bids_open--;

        if(teirs[bid.value].line_length == 0)
            removeTeir_internal(bid.value);
    }

////////////////
// Structs and Events
////////////////

    struct SpotInLine {
        uint next;
        uint bid_id;
        uint prev;
    }

    struct Bidder {
        uint current_bids_open;
        uint total_bids_made;
        mapping (uint => uint) bids;
    }

    struct Bid {
        uint id;
        address owner;
        uint value;
        bool cancelled;
        bool accepted;
    }

    struct Teir {
        uint teir_above;
        uint value;
        uint teir_below;

        uint front_of_line_id;
        uint back_of_line_id;
        uint line_length;
        mapping (uint => SpotInLine) line; //FIFO DLL
    }

    event NewBid_event(address bidder, uint bid_id, uint value);
}

contract MeDao is Tokenized {

    string public version = '0.0.2';

    address public Founder;

    OngoingAuction public Auction;

    uint public weekly_auction_reward;
    uint public scheduled_auction_timestamp;
    uint public auction_period;

    uint public total_proof_of_work;

    uint public cooldown_timestamp;
    uint burn_minimum = 1;

////////////////
// MeDao Functions
////////////////

    function MeDao (address founder, OngoingAuction auction) {
        Founder = founder;
        Auction = auction;
    }

    function startAuction () isScheduled {
        uint winning_bid_id = Auction.startAuction(withdraw_address);
        address winner = Auction.getBidOwner(winning_bid_id);
        uint winning_bid_value = Auction.getBidValue(winning_bid_id);

        Token.generateTokens(winner, 1 hours);

        AuctionWinner_event(winner,winning_bid_value);
    }

    function submitProofOfWork (uint burnAmount, string metadataHash)
    onlyTokenHolders(burn_minimum) {
        if(Token.balanceOf(msg.sender) < burnAmount) throw;

        Token.destroyTokens(msg.sender,burnAmount);
        total_proof_of_work += burnAmount;

        ProofOfWork_event(msg.sender,burnAmount,metadataHash);
    }

////////////////
// Owner Only
////////////////

    function setWeeklyAuctionReward (uint8 reward) onlyOwner
    hasCooldown(7 days) {
        if(reward > 40 hours || reward == 0) throw;

        weekly_auction_reward = reward;
        auction_period = 5 minutes; //set to 5 minutes for testing normally use:
                                    //7 days / auction_reward;
        scheduled_auction_timestamp = now + auction_period;

        NewWeeklyAuctionReward_event(weekly_auction_reward);
    }

    function setBurnMinimum (uint burnMinimum) onlyOwner {
        burn_minimum = burnMinimum;
    }

////////////////
// Events and Modifiers
////////////////

    modifier isScheduled () {
        if (scheduled_auction_timestamp == 0) throw;
        if (now < scheduled_auction_timestamp) throw;

        if(now < scheduled_auction_timestamp + auction_period)
            scheduled_auction_timestamp += auction_period;
        else
            scheduled_auction_timestamp = now + auction_period;

        _;
    }

    modifier onlyAuction () {
        if (msg.sender != address(Auction)) throw;

        _;
    }

    modifier hasCooldown (uint cooldown) {
        if(now < cooldown_timestamp) throw;

        cooldown_timestamp = now + cooldown;
        _;
    }

    event AuctionWinner_event (address winner, uint ether_bid);
    event ProofOfWork_event(address sender, uint amount, string metadataHash);
    event NewDepositAddress_event(address deposit_address);
    event NewWeeklyAuctionReward_event(uint new_auction_reward);
}

contract MeDaoRegistry {

    MiniMeToken PrimeToken;

    uint public total_medaos;
    mapping (uint => address) public founders;
    mapping (address => MeDao ) public medaos;

    function MeDaoRegistry (MiniMeToken Token) {
        PrimeToken = Token;
    }

    function register (string name) {
        if(medaos[msg.sender] != address(0x0)) throw;

        uint next_id = total_medaos++;
        founders[next_id] = msg.sender;

        OngoingAuction Auction = new OngoingAuction();

        medaos[msg.sender] = new MeDao(msg.sender,Auction);
        medaos[msg.sender].setupToken(msg.sender,PrimeToken,name,0,'seconds',0,true);
        medaos[msg.sender].transferOwnership(msg.sender);
        Auction.transferOwnership(medaos[msg.sender]);

        NewMeDao_event(msg.sender, medaos[msg.sender]);
    }

    event NewMeDao_event(address founder, address medao);
}