contract ManageBattleboards is AccessControl, SafeMath  {

    /*** DATA TYPES ***/
    address public angelCardDataContract = 0x6D2E76213615925c5fc436565B5ee788Ee0E86DC;
    address public petCardDataContract = 0xB340686da996b8B3d486b4D27E38E38500A9E926;
    address public accessoryDataContract = 0x466c44812835f57b736ef9F63582b8a6693A14D0;
    address public battleboardDataContract = 0x33201831496217A779bF6169038DD9232771f179;

   
    
    //The reserved balance is the total balance outstanding on all open battleboards. 
    //We keep track of this figure to prevent the developers from pulling out money currently pledged
    //This only features in the paid boards. 
    uint public contractReservedBalance;
    
    
    //Tile TYPES
    // 0 - Empty Space
    // 1 - Team (Angel + Pet)
 
    // 3 - Red Barrier (red is hurt)
    // 4 - Yellow barrier (yellow is hurt)
    // 5 - Blue barrier (blue is hurt)
    // 6 - Exp Boost (permanent)
    // 7 - HP boost (temp)
    // 8 - Eth boost
    // 9 - Warp
    // 10 - Medal
    
    //Aura Boosts
    // Red - Slow Burn - Enemies lose extra 10 hp/round in battle. 
    // Green - Healing Light - 20% chance to heal 50 hp each battle round
    //Yellow - Guiding Path - 5 hp recovered each turn not in a battle
    //Purple - Uncontroled Fury - 10% chance for sudden kill at start of battle. 
    //Orange - Radiant Power - +100 max hp on joining board. 
    //Blue - Undying Love - 30% chance to revive pet when dead. 
    
      
          // Utility Functions
    function DataContacts(address _angelCardDataContract, address _petCardDataContract, address _accessoryDataContract, address _battleboardDataContract) onlyCREATOR external {
        angelCardDataContract = _angelCardDataContract;
        petCardDataContract = _petCardDataContract;
        accessoryDataContract = _accessoryDataContract;
        battleboardDataContract = _battleboardDataContract;

    }
    
    function checkExistsOwnedAngel (uint64 angelId) private constant returns (bool) {
        IAngelCardData angelCardData = IAngelCardData(angelCardDataContract);
       
        if ((angelId <= 0) || (angelId > angelCardData.getTotalAngels())) {return false;}
        address angelowner;
        (,,,,,,,,,,angelowner) = angelCardData.getAngel(angelId);
        if (angelowner == msg.sender) {return true;}
        
       else  return false;
}
  
    function checkExistsOwnedPet (uint64 petId) private constant returns (bool) {
          IPetCardData petCardData = IPetCardData(petCardDataContract);
       
        if ((petId <= 0) || (petId > petCardData.getTotalPets())) {return false;}
        address petowner;
         (,,,,,,,petowner) = petCardData.getPet(petId);
        if (petowner == msg.sender) {return true;}
        
       else  return false;
}

    function checkExistsOwnedAccessory (uint64 accessoryId) private constant returns (bool) {
          IAccessoryData accessoryData = IAccessoryData(accessoryDataContract);
       if (accessoryId == 0) {return true;}
       //Not sending an accessory is valid. 
        if ((accessoryId < 0) || (accessoryId > accessoryData.getTotalAccessories())) {return false;}
        address owner;
         (,,owner) = accessoryData.getAccessory(accessoryId);
        if (owner == msg.sender) {return true;}
        
       else  return false;
}


 function takePet(uint64 petId) private {
           //This contract takes ownership of pets who are entered into battleboards and later distributes the losers' pets to the winners. 
               IPetCardData PetCardData = IPetCardData(petCardDataContract);
                PetCardData.transferPet(msg.sender, address(this), petId);
        }
           
           
       function restrictionsAllow(uint64 angelId, uint8 restrictions) private constant returns (bool) {
       //This function returns true if the restrictions of the board allow the angel and false if not. Basically
       //Angel card series IDs get stronger BP over time, with the exception of Michael and Lucifer. 
       //Note: Zadkiel is allowed on boards where she might be stronger - this is a rare case that will balance against
       //EVERY player needing to pay this gas to check every time ANY angel is added to the board. 
       
        IAngelCardData angelCardData = IAngelCardData(angelCardDataContract);
        uint8 angelCardSeries;
        (,angelCardSeries,,,,,,,,,) = angelCardData.getAngel(angelId);
        
        if (angelCardSeries > restrictions) {return false;}
        if ((angelCardSeries == 2) && (restrictions < 19)) {return false;} //Lucifer Card 
        if ((angelCardSeries == 3) && (restrictions < 21)) {return false;} //Michael Card
        return true;
       }
       
       
       //Opening and Closing Functions
      
  
       function createBattleboard(uint8 restrictions) external payable returns (uint16) {
           if (restrictions <0) {revert();}
           IBattleboardData battleboardData = IBattleboardData(battleboardDataContract);
           return battleboardData.createBattleboard(msg.value, restrictions);
           
       }
       
        function closeBattleboard(uint16 battleboardId) external {
       //This function can be called by ANYONE once either team 1 or team 2 has no more team members left. 
        IBattleboardData battleboardData = IBattleboardData(battleboardDataContract);
        address[] storage winners;
       if (battleboardData.isBattleboardLive(battleboardId) == false) {revert();}
       battleboardData.killBoard(battleboardId); 
        if ((battleboardData.getNumTeams(battleboardId,1) != 0) && (battleboardData.getNumTeams(battleboardId,2) != 0)) {revert();}
        //No teams are out, function shouldn't be called. 
        uint8 id;
        uint64 petId;
        address owner;
        if ((battleboardData.getNumTeams(battleboardId,1) == 0) && (battleboardData.getNumTeams(battleboardId,2) == 0)) {
              //Something odd happened and BOTH teams have lost - this is a tie. 
              IPetCardData PetCardData = IPetCardData(petCardDataContract);
              for (uint8 i =0; i<battleboardData.getMaxFreeTeams(); i++) {
                  owner = battleboardData.getOwner(battleboardId, 0, i);
                  id = battleboardData.getTileIDByOwner(battleboardId,owner);
                 petId = battleboardData.getPetbyTileId(battleboardId, id);
                 PetCardData.transferPet(address(this), owner, petId);
              }
        }
       if ((battleboardData.getNumTeams(battleboardId,1) != 0) && (battleboardData.getNumTeams(battleboardId,2) == 0)) {
       //Team 1 won 
       
       //Give team 1 back their pets. 
        for (i =0; i<(safeDiv(battleboardData.getMaxFreeTeams(),2)); i++) {
                  owner = battleboardData.getOwner(battleboardId, 1, i);
                  id = battleboardData.getTileIDByOwner(battleboardId,owner);
                 petId = battleboardData.getPetbyTileId(battleboardId, id);
                 PetCardData.transferPet(address(this), owner, petId);
                winners.push(owner); 
              }
            //give team 2's pets to team 1.   
        for (i =0; i<(safeDiv(battleboardData.getMaxFreeTeams(),2)); i++) {
                  owner = battleboardData.getOwner(battleboardId, 2, i);
                  id = battleboardData.getTileIDByOwner(battleboardId,owner);
                 petId = battleboardData.getPetbyTileId(battleboardId, id);
                 PetCardData.transferPet(address(this), winners[i], petId);
              }    
       }
          if ((battleboardData.getNumTeams(battleboardId,1) == 0) && (battleboardData.getNumTeams(battleboardId,2) != 0)) {
       //Team 2 won 
       
       //Give team 2 back their pets. 
        for (i =0; i<(safeDiv(battleboardData.getMaxFreeTeams(),2)); i++) {
                  owner = battleboardData.getOwner(battleboardId, 2, i);
                  id = battleboardData.getTileIDByOwner(battleboardId,owner);
                 petId = battleboardData.getPetbyTileId(battleboardId, id);
                 PetCardData.transferPet(address(this), owner, petId);
                winners.push(owner); 
              }
            //give team 1's pets to team 2  
        for (i =0; i<(safeDiv(battleboardData.getMaxFreeTeams(),2)); i++) {
                  owner = battleboardData.getOwner(battleboardId, 1, i);
                  id = battleboardData.getTileIDByOwner(battleboardId,owner);
                 petId = battleboardData.getPetbyTileId(battleboardId, id);
                 PetCardData.transferPet(address(this), winners[i], petId);
              }    
       }
   }
       
   
        function getInitialHP (uint64 angelId, uint64 petId, uint64 accessoryId) public constant returns (uint32 hp, uint16 petAuraComposite) {
           IAngelCardData angelCardData = IAngelCardData(angelCardDataContract);
           
           //need to reuse local variables to avoid stack too deep;
           uint16 temp;
           uint16 tempComposite;
           uint8 aura;
           (,,temp,aura,,,,,,,) = angelCardData.getAngel(angelId);
           if (aura == 3) {temp += 100;} //plus 100 initial HP to orange aura angels. 
           tempComposite = temp;
           //first add battlePower;
           (,,,,temp,,,,,,) = angelCardData.getAngel(angelId);
           tempComposite += temp;
           //now temp is experience
            uint8 petAuraColor;
             (petAuraComposite, petAuraColor) = findAuraComposite (petId, accessoryId);
            hp = (aurasCompatible(angelId,petAuraColor)+ petAuraComposite + tempComposite);
            
            return;
        }
       
       function addTeam1(uint64 angelId, uint64 petId, uint64 accessoryId, uint16 battleboardId) external payable {
           //call this function to add your angel/pet/accessory to team 1. 
           checkTeamToAdd(angelId,petId,accessoryId);
            IBattleboardData battleboardData = IBattleboardData(battleboardDataContract);
            // check if battleboard is full/open first and see where the new tesm should be placed. 
           uint32 hp;
           uint16 petPower;
           uint16 speed = getSpeed(petId,accessoryId);
           (hp, petPower) = getInitialHP(angelId,petId, accessoryId);
           battleboardData.createTile(battleboardId, 1, uint8(speed), getNewTeamPositionAndCheck(battleboardId, 1, angelId), hp, petPower + speed,angelId, petId, msg.sender, 1);
           battleboardData.addTeamtoBoard(battleboardId, msg.sender,1);
           //now add one random tile to the board. 
           addRandomTile(battleboardId, 1);
           takePet(petId);
       }
       
          function addTeam2(uint64 angelId, uint64 petId, uint64 accessoryId, uint16 battleboardId) external payable {
           
           checkTeamToAdd(angelId,petId,accessoryId);
            IBattleboardData battleboardData = IBattleboardData(battleboardDataContract);
            // check if battleboard is full/open first and see where the new tesm should be placed. 
           uint32 hp;
           uint16 petPower;
           uint16 speed = getSpeed(petId,accessoryId);
           (hp, petPower) = getInitialHP(angelId,petId, accessoryId);
           battleboardData.createTile(battleboardId, 1, uint8(speed), getNewTeamPositionAndCheck(battleboardId, 2, angelId), hp, petPower + speed,angelId, petId, msg.sender, 2);
           battleboardData.addTeamtoBoard(battleboardId, msg.sender,2);
           //now add one random tile to the board. 
           addRandomTile(battleboardId, 1);
           takePet(petId);
       }
       
       function getSpeed(uint64 petId, uint64 accessoryId) private constant returns (uint16) {
           //this speed function returns pet's base Luck + any accessory boost from the clovers;
           IAccessoryData accessoryData = IAccessoryData(accessoryDataContract);
           IPetCardData petCardData = IPetCardData(petCardDataContract);

       uint16 temp;
       uint8 accessorySeriesId;
         (,,,temp,,,,,,) = petCardData.getPet(petId);
         //first get the pet's base luck. 
           (,accessorySeriesId,) = accessoryData.getAccessory(accessoryId);
           if (accessorySeriesId == 5) {temp += 5;}
            if (accessorySeriesId == 6) {temp += 10;}
            return temp;
  
       }
          
        function getNewTeamPositionAndCheck (uint16 battleboardId,uint8 team, uint64 angelId) private constant returns (uint8) {
            IBattleboardData battleboardData = IBattleboardData(battleboardDataContract);
            uint8 numTeams1;
            uint8 numTeams2;
             uint8 position;
             uint8 restrictions;
            bool isLive;
        
           //Now see which tile to add the new team. Teams are added in a specified place besed on when they join. 
             (,isLive,,,,,restrictions,, numTeams1,numTeams2,,) = battleboardData.getBattleboard(battleboardId);
               
             if (restrictionsAllow(angelId, restrictions) == false) {revert();} 
            if (isLive== true) {revert();} //Can't add a team to a board that's already live.
             if (team == 1) {
                if (numTeams1 == 0) {position = 10;}
               if (numTeams1 ==1) {position = 12;}
               if (numTeams1 == 2) {position = 14;}
               if (numTeams1 >=3) {revert();}
           }
               if (team == 2) {
               if (numTeams2 == 0) {position = 50;}
               if (numTeams2 == 1) {position = 52;}
               if (numTeams2 == 2) {position = 54;}
               if (numTeams2 >=3) {revert();}
           }
           return position;
        }  
          
          function checkTeamToAdd(uint64 angelId, uint64 petId, uint64 accessoryId) private constant {
               if ((checkExistsOwnedAngel(angelId) == false) || (checkExistsOwnedPet(petId)== false) || (checkExistsOwnedAccessory(accessoryId) == false)) {revert();}
          }
          
             
             function addRandomTile(uint16 _battleboardId, uint8 seed) private  {
             IBattleboardData battleboardData = IBattleboardData(battleboardDataContract);
    
           uint8 newTileType;
           uint8 newTilePower;
           
            
             //Tile TYPES
    // 0 - Empty Space
    // 1 - Team (Angel + Pet)
    // 3 - Red Barrier (red is hurt)
    // 4 - Yellow barrier (yellow is hurt)
    // 5 - Blue barrier (blue is hurt)
    // 6 - Exp Boost (permanent)
    // 7 - HP boost (temp)
    // 8 - Eth boost
    // 9 - Warp
    // 10 - Medal
    // 11 - Pet permanent Aura boost random color
    
           
           uint8 chance = getRandomNumber(100,0,seed);
             
              if (chance <=30)  {
                //Barrier tile
                newTileType = getRandomNumber(5,3,seed);
                newTilePower = getRandomNumber(30,10,seed);
            }
                if ((chance >=30) && (chance <50)) {
                //HP boost
                newTileType = 7;
                newTilePower = getRandomNumber(45,15,seed);
            }
             if ((chance >=50) && (chance <60)) {
                //EXP boost
                newTileType = 6;
                newTilePower = getRandomNumber(5,1,seed);
            }
            if ((chance >=60) && (chance <80)) {
                //Warp
                newTileType = 9;
                newTilePower = getRandomNumber(64,1,seed);
            }
            if ((chance >=80) && (chance <90)) {
                //Medal
                newTileType = 10;
                newTilePower = getRandomNumber(2,0,seed);
            }
            if (chance >=90) {
                //Pet Aura boost 
                newTileType = 11;
                newTilePower = getRandomNumber(4,1,seed);
            }
            
                
         uint8 position = getRandomNumber(49,15,seed);
          //if desided position is already full, try three times to place them in an open spot. Odds are max 0.6% that the tx reverts. 
          //Random tiles are in the middle of the board and won't conflict with anangels. 
          //Random tiels CAN be on the same position as a monster, though. 
           if (battleboardData.getTileIDbyPosition(_battleboardId, position)!= 0) {
              
              position = getRandomNumber(49,15,msg.sender);
              if (battleboardData.getTileIDbyPosition(_battleboardId, position)!= 0) {
               position = getRandomNumber(49,15,msg.sender);
                if (battleboardData.getTileIDbyPosition(_battleboardId, position)!= 0) { 
                   position = getRandomNumber(49,15,msg.sender);
                if (battleboardData.getTileIDbyPosition(_battleboardId, position)!= 0) {revert();}
              }
           }
           }
         battleboardData.createTile(_battleboardId,newTileType, newTilePower, position, 0, 0, 0, 0, address(this),0);
       }
       
       function aurasCompatible(uint64 angel1ID, uint8  _petAuraColor ) private constant returns (uint8) {
            uint8 compatibility = 0;
            
        IAngelCardData angelCardData = IAngelCardData(angelCardDataContract);
          uint8 _angel1Aura;
         (,,,_angel1Aura,,,,,,,) = angelCardData.getAngel(angel1ID);
            if (_petAuraColor == 1) {
                if ((_angel1Aura == 2) || (_angel1Aura == 3) || (_angel1Aura == 4)) {compatibility++;}
            }
            if (_petAuraColor == 2) {
                if ((_angel1Aura == 0) || (_angel1Aura == 2) || (_angel1Aura == 5)) {compatibility++;}
            }
            if (_petAuraColor == 3) {
                if ((_angel1Aura == 1) || (_angel1Aura == 3) || (_angel1Aura == 5)) {compatibility++;}
            }
        return compatibility*12;
            
        }
        
        function findAuraComposite(uint64 pet1ID, uint64 accessoryId) private constant returns (uint16 composite, uint8 color) {
        IPetCardData petCardData = IPetCardData(petCardDataContract);
        
       uint16 pet1auraRed;
       uint16 pet1auraBlue;
       uint16 pet1auraYellow;
        (,,,,pet1auraRed,pet1auraBlue,pet1auraYellow,,,) = petCardData.getPet(pet1ID);
        
          IAccessoryData accessoryData = IAccessoryData(accessoryDataContract);
           
           uint8 accessorySeriesID;
           
           (,accessorySeriesID,) = accessoryData.getAccessory(accessoryId);
        
        if (accessorySeriesID == 7) {pet1auraRed += 6;}
        if (accessorySeriesID == 8) {pet1auraRed += 12;}
        if (accessorySeriesID == 9) {pet1auraYellow += 6;}
        if (accessorySeriesID == 10) {pet1auraYellow += 12;}
        if (accessorySeriesID == 11) {pet1auraBlue += 6;}
        if (accessorySeriesID == 12) {pet1auraBlue += 12;}
        
       
            color = 1; //assume red to start 
            if (((pet1auraBlue) > (pet1auraRed)) && ((pet1auraBlue) > (pet1auraYellow))) {color = 2;}
            if (((pet1auraYellow)> (pet1auraRed)) && ((pet1auraYellow)> (pet1auraBlue))) {color = 3;}
            composite = (pet1auraRed) + (pet1auraYellow) + (pet1auraBlue);
            return;
            }
        
          function kill() onlyCREATOR external {
        selfdestruct(creatorAddress);
    }
                function withdrawEther()  onlyCREATOR external {
   //shouldn't have any eth here but just in case. 
    creatorAddress.transfer(this.balance);
}
          
}
//This contract is to Manage (open, close, add teams, etc) battleboards. Call the Battleboards contract to make moves. Both of these contracts 
//interface with the battleboards data contract. 
function createBattleboard(uint8 restrictions) external payable returns (uint16) {
    if (restrictions <0) {revert();}
    IBattleboardData battleboardData = IBattleboardData(battleboardDataContract);
    return battleboardData.createBattleboard(msg.value, restrictions);
           
}

//Opening and Closing Functions