# GRUSS BETTING ASSISTANT COM INTERFACE
#
# This python file contains a class that is used to access Gruss Betting Assistant objects and functions via the com interface.
# Betting Assistant must be running and the COM server enabled.
# A jupyter notebook is available to demonstrate how to access the methods defined below.
#
# Version History
# 0001  Initial development

# Install required packages
# The followning package is required. This need only be installed once.
# pip3 install pythonnet

# Import required modules and packages
import clr
from System import Array, Int32, String, Double, Boolean, Byte  # Need to convert from Python structures to .net in some functions

# Define the Class
class BAHandler:

    # Constructor method (called when an object is created)
    def __init__(self, dllLocation):
        # Define class attributes
        # dllLocation is passed from the calling code to provide the address of the BettingAssistantCom.dll module.
        # e.g. dllLocation could equal "c:/Program Files (x86)/Betting Assistant/BettingAssistantCom.dll"
        clr.AddReference(dllLocation)
        
        # Now that we have established a referemce to the COM dll, import the ComClass.
        from BettingAssistantCom.Application import ComClass
        
        # Define a class attritbute to access the Com Class
        self.ba = ComClass()
    
    ### BETTING ASSISTANT PROPERTIES
    ### -------------------------------------------------------------------------------------------
    # This section accesses the ComClass properties
        
    # baVersion: String
    # Contains the current program version, e.g. [version]
    def ba_Version(self):
        return self.ba.baVersion
    
    # commissionRate: float
    # Contains the Commission Rate
    def Commission_Rate(self):
        return self.ba.commissionRate
    
    # includeNonRunners: Boolean
    # Indicates whether getPrices should include non runners.
    def get_Include_NonRunners(self):
        return self.ba.includeNonRunners
    
    def set_Include_NonRunners(self, toggle):
        self.ba.includeNonRunners = toggle
 
    # keepBetType: integer
    # Set the 'SP in play' and 'Keep in play' check boxes in the betting options. (Refer to the documentation for values)
    def get_Keep_Bet_Type(self):
        return self.ba.keepBetType
    
    def set_Keep_Bet_Type(self, toggle):
        self.ba.keepBetType = toggle

    # marketId: long
    # Current market id 
    def Market_Id(self):
        return self.ba.marketId
    
    # marketName: String
    # Contains the current market name.
    def Market_Name(self):
        return self.ba.marketName
    
    # refreshRate: float
    # Set or retrieve refresh rate in seconds.
    # Minimum allowed value is 0.2
    def get_Refresh_Rate(self):
        return self.ba.refreshRate
    
    def set_Refresh_Rate(self, value):
        self.ba.refreshRate = value

    # startTime: Date
    # Contains the scheduled start time of the current market.
    def Start_Time(self):
        return self.ba.startTime

    # tabIndex: Integer
    # 0 based index of tab pages currently open. e.g. use tabIndex=1 prior to placing a bet to place a bet on the second tab page.
    def Tab_Index(self):
        return self.ba.tabIndex
    
       
    ### BETTING ASSISTANT EVENTS
    ### -------------------------------------------------------------------------------------------
    # This section accesses the ComClass Events
    
    # TO DO
    
    
    ### BETTING ASSISTANT FUNCTIONS
    ### -------------------------------------------------------------------------------------------
    # This section accesses the ComClass Functions
    
    # addTabPage():Integer
    # Opens a new tab page.
    # Returns the number of pages currently open.
    def add_Tab_Page(self):
        return self.ba.addTabPage()
    
    # backField(odds:Double,stake:Double):String
    # Backs every selection in the currently displayed market in Betting Assistant.
    # The function returns the bet refs in a string array.
    # If the bet placement fails then each bet ref will contain the error message received from Betfair’s server.
    # Parameters
    # odds: odds to back at for each selection
    # stake: stake to back at for each selection  
    def back_Field(self, odds, stake):
        return self.ba.backField(odds, stake)
    
    # cancelAllBets():string
    # Cancels all unmatched bets in the currently displayed market.
    # Returns empty string if successful or an error message.
    def cancel_All_Bets(self):
        return self.ba.cancelAllBets()

    # cancelBet(ref:String,eventid:Long):float (Double)
    # Cancels an individual bet.
    # Returns the amount cancelled.
    # The eventid parameter is optional and if not supplied then the eventid of the current open market is used.
    def cancel_Bet(self, ref, event_Id=''):
        if event_Id == '':
            return self.ba.cancelBet(ref)
        else:
            return self.ba.cancelBet(ref, event_Id)
 
    # deleteTabPage(tabIndex:Integer):Integer
    # deletes an open tab page.
    # Returns the number of pages currently open.
    # If you specify a tabIndex that doesn't exist then -1 will be returned.
    # The tabIndex parameter start at zero and are numbered left to right for the open Tab Pages.
    def delete_Tab_Page(self, index):
        return self.ba.deleteTabPage(index)
    
    # getAllTradedVolume(waitForResult:Boolean):Array of 'TradedVolumeSelection'
    # Returns total amount traded at each price in an array of 'TradedVolumeSelection' objects.
    # Set waitForResult to false to call asynchronously (see getAllTradedVolumeComplete event).
    def get_All_Traded_Volume(self, waitForResult = True):
        return self.ba.getAllTradedVolume(waitForResult)

    #getBalance(exchangeId:integer):Balance
    #Returns current balance in a Balance object.
    #Parameters
    #    exchangeId: 1=UK server
    def get_Balance(self, exchangeId = 1):
        return self.ba.getBalance(exchangeId)
    
    # getBet(ref:string, exchangeId:Integer):BetDetail
    # Returns current status of a single bet in a BetDetail object.
    # exchangeId is optional and is 1 for UK by default.
    def get_Bet(self, ref, exchangeId = 1):
        return self.ba.getBet(ref, exchangeId)

    # getBets():Bet()
    # Returns all matched and unmatched bets for the currently displayed market in an array of 'Bet' object
    def get_Bets(self):
        return self.ba.getBets()
    
	# getDaysSinceLastRun(horse:string):Integer
	# Returns the number of days since the horse last run.
	# The horse parameter should be the name of the horse as provided in the Price object.
	# The silks column must be displayed in the prices grid in Betting Assistant for this data to be available.
    def get_Days_Since_Last_Run(self, horse):
        return self.ba.getDaysSinceLastRun(horse)

    # getEvents(eventId:integer):BfEevent()
    # Returns a list of events linked to the eventId in an array of BfEvent objects.
    # Parameters
    #   EventId: id of event returned from getSports or getEevents
    def Get_Events(self, eventId):
        return self.ba.getEvents(eventId)

    # getHorseForm(selecId:integer):String  (although the documentation says 'selecId' is an int you need to pass a string)
    # Returns the form of the horse.
    def get_Horse_Form(self, selecId):
        return self.ba.getHorseForm((String)(selecId))

    # getMarketDepth(WaitForResult:Boolean):Array of MarketDepthSelection
    # Returns the back and lay amounts currently offered in an array of MarketDepthSelection objects.
    # Set waitForResult to false to call asynchronously (see getMarketDepthComplete event).
    def get_Market_Depth(self, WaitForResult = True):
        return self.ba.getMarketDepth(WaitForResult)

    # getMetaData(selecName:String):selectionMetaData
    # Applies to horse racing markets where Betfair have provided the metadata. 
    # Returns an array of selectionMetaData object for the given horse name.
    def get_Meta_Data(self, selecName):
        return self.ba.getMetaData(selecName)
    
    # getPrices():Price()
    # Returns last refreshed prices for all selections in currently displayed market in an array of 'Price' object.
    def get_Prices(self):
        return self.ba.getPrices()
    
    # getSaddleCloth(horse as string):Integer
    # Return saddlecloth number of horse.
    # Parameters
    #   horse: name of horse
    def get_Saddle_Cloth(self, horse):
        return self.ba.getSaddleCloth(horse)

    # getSports():BfSport()
    # Returns list of available sports in an array of BfSport objects.
    def Get_Sports(self):
        return self.ba.getSports()   
    
    # getTradedVolume(selection:String):Array of TradedVolume
    # Returns total amount traded at each price for this selection in an array of TradedVolume objects.
    # Selection should contain the name of selection as returned in the Price object. 
    def Get_Traded_Volume(self, selection):
        return self.ba.getTradedVolume(selection)
    
    # getUserName():String
    # Returns currently logged in Betfair username.
    def Get_User_Name(self):
        return self.ba.getUserName()
    
    # layField(odds:Double,stake:Double):String
    # Lays every selection in the currently displayed market in Betting Assistant.
    # The function returns the bet refs in a string array.
    # If the bet placement fails then each bet ref will contain the error message received from Betfair’s server.
    # Parameters
    # odds: odds to lay at for each selection
    # stake: stake to lay at for each selection
    def Lay_Field(self, odds, stake):
        return self.ba.layField(odds, stake)
    
    # loadQuickPickList(marketType:byte, clearExisting:Boolean):String
    # Loads markets into quick pick list.
    # Returns an empty string if successful or an error message.
    # clearExisting: Optional, default is true.
    # If set to true then the quick pick list will be emptied first otherwise the markets will be added to existing markets.
    def Load_QuickPick_List(self, marketType, clearExisting):
        return self.ba.loadQuickPickList(marketType, clearExisting)
    
    # openFirstQuickPickMarket():String
    # Opens the first market in the quick pick list.
    # Possible return values are OK, NO_MARKETS, INVALID_TAB_INDEX, UNKNOWN_ERROR
    def Open_First_QuickPick_Market(self):
        return self.ba.openFirstQuickPickMarket()
    
    # openLastQuickPickMarket():String
    # Opens the last market in the quick pick list.
    # Possible return values are OK, NO_MARKETS, INVALID_TAB_INDEX, UNKNOWN_ERROR
    def Open_Last_QuickPick_Market(self):
        return self.ba.openLastQuickPickMarket()
    
    # openMarket(eventId:integer,exchangeId:integer):String
    # Displays the market in the current tab page in Betting Assistant. 
    # Returns empty string if successful or error message.
    # Parameters
    # EventId: event id of market as retrieved in getEvents
    # exchangeId: 1=UK
    def Open_Market(self, eventId, exchangeId = 1):
        return self.ba.openMarket(eventId, exchangeId)
    
    # openNextQuickPickMarket():String
    # Opens the next market in the quick pick list.
    # Possible return values are OK, NO_MARKETS, INVALID_TAB_INDEX, UNKNOWN_ERROR
    def Open_Next_QuickPick_Market(self):
        return self.ba.openNextQuickPickMarket()
    
    # openPreviousQuickPickMarket():String
    # Opens the previous market in the quick pick list.
    # Possible return values are OK, NO_MARKETS, INVALID_TAB_INDEX, UNKNOWN_ERROR
    def Open_Previous_QuickPick_Market(self):
        return self.ba.openPreviousQuickPickMarket()
    
    # placeBet(selecId:Integer,betType:String,odds:Double,stake:Double,waitForResult:Boolean,
    #          token:String,fillKillDelay:Double,KeepBetType:Integer):String
    # Places a bet on 1 selection and returns bet reference or error message received from Betfair’s server.
    # Parameters
    # selecId: index of selection, eg. to place a bet on the 3rd selection down selecId=2
    # betType: “B” to back and “L” to lay.
    # Odds: required odds
    # Stake: required stake
    # waitForResult: Set to false to place bet in background and get result in betPlaced event. 
    #                Set to true to wait for bet placement.
    # token: An identifier to be returned in betPlaced event to match to original request. Can be any string value you want.
    # fillKillDelay: Optional. If used the bet will be cancelled if unmatched after a specified number of seconds, 
    #                e.g. 0.5 for half a second.
    # KeepBetType: Optional. If used this option will override the KeepBetType global property if that was previously set.
    #                         Use 0 to set 'Cancel when market turns in play', 
    #                             1 to set 'do not cancel when market turns in play' or 
    #                             2 to set 'Convert to SP when market turns in play'
    #                         Use 6 to place at SP (odds will be used to limit the SP bet, 
    #                         e.g. max odds for a lay bet and min odds for a back bet, 
    #                              use 0 if you do not want to set a limit)
    def place_Bet(self, selecId, betType, odds, stake, waitForResult, token = '', fillKillDelay=-1, keepBetType = 0):
        if fillKillDelay == -1:
            return self.ba.placeBet(selecId, betType, odds, stake, waitForResult, token, KeepBetType=keepBetType)
        else:
            return self.ba.placeBet(selecId, betType, odds, stake, waitForResult, token, fillKillDelay, keepBetType)

    # placeBets(selecId:Array of Integer, betType:Array of String, odds:Array of Double, stake:Array of double, 
    #           waitForResult:Boolean, token:Array of String, fillKillDelay:Double, KeepBetType:Integer):Array of String
    # Places bets on a batch of selections and returns bet references in an array or an error message from Betfair’s server.
    # See placeBet for description of parameters.
    def Place_Bets(self, selecIds, betTypes, odds, stakes, waitForResult, tokens = '', fillKillDelay=-1, keepBetType = 0):
        if fillKillDelay > 0:
            response = self.ba.placeBets(Array[Int32](selecIds), Array[String](betTypes), 
                                         Array[Double](odds), Array[Double](stakes), 
                                         True, Array[String](tokens), fillKillDelay, KeepBetType=keepBetType)    
        else:
            response = self.ba.placeBets(Array[Int32](selecIds), Array[String](betTypes), 
                                         Array[Double](odds), Array[Double](stakes), 
                                         True, Array[String](tokens), KeepBetType=keepBetType)    
        
        return list(response)  # Need to convert .net string array in to python 
    
    # refreshMarkets
    # refreshMarkets():Boolean
    # Simulates clicking on the "Refresh" button above the Market Tree to reload the Betfair Market data.  
    def Refresh_Markets(self):
        return self.ba.refreshMarkets()
    
    # setQuickPickAutoSelect(waitUntilAfterOff:boolean,timeBeforeOff:integer):String
    # Set quick pick list auto select options (use in conjunction with loadQuickPickList function)
    # Returns empty string if successful or error message.
    # Parameters
    # waitUntilAfterOff: wait until market has been suspended after scheduled off or turned in play 
    #   before switching markets: true or false
    # timeBeforeOff: number of minutes before scheduled off to select market 
    #   (eg. 1 = market will be selected 1 minute before scheduled off)
    def Set_QuickPick_Auto_Select(self, waitUntilAfterOff, timeBeforeOff):
        return self.ba.setQuickPickAutoSelect(waitUntilAfterOff, timeBeforeOff)
    
    # updateBet(ref:String, oldOdds:Double, oldStake:Double, newOdds:Double, newStake:Double,exchangeId as Integer)
    # Update stake or odds of a bet (both cannot be updated at the same time).
    # exchangeId is optional, if not provided it will default to 1 for UK exchange, specify 2 for Australian exchange.
    # Returns UpdateBetResult object.
    def Update_Bet(self, ref, oldOdds, oldStake, newOdds, newStake, exchangeId = 1):
        return self.ba.updateBet(String(str(ref)), Double(float(oldOdds)), Double(float(oldStake)), Double(float(newOdds)), Double(float(newStake)), Int32(int(exchangeId)))
    