// File: BrightCoinAdvisorTokenDistribution.sol



pragma solidity ^0.4.25;

import "./BrightCoinTokenOwner_ICO.sol";
import "./SafeMath.sol";

contract BrightCoinAdvisorTokenDistribution  is BrightCoinTokenOwner
{


using SafeMath for uint;
mapping(address => uint256)AdvisorBalances;


constructor() public
{

}
  //There might be multiple entry to this
 uint256 public TotalAllocatedAdvisorToken;
 
 
 struct advisorDistribution {
        address addr;
        uint256 tokenamount;
        uint256 expiryDateTime;
        bool   advisorpartofTeam;
        bool  tokenlocked;
    }
 mapping(address => advisorDistribution) advisorDistributionDetails;
 address[] public advisorDistributionAddr;
 
 
 //Advisor Starts
//Adding New Advisor for Token Distribution
 function AddAdvisor(address _newAdvisor,uint256 _tokenamount,uint256 _lockexpiryTime,bool _tokenlocked)   internal
  {
    
        advisorDistribution storage  advisorDetails = advisorDistributionDetails[_newAdvisor];

       advisorDetails.addr = _newAdvisor;
       advisorDetails.tokenamount = _tokenamount;
       advisorDetails.expiryDateTime = _lockexpiryTime;
       advisorDetails.advisorpartofTeam = true;
       advisorDetails.tokenlocked   = _tokenlocked;

       advisorDistributionAddr.push(_newAdvisor);
 
 }

function UpdateAdvisorTokenDetails (address _newAdvisor,uint256 _tokenamount,
  uint256 _lockExpiryDateTime) internal
{
 require(CheckIfAdvisorActive(_newAdvisor) == true);
  advisorDistribution storage  advisorDetails = advisorDistributionDetails[_newAdvisor];
                  
   //Add new Token amount and Set new locking Period
   advisorDetails.tokenamount  = advisorDetails.tokenamount.add(_tokenamount);
   advisorDetails.expiryDateTime = _lockExpiryDateTime;

        
}

 //Remove Advisor For Further Investment and from the Team
 function RemoveAdvisorFromFurtherInvestment(address _newAdvisorAddr) public onlyTokenOwner  returns(bool)
 {
    advisorDistribution storage newAdvisorDetails = advisorDistributionDetails[_newAdvisorAddr];
    newAdvisorDetails.advisorpartofTeam = false;
    return true;
 }

 //Ends


 //check if Advisor Removed 
 function CheckIfAdvisorActive(address _newAdvisorAddr) view internal returns(bool)
 {

  advisorDistribution storage advisorDetails = advisorDistributionDetails[_newAdvisorAddr];
  if(advisorDetails.advisorpartofTeam == true)
  {
      return true;
  }
  return false;

 }

//Count total no of Advisors
 function TotalAdvisor() public onlyTokenOwner  view returns(uint256) 
 {
   return advisorDistributionAddr.length;
 }


 //check Amount with Advisor
 function CheckAdvisorTokenAmount(address _newAdvisorAddr) public onlyTokenOwner  view returns(uint256)
 {

  advisorDistribution storage advisorDetails = advisorDistributionDetails[_newAdvisorAddr];
  return advisorDetails.tokenamount ;

 }

 }

// File: BrightCoinERC20ContractInterface_ICO.sol



pragma solidity ^0.4.25;

interface ERC20TokenInterface {
    function totalSupply() external constant returns (uint);
    function balanceOf(address _tokenOwner) external constant returns (uint balance);
    function allowance(address _tokenOwner, address spender) external constant returns (uint remaining);
   function transfer(address _to, uint _tokens) external returns (bool success);
    function approve(address _spender, uint _tokens) external returns (bool success);
    function transferFrom(address _from, address _to, uint _tokens) external returns (bool success);

}

  
// File: BrightCoinERC20Contract_ICO.sol



pragma solidity ^0.4.25;
//pragma experimental "v0.5.0";

import "./BrightCoinTokenOwner_ICO.sol";
import "./BrightCoinERC20ContractInterface_ICO.sol";
import "./BrightCoinTokenSaleType_ICO.sol";
//import "./BrightCoinAdminTokenDistributionDetails.sol";
import "./SafeMath.sol";
import "./BrightCoinInvestorTokenLock_ICO.sol";





contract BrightCoinERC20 is BrightCoinTokenPreSaleDetails,
                            BrightCoinTokenMainSaleDetails,
                        //   BrightCoinAdminTokenDistributionDetails,
                            BrightCoinInvestorTokenLock
                           
                          
                         
                            
{
  using SafeMath for uint;


 event Transfer(address indexed from, address indexed _to, uint256 _value);
  event Approval(address indexed owner, address indexed _spender, uint256 _value);
   // Event called when presale is done
  event PresaleFinalized(uint tokens);
  event ICOSoftCapReached(string softcap);
  event ICOSoftUnsuccessFull(uint256 TokenSold,string str);
  event Burn(address addr, uint256 tokens);
  event Mint(address target, uint256 mintedAmount);
  
 
  bool  internal pauseICO = false; 
  uint8 internal saleIndex = 0; //Default Sale Index as zero

//Token Supply Details
  uint256 public totalSupply; //Need to set at constructor level
  uint256 private  BountyDistriuted = 0;
  uint256 public CompanyHoldingValue = 0;
   mapping(address => uint256)CompanyHoldingBalances;
   address internal custodianaddress;
 

  //Purchase Rate
  function setPurchaseRate(uint newRate) public onlyTokenOwner {
        require(purchaseRate != newRate);
        purchaseRate = newRate;
    }
 
 //ICO SoftCap & HardCap
 /*
    Soft cap is the minimal amount required by your project, to make it viable, in order to continue. If you do not reach that amount during your ICO then you should allow your investors to refund their money using a push/ pull mechanism.
 */
 

   function ChangeSoftCap(uint _newSoftcap) public onlyTokenOwner {
        require(icoSoftcap != _newSoftcap);
       // require(_newSoftcap <= totalSupply);
       require(icoSoftcap <= (balances[owner()].add(balances[custodianaddress])));
        icoSoftcap = _newSoftcap;
      
    }
    function changeHardCapLimit(uint256 _newHardcap) public onlyTokenOwner {
       
        require(icoHardcap != _newHardcap);
        require(_newHardcap <= (balances[owner()].add(balances[custodianaddress])));
       // require(_newHardcap <= totalSupply);
        icoHardcap = _newHardcap;
      
    }


    function GetSoftcap() public  view   returns(uint256) {

      return icoSoftcap;
    }

     function GetHardcap() public   view  returns(uint256) {

      return icoHardcap;
    }

    //Check if softcap reached
    function CheckIfSoftcapAchived() public   view returns(bool)
    {

        uint256 ownerbal = balances[owner()]; 
        uint256 custodianbal = balances[custodianaddress];
       uint tokenSold = totalSupply.sub(ownerbal.add(custodianbal));

        if (tokenSold > icoSoftcap)
            return true;
        else 
            return false;  
        
    }

    //check if HARD Cap Achived
    function CheckIfHardcapAchived(uint256 _tokens)  public view returns(bool)
    {
        uint256 ownerbal = balances[owner()]; 
        uint256 custodianbal = balances[custodianaddress];
      uint tokenSold = totalSupply.sub(ownerbal.add(custodianbal));
      require(tokenSold <= icoHardcap);
      require(tokenSold.add(_tokens) <=icoHardcap);
      return true;

    }

 
    function setSalePeriodIndex(uint8 _index) public onlyTokenOwner  
    {
        require(_index >0); //To set minimum Sale Period Index
        saleIndex = _index;
        
    }
    
     function getSalePeriodIndex()  public view  returns(uint8)
    {
        return saleIndex;
        
    }
  
  
 /* function ChangeFundDepositAddress(address _newFundDepositAddress) public onlyTokenOwner  {
    require(_newFundDepositAddress != address(0x0));
    require( FundDepositAddress != _newFundDepositAddress );
    //FundDepositAddress = _newFundDepositAddress;
  }
  */
  

//option for Minting more token 
bool public MintMoreTokens  = false;
function UpdateTokenMintingOption(bool _mintingOption) public onlyTokenOwner  {
  
  MintMoreTokens = _mintingOption;
}


 /////////////////////////////////////////////////   

 
 ////////////////////////////////////////
  mapping(address => uint256) balances;
  mapping(address => mapping (address => uint256)) allowed;

  
  
 //////////////////////////////////////////
  
 
  constructor () public{

   totalSupply = initialSupply*(10**uint256(decimals));

   //TotalAllocatedTeamToken = InitialAllocatedTeamToken*(10**uint256(decimals));
  // TotalAllocatedFounder = InitialFounderToken*(10**uint256(decimals));
 //  TotalAllocatedAdvisorToken = InitialAllocatedAdvisorToken*(10**uint256(decimals));
  CompanyHoldingValue = InitialCompanyHoldingValue*(10**uint256(decimals));
  // BountyAllocated = totalBountyAllocated*(10**uint256(decimals));
   

   icoHardcap = icoHardcap.mul(10**uint256(decimals));
   icoSoftcap = icoSoftcap.mul(10**uint256(decimals));
   
   balances[msg.sender] = totalSupply;
  emit Transfer(address(0), msg.sender, totalSupply);
   
 // FounderBalances[msg.sender] = TotalAllocatedFounder;
 // AdvisorBalances[msg.sender] = TotalAllocatedAdvisorToken;
//  TeamBalances[msg.sender] = TotalAllocatedTeamToken;
  CompanyHoldingBalances[msg.sender] = CompanyHoldingValue;
  //BountyBalances[msg.sender]  = BountyAllocated;
 // balances[BountyTokenHolder] = BountyAllocated;
   
  
  // BountyDistriuted = 0;
 
  }

 

 // This function returns remaininig token
  function balanceOf(address _who) public constant returns (uint256) {
      return balances[_who];
  }

//Function Total Supply
function totalSupply() public constant returns (uint256) {
      return totalSupply;
  }

function allowance(address _owner, address _spender) public view returns (uint256) {
        return allowed[_owner][_spender];
}
    
   
    /// @notice Will Transfer tokens from current address to receipient address.
    /// @param _from  addresses to send token.
    /// @param _tokens amount of token to be transferred.
   /* function internaltransfer1(address _to, address _from, uint _tokens) internal returns (bool) {
  
     // Prevent transfer to 0x0 address. 
   require(_to != 0x0);
    require (_tokens > 0);
    require (_from != _to);
   require(balances[_from] >= _tokens);
   require(balances[_to] + _tokens > balances[_to]);
    balances[_from] = balances[_from].sub(_tokens);
    balances[_to] = balances[_to].add(_tokens);
    return true;
   }
   */
   
   /// @notice Will Transfer tokens from current address to receipient address.
    /// @param _recipient  addresses to send token.
    /// @param _tokens amount of token to be transferred.
    function internaltransfer(address _sender, address _recipient, uint _tokens) internal returns (bool) {
  
     // Prevent transfer to 0x0 address. 
   /* require(_to != 0x0);
    require (_tokens > 0);
  //require (_from != _to);
   require(balances[_from] >= _tokens);
   require(balances[_to] + _tokens > balances[_to]);
    balances[_from] = balances[_from].sub(_tokens);
    balances[_to] = balances[_to].add(_tokens);
    emit Transfer(_from, _to, _tokens);
    */
    require(_sender != address(0), "ERC20: transfer from the zero address");
       require(_recipient != address(0), "ERC20: transfer to the zero address");

       balances[_sender] = balances[_sender].sub(_tokens);
       balances[_recipient] = balances[_recipient].add(_tokens);
         //  emit Transfer(_sender, _recipient, _tokens);
    
    return true;
   }
   

    /// @notice Will cause a certain `_value` of coins minted for `_to`.
    /// @param _to The address that will receive the coin.
    /// @param _value The amount of coin they will receive.
    function mint(address _to, uint _value) public onlyTokenOwner  {
       // assuming you have a contract owner
        mintToken(_to, _value);
    }

/*
    /// @notice Will allow multiple minting within a single call to save gas.
    /// @param recipients A list of addresses to mint for.
    /// @param _values The list of values for each respective `_to` address.
    function airdropMinting(address[] recipients, uint256[] _values) public onlyTokenOwner  {
        // assuming you have a contract owner
        require(recipients.length == _values.length);
        for (uint i = 0; i < recipients.length; i++) {

            mintToken(recipients[i], _values[i]);
        }
    }
    */

    /// Internal method shared by `mint()` and `airdropMinting()`.
    function mintToken(address _to, uint256 _value) internal {

   
        balances[_to]  = balances[_to].add(_value);
        totalSupply =  totalSupply.add(_value); 
        require(balances[_to] >= _value && totalSupply >= _value); // overflow checks
        emit  Mint(_to, _value);
    } 

    /// @notice it will burn all the token passed as parameter.
    /// @param _value Value of token to be burnt
   function burn(uint256 _value) public onlyTokenOwner  {
    require(_value > 0);
    require(_value <= balances[msg.sender]);
    // no need to require value <= totalSupply, since that would imply the
    // sender's balance is greater than the totalSupply, which *should* be an assertion failure

    address burner = msg.sender;
    balances[burner] = balances[burner].sub(_value);
    totalSupply = totalSupply.sub(_value);
    emit Burn(burner, _value);
   }


   //Owner of ICO can anytime Stop the ICO post that no any transaction will happen
   function pauseICOtransaction(bool _status) public onlyTokenOwner  {

    pauseICO = _status;
   }

//Check if token is available for further Distribution to Admin
/* function InterTransferToAdmin(address _addr,
                     uint256 _tokenAmount,
                     uint8 _adminType )   internal  returns(bool)
 {
     

     if(_adminType == uint8(BrightCoinAdminType.Founder))
    {
        require(_tokenAmount <= FounderBalances[msg.sender]);
        // balances[_addr] = balances[_addr].add(_tokenAmount);
        internaltransfer(msg.sender,_addr,_tokenAmount);
         FounderBalances[msg.sender] = FounderBalances[msg.sender].sub(_tokenAmount);
        // emit Transfer(msg.sender, addr, TokenAmount);
      return true;
    }
    else if (_adminType == uint8(BrightCoinAdminType.Advisor))
    {
      require(_tokenAmount <= AdvisorBalances[msg.sender]);
      //balances[_addr] = balances[_addr].add(_tokenAmount);
       internaltransfer(msg.sender,_addr,_tokenAmount);
      AdvisorBalances[msg.sender] = AdvisorBalances[msg.sender].sub(_tokenAmount);
     // emit Transfer(msg.sender, addr, TokenAmount);
      return true;
    }
    else if(_adminType == uint8(BrightCoinAdminType.Team))
    {
      require(_tokenAmount <= TeamBalances[msg.sender]);
      //balances[_addr] = balances[_addr].add(_tokenAmount);
       internaltransfer(msg.sender,_addr,_tokenAmount);
      TeamBalances[msg.sender] = TeamBalances[msg.sender].sub(_tokenAmount);
     // emit Transfer(msg.sender, addr, TokenAmount);
      return true;

    }  
    
    
 }
 */
    //Check if token is available for further Distribution to Advisor
 function InternalTransferfrom(address _from , address _addr, uint256 _tokenamount)  internal returns(bool)
 {
     
        require(allowed[owner()][_from] >= _tokenamount);
        require(internaltransfer(_from, _addr, _tokenamount) == true);
        allowed[owner()][_from] = allowed[owner()][_from].sub(_tokenamount);
       
       return true;
 }  
 
function transferownership(address _newOwner) public onlyTokenOwner 
 {
     
     require(_newOwner != 0x0);
    balances[_newOwner] =  balances[_newOwner].add(balances[msg.sender]);
     //To avoid reentrency 
   balances[msg.sender] = 0;
   emit Transfer(msg.sender, _newOwner, balances[_newOwner]);
   
   
  //FounderBalances[_newOwner] = FounderBalances[_newOwner].add(FounderBalances[msg.sender]);
  //AdvisorBalances[_newOwner] = AdvisorBalances[_newOwner].add(AdvisorBalances[msg.sender]);
 // TeamBalances[_newOwner] = TeamBalances[_newOwner].add(TeamBalances[msg.sender]);
 CompanyHoldingBalances[_newOwner] = CompanyHoldingBalances[_newOwner].add(CompanyHoldingBalances[msg.sender]);

    //To avoid reentrency 
 // balances[msg.sender] = 0;
//  FounderBalances[msg.sender] = 0;
 // AdvisorBalances[msg.sender] = 0;
 // TeamBalances[msg.sender] = 0;
  
   transferOwnership(_newOwner); // Transfer Ownership
     
     
 }



}
// File: BrightCoinFounderTokenDistribution.sol



pragma solidity ^0.4.25;

import "./SafeMath.sol";
import "./BrightCoinTokenOwner_ICO.sol";

contract BrightCoinFounderTokenDistribution  is BrightCoinTokenOwner
{

  mapping(address => uint256)FounderBalances;
using SafeMath for uint;

 struct founderDistribution {
       address founderAddress;
       uint256 founderToken;
       uint256 lockExpiryTime;
       bool    founderActive;
       bool    tokenlocked;
    }
    
 mapping(address => founderDistribution) founderTokenDetails;
 address[] internal founderAddrs;

constructor() public
{
 
}

//Add Founder Starts

function AddFounder(address _newFounder,uint256 _founderToken,uint256 _lockExpiryDateTime,
 bool _tokenLocked)   internal 
  {

      founderDistribution storage  founderDetails = founderTokenDetails[_newFounder];
    
      founderDetails.founderAddress = _newFounder;
      founderDetails.founderToken = _founderToken;
      founderDetails.lockExpiryTime = _lockExpiryDateTime;
      founderDetails.tokenlocked = _tokenLocked;
      founderDetails.founderActive = true;
      founderAddrs.push(_newFounder);
    
 }


  function  UpdateFounderTokenDetails(address _newFounder,uint256 _founderToken,
  uint256 _lockExpiryDateTime) internal
  {
    require(CheckIfFounderActive(_newFounder) == true);   
     //Add new Token amount and Set new locking Period
      founderDistribution storage  founderDetails = founderTokenDetails[_newFounder];
     founderDetails.founderToken  = founderDetails.founderToken.add(_founderToken);
     founderDetails.lockExpiryTime = _lockExpiryDateTime;     
  }

//Remove Founder For Further Investment and from the Team
 function RemoveFounderFromFurtherInvestment(address _newFounderAddr)  public onlyTokenOwner  returns(bool)
 {
   founderDistribution storage newFounderDetails = founderTokenDetails[_newFounderAddr];
   newFounderDetails.founderActive = false;
    
    return true;

 }

//ENDS

//check if Founder Removed 
 function CheckIfFounderActive(address _newFounderAddr) view internal returns(bool)
 {

  founderDistribution storage founderDetails = founderTokenDetails[_newFounderAddr];
 if(founderDetails.founderActive == true)
  {
      return true;
  }
  
  return false;

 }



//Count total no of Advisors
 function TotalFounder() public view returns(uint256) 
 {
   return founderAddrs.length;
  
 }


 //check Amount with Advisor
 function CheckFounderTokenAmount(address _newFounderAddr) view public returns(uint256)
 {

  founderDistribution storage founderDetails = founderTokenDetails[_newFounderAddr];
  return founderDetails.founderToken;
   
 }

}


 
    
// File: BrightCoinInvestorTokenLock_ICO.sol



pragma solidity ^0.4.25;

import "./SafeMath.sol";
import "./BrightCoinTokenOwner_ICO.sol";



contract BrightCoinInvestorTokenLock is  BrightCoinTokenOwner
{

 /**
    * @dev Error messages for require statements
    */
   // string internal constant ALREADY_LOCKED = 'Tokens already locked';
   // string internal constant NOT_LOCKED = 'No tokens locked';
    //string internal constant AMOUNT_ZERO = 'Amount can not be 0';

enum BrightCoinLockType { Investor, Admin,Bounty }
      using SafeMath for uint;

    /**
     * @dev locked token structure
     */
    struct lockToken {
        uint256 amount;
        uint256 validity;
        bool exists;
        bool claimed;
 
    }

    /**
     * @dev Holds number & validity of tokens locked for a given reason for
     *      a specified address
     */

    mapping(address => lockToken)  locktokenDetails;

    /**
     * @dev Records data of all the tokens Locked
     */
    event Locked(
        address indexed _of,
        uint256 _amount,
        uint256 _validity
    );

    /**
     * @dev Records data of all the tokens unlocked
     */
    event Unlocked(
        address indexed _of,
        bytes32 indexed _reason,
        uint256 _amount
    );


   constructor() public 
   {
	
   }

    function tokensLocked(address _of)
        internal
        view
        returns (uint256 amount)
    {
        uint256 am = 0;
        lockToken storage AddrStruct =  locktokenDetails[_of];
        if (!AddrStruct.claimed)
            am = AddrStruct.amount;
      
            return am;
    }
    
    function isTokenLockExpire(address _of, uint256 _time)  view internal  returns(bool)
    {
        bool retVal = false;

       if(locktokenDetails[_of].validity < _time)
            return retVal = true;
      
            return retVal;
    }

    function getTokenLockExpiry(address _of)  view internal returns(uint256 )
    {
       return  locktokenDetails[_of].validity;
    }


    function isAddrExists(address _addr) view internal returns(bool)
    {
        
       lockToken storage lockBounty = locktokenDetails[_addr];
          return lockBounty.exists;
       
           
    }
    
    
    
     function SetTokenLock(address _of, uint256 _time, 
         uint256 _amount)  internal   
          {
              
               //uint256 validUntil = now.add(_time); //solhint-disable-line

                 require(_amount != 0);
                 require(_of != 0x0);

                 lockToken storage AddrStruct =  locktokenDetails[_of];
                 if(tokensLocked(_of) > 0)
                 {
                      
                        AddrStruct.amount = (AddrStruct.amount).add(_amount);
                        AddrStruct.validity = _time;
                 }
                 else
                 {
        
                        AddrStruct.amount = _amount;
                        AddrStruct.validity = _time;
                        AddrStruct.exists = true;
                        AddrStruct.claimed = false;
                 }
          }

          function IncreaseTokenAmount(address _addr, uint256 _validity,uint256 _amount)
         internal 
        returns (bool)
    {
       
        require(tokensLocked(_addr) > 0);
         lockToken storage AddrStruct =  locktokenDetails[_addr];
        AddrStruct.amount = (AddrStruct.amount).add(_amount);
        AddrStruct.validity = _validity;
       
       // emit Locked(msg.sender, _reason, locked[msg.sender][_reason].amount, locked[msg.sender][_reason].validity);
        return true;
        
    }



}


// File: BrightCoinRegulatedToken_ICO.sol




pragma solidity ^0.4.25;


import "./BrightCoinTokenOwner_ICO.sol";
import "./BrightCoinERC20Contract_ICO.sol";


//Rules for Accredited Investors
/*
 This is special BrightCoinToken that will allow User to invest to Token Only after Proper Checks and Validation as provide by US Regulatories Authorities
*/

/*
 import all specific contract that will help in Validation 
 Accridetion
 KYC
 TokenDistribution Details
*/

contract BrightCoinRegulatedToken  is BrightCoinERC20
{

//BrightCoinInvestorKYC InvestorKYCInfo;
//BrightCoinInvestorCheck AccreditationInfo;


address public BrightCoinInvestorKYCAddress; 
address public BrightCoinInvestorAccreditationAddress; 
mapping (uint8 => uint256) private PeriodTokenAmount;
bool InvestorSecurity;
bool KYCSupport;

constructor() public 
{
 InvestorSecurity = false;
 KYCSupport = false;
}




  
  function DistributeToken(address _addrOfInvestor, uint256 _currenttime,
                    uint256 _tokens, uint8 _mainSalePeriodIndex) public onlyTokenOwner 
  {

      require(_addrOfInvestor != 0x0);
      require(_currenttime >0);
   //   require(_tokenlockPeriod > 0);
      require(_tokens > 0);

   
       
        if((_mainSalePeriodIndex == 0) && (PreSaleOn == true))  //PreSale
        {
        
       
      
            //Check if Period Hard cap achived 

            require(_tokens <= getMaxCoinSoldDuringPreSale());
            
            //Check if ICO Hardcap achived
            require(CheckIfHardcapAchived(_tokens) == true);
            internaltransfer(msg.sender,_addrOfInvestor,_tokens);
           // SetTokenLock(_addrOfInvestor,tokenTimeLock,_tokens);
            updatepresalemaxTokenCount(_tokens);
        }
        else
        {
            //Calculate token amount
            require(CheckIfMainSaleOn(_mainSalePeriodIndex) == true);
            require(CheckTokenPeriodSale(_currenttime,_mainSalePeriodIndex) == true);
            
          
      
            require(CheckMainSaleLimit(_mainSalePeriodIndex,_tokens) == true, "Main Sale Limit Crossed");
             
            require(CheckIfHardcapAchived(_tokens) == true);
            
             internaltransfer(msg.sender,_addrOfInvestor,_tokens);
           //  SetTokenLock(_addrOfInvestor,tokenTimeLock,_tokens);
             updateCurrentTokenCount(_mainSalePeriodIndex,_tokens);
        }
  }

 function tranfertocustodian(address _to) public onlyTokenOwner returns(bool)
{
    internaltransfer(msg.sender, _to, balances[msg.sender]);
    custodianaddress = _to;
    return true;
}

  

function regulatedtransfer( address _from , address _to, uint256 _tokens) private returns(bool) 
{
    //check if locking period is expired or not 
      uint256 currenttime = now;
      
      if (InvestorSecurity == true)
      {
          
           if(isTokenLockExpire(_from,currenttime) == true)
           {
               internaltransfer(_from,_to,_tokens);
                return true;
           }
           
           uint256 TokenLockExpiry = getTokenLockExpiry(_from); 
           require(TokenLockExpiry !=0);
         
         if( ICOType != uint8(BrightCoinICOType.Utility))
         {
        //    require(AccreditationInfo.checkBothInvestorValidity(_from,_to, ICOType) == true); 
            SetTokenLock(_to,TokenLockExpiry,_tokens);
            internaltransfer(_from,_to,_tokens);
            return true; 
         }
         else
         {
          SetTokenLock(_to,TokenLockExpiry,_tokens);
           internaltransfer(_from,_to,_tokens);
           return true;
         }
            
      }
      else
      {
        // require(isTokenLockExpire(msg.sender,currenttime) == true);
         internaltransfer(_from,_to,_tokens);
         return true;
      }
      
}

function transferFrom(address _from, address _to, uint256 _tokens) public returns (bool success) 
{
      
       require(pauseICO == false);  //if this flag is true the no operation is allowed.
      require(_tokens > 0);
      require(allowed[_from][msg.sender] >= _tokens);
    
     require(internaltransfer(_from ,_to,_tokens) == true);
      //If regulated transfer is true then only reduce allowed map
      allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_tokens);
      return true;
}
    

function approve(address _spender, uint256 _value) public returns (bool success)
 {
     require(_spender != address(0));
    //  require(checkCompliance(_spender) == true);
      allowed[msg.sender][_spender] = _value;
      emit Approval(msg.sender, _spender, _value);
      
      return true;
 }
  //This method will be called when investor  wants to tranfer token to other.  
function transfer(address _to, uint _tokens) public returns (bool) 
 {     
    require(pauseICO == false);  //if this flag is true the no operation is allowed.
    require(_to != address(0));
    require (internaltransfer(msg.sender, _to,_tokens ) == true);
   
     emit Transfer(msg.sender, _to, _tokens);
    return true;
       
      
}
      

   function TransferCompanyHoldingTokens() public onlyTokenOwner     returns(bool)
  { 
    
      require(CompanyHoldingBalances[msg.sender] == CompanyHoldingValue) ; 
   //   uint256 Holdinglockexpiry = now.add(_lockExpiry);
     // balances[CompanyHoldingAddress] = CompanyHoldingBalances[msg.sender];
      internaltransfer(msg.sender,CompanyHoldingAddress,CompanyHoldingValue);
    CompanyHoldingBalances[msg.sender] = 0;
  // SetTokenLock(CompanyHoldingAddress,Holdinglockexpiry,CompanyHoldingValue);
    //  emit Transfer(msg.sender, CompanyHoldingAddress, CompanyHoldingValue);
      return true;

 } 
 


}
// File: BrightCoinTeamTokenDistribution_ICO.sol




pragma solidity ^0.4.25;

import "./BrightCoinTokenOwner_ICO.sol";

contract BrightCoinTeamTokenDistribution  is BrightCoinTokenOwner
{


  mapping(address => uint256)TeamBalances;
constructor() public
{
 
}

//Team Distribution  
 //There might be multiple entry to this
 uint256 public TotalAllocatedTeamToken;
 
 
 struct teamDistribution {
        address addr;
        uint256 tokenamount;
        uint256 lockexpiry;
        bool   teamActiveInvestor;  //To Ensure if team is still on
        bool tokenlocked;
    
    }

 mapping(address => teamDistribution) teamDistributionDetails;
 address[] public teamTokenDetailsAddr;
 

 //Team Token Distribution  Starts

//Addng Details to Team Token
 function AddTeam(address _newTeamAddr,uint256 _tokenamount,
 uint256 _lockexpirydate,
 bool _tokenLocked)  internal {
     
  teamDistribution storage teamDetails = teamDistributionDetails[_newTeamAddr];
    teamDetails.addr = _newTeamAddr;
    teamDetails.tokenamount = _tokenamount;
    teamDetails.lockexpiry = _lockexpirydate;
    teamDetails.teamActiveInvestor = true;
    teamDetails.tokenlocked = _tokenLocked;
    teamTokenDetailsAddr.push(_newTeamAddr);

 }

 function UpdateTeamTokenDetails(address _teamaddr,
                            uint256 _tokenamount,
                            uint256 _lockExpiryDateTime) internal
 {
       //check if Team is Active
       require(CheckIfTeamActive(_teamaddr) == true);
      teamDistribution storage teamDetails = teamDistributionDetails[_teamaddr];
 
      teamDetails.tokenamount += _tokenamount;
      teamDetails.lockexpiry = _lockExpiryDateTime;
  
 }

 function RemoveTeamFromFurtherInvestment(address _newTeamAddr) public onlyTokenOwner  
 {
    teamDistribution storage teamDetails = teamDistributionDetails[_newTeamAddr];
    teamDetails.teamActiveInvestor = false;

 }



 function TotalTeamnvestor() public view returns(uint256) 
 {
   return teamTokenDetailsAddr.length;
  
 }

//check if Team Removed 
 function CheckIfTeamActive(address _teamAddr) view internal returns(bool)
 {

  teamDistribution storage teamDetails = teamDistributionDetails[_teamAddr];

  if(teamDetails.teamActiveInvestor == true)
  {
      return true;
  }
  return false;

 }

 //check Amount with Advisor
 function CheckTeamTokenAmount(address _newTeamAddr) view public returns(uint256)
 {

  teamDistribution storage teamDetails = teamDistributionDetails[_newTeamAddr];
        return teamDetails.tokenamount;

 }

}
// File: BrightCoinTokenConfig_ICO.sol



pragma solidity ^0.4.25;

contract BrightCoinTokenConfig
{

    //Token Details  
  string public constant symbol = "BDLR"; // This is token symbol
  string public constant name = "BDollar"; // this is token name
  uint256 public constant decimals = 18; // decimal digit for token price calculation
  string public constant version = "1.0";

  uint256 public constant initialSupply = 3000000000;

   //For Presales Only 
    uint256  public constant maxCoinSoldDuringPresale = 400000000;
    uint256  public constant BonusAmountPreSale = 0; //To be calculated from outside
    uint8    public constant Discount = 0; // to be calculated from outside

     //PreSale Start & End Dates 
    uint256 internal ICOstartDate = 1561161600; //22/06/2019 00:00 UTC
    uint256 internal ICOendDate =  1569193200;   //22/09/2019 23:00 UTC

     //Presale Maximum and Minmum Contributions
    uint256  internal MinimumContribution = 0.0 ether;
    uint256  internal MaximumContribution = 0.0 ether;

    //purchase rate can be changed by the Owner
     uint256 public purchaseRate = 0;

    enum BrightCoinICOType { RegD, RegS, RegDRegS, Utility }
    uint8 public constant ICOType = uint8(BrightCoinICOType.Utility);   //0 for RegD , 1 for RegS and 2 for RedDRegS and 3 means utility ICO

   uint256 public constant InitialFounderToken = 0;
   uint256 internal constant InitialAllocatedTeamToken = 0;  // Token token allocated for Team distribution
   uint256 internal constant InitialAllocatedAdvisorToken = 0;


   uint internal icoSoftcap = 0; //Minimum Eather to Reach
   uint internal icoHardcap = 2400000000;

    //Investment storage address
  address public constant FundDepositAddress = 0x00; //Should be taken from Script 

   //Company Holdings
 address public constant CompanyHoldingAddress = 0x7027F37e7d67E5656954483181dE674A61c0A6BD; //company Holding adddress
 uint256 public constant InitialCompanyHoldingValue = 600000000;// Value to be updated via Script

//Bounty Token Distribution
uint256 public constant totalBountyAllocated = 0;
address public  constant BountyTokenHolder = 0x00; //This address own the token and finally transfer



}
// File: BrightCoinTokenDistributionDetails_ICO.sol




pragma solidity ^0.4.24;

import "./BrightCoinTeamTokenDistribution_ICO.sol"; 
import "./BrightCoinFounderTokenDistribution.sol"; 
import "./BrightCoinAdvisorTokenDistribution.sol"; 
import "./SafeMath.sol";
import "./BrightCoinTokenOwner_ICO.sol";

contract BrightCoinTokenDistributionDetails is BrightCoinAdvisorTokenDistribution,BrightCoinTeamTokenDistribution,BrightCoinFounderTokenDistribution
{

using SafeMath for uint;

constructor() public
{
 
}


  //Token distribution details
  /////////////////////////////////////////
  //Token Distribution Details --- Founder 
 //The distribution amount, Timelock  is fixed   
 //Rewards Token : The Amount of token to be used for Bounty and Rewards
  uint256 public constant InitialRewardsBountyToken = 1000; //To be updated via Script
  uint256 public constant RewardBountylockinPeriod  = 234567;  
  uint256 public RewardsBountyToken;


 //Company Holdings
 address public constant CompanyHoldingAddress = 0xcf530e5b154EB28A379cF5774d5d15B04cF10422;//To be updated via Script
 uint256 public constant InitialCompanyHoldingValue = 1000;// Value to be updated via Script
 uint256 public CompanyHoldingValue;
 uint256 public constant CompanyHoldinglockingPeriod = 123456; //Token LockTIme
 
 
} 
// File: BrightCoinTokenOwner_ICO.sol



pragma solidity ^0.4.25;


/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract BrightCoinTokenOwner 
{
  address private _owner;


  event OwnershipRenounced(address indexed previousOwner);
  event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
  );


  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() public {
    _owner = msg.sender;
  }

  /**
   * @return the address of the owner.
   */
  function owner() public view returns(address) {
    return _owner;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyTokenOwner() {
    require(isOwner());
    _;
  }
  

  /*modifier onlyTokenOwner(address _account) {
        require(msg.sender == _account, "Owner Not Authorized");
        _;
    }
    */

  /**
   * @return true if `msg.sender` is the owner of the contract.
   */
  function isOwner() public view returns(bool) {
    return msg.sender == _owner;
  }
  

  /**
   * @dev Allows the current owner to relinquish control of the contract.
   * @notice Renouncing to ownership will leave the contract without an owner.
   * It will not be possible to call the functions with the `onlyOwner`
   * modifier anymore.
   */
  function renounceOwnership() internal  {
    emit OwnershipRenounced(_owner);
    _owner = address(0);
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function transferOwnership(address _newOwner) internal 
   {
    _transferOwnership(_newOwner);
  }

  /**
   * @dev Transfers control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function _transferOwnership(address _newOwner) internal {
    require(_newOwner != address(0));
    _owner = _newOwner;
    emit OwnershipTransferred(_owner, _newOwner);
  }

}

// File: BrightCoinTokenSaleType_ICO.sol



pragma solidity ^0.4.25;

import "./BrightCoinTokenOwner_ICO.sol";
import "./SafeMath.sol";
import "./BrightCoinTokenConfig_ICO.sol";

//Section1
//...............................................................................
contract BrightCoinTokenPreSaleDetails is BrightCoinTokenOwner, BrightCoinTokenConfig
{

    //address owner;
    using SafeMath for uint;
    uint256 Availabletokenpresale;
    constructor() public
    { 
        Availabletokenpresale = maxCoinSoldDuringPresale*(10**uint256(decimals));
    }

  
    //Current Presale Status 
    bool public PreSaleOn = true;
 
     //Function for changing the startDate of Presale
    function changeStartDate(uint256 _startDateTimeStamp, uint256 _currenttime) public onlyTokenOwner  returns(bool){

    require(ICOstartDate > _currenttime );
    require( _startDateTimeStamp < ICOendDate );
    ICOstartDate = _startDateTimeStamp;
    return true;
  }

    function getMaxCoinSoldDuringPreSale() view internal  returns(uint256)
    {
        return Availabletokenpresale;
        
    }

    /**
   * @dev It changes end date of Presale , provided it is not crossed.
   * @param _endDateTimeStamp The new proposed end datetime for Presale.
   */
    function changeEndDate(uint256 _endDateTimeStamp,uint256 _currenttime) public onlyTokenOwner  returns(bool) 
    {
      require(ICOendDate > _currenttime);
      require( _endDateTimeStamp > ICOstartDate );
      ICOendDate = _endDateTimeStamp;
      return true;
    }
    
    /**
   * @dev It check whether the datetime passed is in presale period or not
   * @param _currenttime The datetime to be checked for presale period.
   */
     function inPreSalePeriod(uint256 _currenttime) public view returns (bool) {
      if (_currenttime >= ICOstartDate && _currenttime <= ICOendDate) 
          return true;
      else 
          return false;     
     }

    /**
   * @dev It will change the presale status or true/false depending upon input
   * @param _presalestatus the staus to be set with presale.
   */
     function changePresaleStatus(bool _presalestatus) public onlyTokenOwner 
     {
       PreSaleOn = _presalestatus;
     }
     
   function updatepresalemaxTokenCount(uint256 _token) internal 
  {
       Availabletokenpresale = Availabletokenpresale.sub(_token);
  }
}

//Section2
//..........................................................................
contract BrightCoinTokenMainSaleDetails  is BrightCoinTokenOwner, BrightCoinTokenConfig
{
   //For MainSale
struct mainSaleTokenDistrubution
{
    uint256 mainStartDate;
    uint256 mainSaleEndDate ;
    uint256 maxCoinSold;
  // uint256 currentTokenCount;
    uint256 discount;
    bool    periodActive;
    uint8  periodIndex;
    bool exists;
}

 //address owner;
  using SafeMath for uint;
  
constructor() public
{
 
}

mapping(uint256 => mainSaleTokenDistrubution) mainSaleCountMapping;
uint256[] internal  mainSaleList;
 mapping (uint8 => uint256) private PeriodTokenAmount;

 /**
   * @dev It add mainsale period details 
   * @param _mainStartDate Start date of curent mainsale period
   * @param _mainSaleEndDate End Date of MainSale
   * @param _maxCoinSold Maximum coin sold during this period
   * @param _discount Bonus if any for this sale period
   * @param _periodIndex Period Index so that MainSale period can be tracked.
   * @param _periodActive  Setting current period state.
   */
function AddMainSalePeriod(uint256 _mainStartDate,uint256 _mainSaleEndDate,uint256 _maxCoinSold,
                        uint256 _discount,uint8 _periodIndex,
                         bool _periodActive) public onlyTokenOwner  returns(bool)
   {

      mainSaleTokenDistrubution storage mainSale = mainSaleCountMapping[_periodIndex];
      require(mainSale.exists == false); //To ensure that it is  new instance
      
      mainSale.mainStartDate  = _mainStartDate;
      mainSale.mainSaleEndDate = _mainSaleEndDate;
      mainSale.maxCoinSold = _maxCoinSold.mul(10**uint256(decimals));
     mainSale.discount = _discount;
      mainSale.periodIndex = _periodIndex; 
      mainSale.periodActive = _periodActive;
      mainSale.exists = true;

      mainSaleList.push(_periodIndex);
      return true;
    }                                                      
                            

   /**
   * @dev It check whenther current datetime fits is given mainsale period
   * @param _dateTimeStamp to be verified.
   * @param _periodIndex mainSale period Index
   */
 function CheckTokenPeriodSale(uint256 _dateTimeStamp, uint8 _periodIndex) public onlyTokenOwner  view returns(bool) {

        mainSaleTokenDistrubution storage mainSaleTokenSale = mainSaleCountMapping[_periodIndex];
       require(mainSaleTokenSale.mainStartDate !=0);
        require(mainSaleTokenSale.mainSaleEndDate !=0);

        if( (mainSaleTokenSale.mainStartDate < _dateTimeStamp) && (mainSaleTokenSale.mainSaleEndDate > _dateTimeStamp) )
        return true;

        return false;
        
  }

  /**
   * @dev It check bonus details for given mainSale Period 
   * @param _periodIndex period to be verfied.
   */
 function getMainSaleDiscount(uint8 _periodIndex)  internal view returns(uint256) {

        mainSaleTokenDistrubution storage mainSaleToken = mainSaleCountMapping[_periodIndex];
        return mainSaleToken.discount;
  }

   /**
   * @dev It ends mainSale for s given period 
   * @param _periodIndex period to be End.
   */
  function EndMainSale(uint8 _periodIndex) public onlyTokenOwner 
  {

  mainSaleTokenDistrubution storage mainSaleTokenSale = mainSaleCountMapping[_periodIndex];
  mainSaleTokenSale.periodActive = false;

  //Whether we need to give refund instantly  , to be managed by Application not Samrt contract

  }

  /**
   * @dev Check if mainSale is On  for a particular period
   * @param _periodIndex period to be verfied.
   */
 function CheckIfMainSaleOn(uint8 _periodIndex)  public view returns(bool) {

        mainSaleTokenDistrubution storage mainSaleTokenSale = mainSaleCountMapping[_periodIndex];
        return(mainSaleTokenSale.periodActive);
        
  }


 /**
   * @dev It returns total mainSale count
   */
  function MainSaleCount() view public returns(uint256)
  {
     return mainSaleList.length;
  }

  /**
   * @dev Check limit of main sale for a particular 
   * @param _periodIndex period to be verfied.
   * @param  _tokenamount to be compared with maximit
   */
   
   // @param  _decimalValue this value to multipled with value so that compare uint become same
  function CheckMainSaleLimit( uint8 _periodIndex, uint256 _tokenamount) view public returns(bool)
  {

    mainSaleTokenDistrubution storage mainSaleTokenSale = mainSaleCountMapping[_periodIndex];

  //  uint256 maxCoinSoldDecimal = (mainSaleTokenSale.maxCoinSold).mul(10**uint256(_decimalValue));
    if(_tokenamount <= mainSaleTokenSale.maxCoinSold )
      return true;

      return false;

  }

/**
   * @dev it changes the limit of max token that can be sold in this period
   * @param _periodIndex period to be verfied.
   * @param _maxTokenTobeSold  maximum token to be sold
   */
  function changeMainSaleLimit( uint8 _periodIndex, uint256 _maxTokenTobeSold) public onlyTokenOwner  
  {

  mainSaleTokenDistrubution storage mainSaleTokenSale = mainSaleCountMapping[_periodIndex];
  mainSaleTokenSale.maxCoinSold = _maxTokenTobeSold.mul(10**uint256(decimals));


  }

  /**
   * @dev it changes the limit of max token that can be sold in this period
   * @param _currenttime check current time for mainSale
   */
  function checkMainSalePeriod( uint256 _currenttime) view internal returns(uint256)
  {

  if(mainSaleList.length  == 0)
    return 0;
  //get first MainSale period Index
  uint8 periodindex;
  for(periodindex = 0; periodindex <=mainSaleList.length; periodindex++)
  {
  mainSaleTokenDistrubution storage mainSaleTokenSale = mainSaleCountMapping[periodindex];
  if( (mainSaleTokenSale.mainStartDate <= _currenttime) && 
                      (_currenttime <=mainSaleTokenSale.mainSaleEndDate) )
  {
       return mainSaleTokenSale.periodIndex;
  }

  }
  
  return 0;

  }
  
  function updateCurrentTokenCount(uint8 _periodIndex, uint256 _token) internal 
  {
        mainSaleTokenDistrubution storage mainSaleTokenSale = mainSaleCountMapping[_periodIndex];
        mainSaleTokenSale.maxCoinSold = mainSaleTokenSale.maxCoinSold.sub(_token);
  }
  
  
  
}
    

 
    
// File: SafeMath.sol



pragma solidity ^0.4.24;
  
library SafeMath {
    function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
        if (_a == 0) {
            return 0;
        }
        uint256 c = _a * _b;
        require(c / _a == _b);
        return c;
    }

    function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
        // assert(_b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = _a / _b;
        require(_a == _b * c + _a % _b); // There is no case in which this doesn’t hold
        return c;
    }

    function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
        require(_b <= _a);
        return _a-_b;
    }

    function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
        uint256 c = _a + _b;
        require(c >= _a);
        return c;
    }
}