# -*- coding: utf-8 -*-

# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code

from ccxt.base.exchange import Exchange

# -----------------------------------------------------------------------------

try:
    basestring  # Python 3
except NameError:
    basestring = str  # Python 2
import math
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import OnMaintenance
from ccxt.base.errors import InvalidNonce


class tokensnet (Exchange):

    def describe(self):
        return self.deep_extend(super(tokensnet, self).describe(), {
            'id': 'tokensnet',
            'name': 'Tokens.net',
            'countries': ['GB'],
            'rateLimit': 200,  # 1R/200ms = 300/min = 5/s
            'certified': False,
            'has': {
                'fetchMarkets': True,
                'fetchTickers': True,
                'fetchTicker': True,
                'fetchCurrencies': True,
                'fetchOrderBook': True,
                'fetchTrades': True,
                'fetchBalance': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'createOrder': True,
                'cancelOrder': True,
                'fetchMyTrades': True,
                'fetchOHLCV': False,
                'fetchStatus': False,
                'fetchOrders': False,
                'fetchClosedOrders': False,
            },
            'urls': {
                'api': 'https://api.tokens.net/',
                'doc': 'https://www.tokens.net/api/',
                'logo': 'https://user-images.githubusercontent.com/4224211/52278879-67fcea80-2958-11e9-9cec-94a7333a9d7e.png',
                'www': 'https://www.tokens.net',
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'api': {
                'public': {
                    'get': [
                        'public/trading-pairs/get/all/',
                        'public/ticker/all/',
                        'public/ticker/{pair}/',
                        'public/currency/all/',
                        'public/order-book/{pair}/',
                        'public/trades/hour/{pair}/',
                    ],
                },
                'private': {
                    'get': [
                        'private/balance/all/',
                        'private/orders/get/all/',
                        'private/orders/get/{pair}/',
                        'private/orders/get/{id}/',
                        'private/trades/all/{page}/',
                        'private/trades/{pair}/{page}/',
                    ],
                    'post': [
                        'private/orders/add/limit/',
                        'private/orders/cancel/{id}/',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'taker': 0.2 / 100,
                    'maker': 0,
                },
            },
            'exceptions': {
                '100': ArgumentsRequired,
                '101': ArgumentsRequired,
                '102': ArgumentsRequired,
                '110': InvalidNonce,
                '111': InvalidNonce,
                '120': PermissionDenied,
                '121': PermissionDenied,
                '122': PermissionDenied,
                '123': BadRequest,
                '124': BadRequest,
                '130': BadRequest,
                '131': BadRequest,
                '140': BadRequest,
                '150': BadRequest,
                '151': BadRequest,
                '152': BadRequest,
                '160': BadRequest,
                '161': BadRequest,
                '429': DDoSProtection,
                '503': OnMaintenance,
            },
        })

    def fetch_markets(self, params={}):
        markets = self.publicGetPublicTradingPairsGetAll(params)
        #    {
        #        "xtzeurs": {
        #            "counterCurrency": "eurs",
        #            "priceDecimals": 4,
        #            "baseCurrency": "xtz",
        #            "amountDecimals": 6,
        #            "minAmount": "1",
        #            "title": "XTZ/EURS"
        #         },
        #         ...
        #    }
        result = []
        keys = list(markets.keys())
        for i in range(0, len(keys)):
            market = markets[keys[i]]
            id = keys[i]
            baseId = self.safe_string_upper(market, 'baseCurrency')
            base = self.safe_currency_code(baseId)
            quoteId = self.safe_string_upper(market, 'counterCurrency')
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            precision = {
                'price': market['priceDecimals'],
                'amount': market['amountDecimals'],
                'cost': market['amountDecimals'],
            }
            cost = self.safe_float(market, 'minAmount')
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': True,
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': math.pow(10, -precision['amount']),
                        'max': None,
                    },
                    'price': {
                        'min': math.pow(10, -precision['price']),
                        'max': None,
                    },
                    'cost': {
                        'min': float(cost),
                        'max': None,
                    },
                },
                'info': market,
            })
        return result

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        tickers = self.publicGetPublicTickerAll(params)
        #    {
        #        "status": "ok",
        #        "timestamp": 1572945773,
        #        "btcusdt": {
        #            "last": "9381.24",
        #            "volume_usdt": "272264.02067160",
        #            "open": "9616.58",
        #            "volume": "28.54974563",
        #            "ask": "9381.25",
        #            "low": "9340.37",
        #            "bid": "9371.87",
        #            "vwap": "9536.47798478",
        #            "high": "9702.76"
        #        },
        #        ...
        #    }
        result = []
        keys = list(tickers.keys())
        for i in range(0, len(keys)):
            symbolId = keys[i]
            ticker = tickers[symbolId]
            if symbolId != 'status' and symbolId != 'timestamp' and symbolId in self.marketsById:
                market = self.marketsById[symbolId]
                result.append(self.parse_ticker(ticker, market))
        return self.index_by(result, 'symbol')

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.markets[symbol]
        ticker = self.publicGetPublicTickerPair(self.extend({
            'pair': self.market_id(symbol),
        }, params))
        #    {
        #        "vwap": "9536.47798478",
        #        "volume_usdt": "272264.02067160",
        #        "open": "9616.58",
        #        "ask": "9381.25",
        #        "status": "ok",
        #        "high": "9702.76",
        #        "timestamp": 1572265101,
        #        "volume": "28.54974563",
        #        "bid": "9371.87",
        #        "last": "9381.24",
        #        "low": "9340.37"
        #    }
        return self.parse_ticker(ticker, market)

    def fetch_currencies(self, params={}):
        response = self.publicGetPublicCurrencyAll(params)
        #    {
        #        "status": "ok",
        #        "timestamp": 1572945773,
        #        "currencies": {
        #            "BTC": {
        #                "canWithdraw": True,
        #                "ethValue": "50.00000000",
        #                "namePlural": "Bitcoins",
        #                "minimalOrder": "0.0001",
        #                "withdrawalFee": "0.0003",
        #                "btcValue": "1.00000000",
        #                "decimals": 8,
        #                "name": "Bitcoin",
        #                "usdtValue": "9302.93000000"
        #            },
        #            ...
        #        },
        #    },
        currencies = self.safe_value(response, 'currencies', [])
        # Currencies are returned as a dict(historical reasons)
        keys = list(currencies.keys())
        result = {}
        for i in range(0, len(keys)):
            currencySymbol = keys[i]
            currency = currencies[currencySymbol]
            result[currencySymbol] = {
                'id': currencySymbol,
                'name': self.safe_string(currency, 'name'),
                'code': currencySymbol,
                'precision': self.safe_integer(currency, 'decimals'),
                'info': currency,
            }
        return result

    def fetch_order_book(self, symbol, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        orderbook = self.publicGetPublicOrderBookPair(self.extend({
            'pair': market['id'],
        }, params))
        #    {
        #        "timestamp":1573031500,
        #        "status":"ok",
        #        "asks":[
        #            [
        #                "0.02126453",
        #                "192.94"
        #            ],
        #            [
        #                "0.02322024",
        #                "192.95"
        #            ],
        #            ...
        #        ],
        #        "bids":[
        #            [
        #                "0.00486816",
        #                "192.75"
        #            ],
        #            [
        #                "0.02811401",
        #                "192.56"
        #            ],
        #            ...
        #        ]
        #    }
        timestamp = self.safe_timestamp(orderbook, 'timestamp')
        parsedOrderbook = self.parse_order_book(orderbook, timestamp, 'bids', 'asks', 1, 0)
        parsedOrderbook['nonce'] = self.nonce()
        return parsedOrderbook

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        response = self.publicGetPublicTradesHourPair(self.extend({
            'pair': market['id'],
        }, params))
        #    {
        #        "timestamp":1573031391,
        #        "trades":[
        #            {
        #                "datetime":1573028240,
        #                "price":"192.93",
        #                "id":3655654,
        #                "amount":"1.51166933",
        #                "type":"buy"
        #            },
        #            ...
        #        ],
        #        "status":"ok"
        #    }
        return self.parse_trades(response['trades'], market, since, limit)

    def fetch_balance(self, params={}):
        self.load_markets()
        response = self.privateGetPrivateBalanceAll(params)
        #    {
        #      timestamp: 1573045496,
        #      status: 'ok',
        #      balances: {
        #        BTC: {
        #          available: '0.00009176',
        #          usdtValue: '40.60',
        #          btcValue: '0.00433932',
        #          total: '0.00433932'
        #        },
        #        ...
        #    }
        balances = self.safe_value(response, 'balances', [])
        keys = list(balances.keys())
        result = {
            'info': response,
        }
        for i in range(0, len(keys)):
            currencySymbol = keys[i]
            balance = balances[currencySymbol]
            code = self.safe_currency_code(currencySymbol)
            total = self.safe_float(balance, 'total', 0)
            available = self.safe_float(balance, 'available', 0)
            account = self.account()
            account['free'] = available
            account['used'] = total - available
            result[code] = account
        return self.parse_balance(result)

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        response = None
        if symbol is None:
            response = self.privateGetPrivateOrdersGetAll()
        else:
            response = self.privateGetPrivateOrdersGetPair()
        #    {
        #        timestamp: 1573045496,
        #        status: 'ok',
        #        openOrders: [
        #            {
        #                id: '682aeee9-2826-416c-a15a-7e95c1645495',
        #                price: '0.00000448',
        #                type: 'sell',
        #                takeProfit: null,
        #                created: 1571145633,
        #                currencyPair: 'dtrbtc',
        #                remainingAmount: '30.00000000',
        #                amount: '30.00000000'
        #            },
        #            ...
        #        ],
        #    }
        orders = self.safe_value(response, 'openOrders', [])
        result = []
        for i in range(0, len(orders)):
            orderExchange = response['openOrders'][i]
            self.extend(orderExchange, {
                'status': 'open',
            })
            order = self.parse_order(orderExchange)
            result.append(order)
        return self.filter_by_since_limit(result, since, limit)

    def fetch_order(self, id, symbol=None, params={}):
        self.load_markets()
        response = self.privateGetPrivateOrdersGetId(self.extend({
            'id': id,
        }, params))
        #    {
        #      id: '821e4178-5b97-4ead-8f29-85cd16d3f891',
        #      price: '0.00000444',
        #      amount: '30.00000000',
        #      timestamp: 1573129169,
        #      status: 'ok',
        #      takeProfit: '0.00000448',
        #      type: 'buy',
        #      remainingAmount: '0.00000000',
        #      currencyPair: 'dtrbtc',
        #      orderStatus: 'filled',
        #      created: 1571145631,
        #      trades: [
        #          {
        #              value: '0.00013320',
        #              type: 'sell',
        #              price: '0.00000444',
        #              amount: '30.00000000',
        #              datetime: 1571145632,
        #              id: 3509679
        #          },
        #          ...
        #      ]
        #    }
        return self.parse_order(response)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'tradingPair': market['id'],
            'amount': self.amount_to_precision(symbol, amount),
            'side': side,
            'type': type,
            'price': self.price_to_precision(symbol, price),
        }
        # not !not  takeProfit = forms.DecimalField(required=False, max_digits=30, decimal_places=8)
        # not !not  expireDate = forms.IntegerField(required=False)
        response = self.privatePostPrivateOrdersAddLimit(self.extend(request, params))
        order = {
            'id': self.safe_string(response, 'orderId'),
            'currencyPair': market['id'],
            'created': self.safe_integer(response, 'timestamp'),
            'status': 'open',
            'type': type,
            'side': side,
            'price': price,
            'amount': amount,
            'remainingAmount': 0,
            'info': response,
        }
        return self.parse_order(order, market)

    def cancel_order(self, id, symbol=None, params={}):
        response = self.privatePostPrivateOrdersCancelId(self.extend({
            'id': id,
        }, params))
        return response

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        response = None
        if symbol is None:
            response = self.privateGetPrivateTradesAllPage(self.extend({
                'page': '1',
            }, params))
        else:
            market = self.market(symbol)
            response = self.privateGetPrivateTradesPairPage(self.extend({
                'pair': market['id'],
                'page': '1',
            }, params))
        #    {
        #        trades: [
        #            {
        #                id: 3625965,
        #                datetime: 1572720454,
        #                type: 'sell',
        #                amount: '30.00000000',
        #                currencyPair: 'dtrbtc',
        #                price: '0.00000311',
        #                fee: '0.00000000'
        #            },
        #            ...
        #        ],
        #        timestamp: 1573134052,
        #        page: 1,
        #        status: 'ok',
        #        pages: 9
        #    }
        trades = self.safe_value(response, 'trades', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_ticker(self, ticker, market=None):
        symbol = None
        symbolId = self.safe_string(market, 'symbol')
        if (market is None) and (symbolId in list(self.marketsById.keys())):
            market = self.marketsById[symbolId]
        if market is not None:
            symbol = market['symbol']
        lastPrice = self.safe_float(ticker, 'last')
        openPrice = self.safe_float(ticker, 'open')
        change = lastPrice - openPrice
        percentage = 0
        if openPrice != 0:
            percentage = (change / openPrice) * 100
        timestamp = self.safe_timestamp(ticker, 'timestamp')
        baseVolume = self.safe_float(ticker, 'volume')
        vwap = self.safe_float(ticker, 'vwap')
        quoteVolume = baseVolume * vwap
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'high'),
            'low': self.safe_float(ticker, 'low'),
            'bid': self.safe_float(ticker, 'bid'),
            'bidVolume': None,
            'ask': self.safe_float(ticker, 'ask'),
            'askVolume': None,
            'vwap': vwap,
            'open': openPrice,
            'close': lastPrice,
            'last': lastPrice,
            'previousClose': None,
            'change': change,
            'percentage': percentage,
            'average': None,
            'baseVolume': self.safe_float(ticker, 'volume'),
            'quoteVolume': quoteVolume,
            'info': ticker,
        }

    def parse_trade(self, trade, market=None):
        timestamp = self.safe_timestamp(trade, 'datetime')
        id = self.safe_string(trade, 'id')
        side = self.safe_string(trade, 'type')
        price = self.safe_float(trade, 'price')
        amount = self.safe_float(trade, 'amount')
        symbol = None
        if market is not None:
            symbol = self.safe_string(market, 'symbol')
        return {
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'id': id,
            'symbol': symbol,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': price * amount,
            'info': trade,
            'order': None,
            'takerOrMaker': None,
            'type': None,
            'fee': None,
        }

    def parse_order(self, order, market=None):
        id = self.safe_string(order, 'id')
        timestamp = self.safe_timestamp(order, 'created')
        status = self.parse_order_status('open')
        type = 'limit'
        side = self.safe_string_lower(order, 'type')
        price = self.safe_float(order, 'price')
        amount = self.safe_float(order, 'amount')
        remaining = self.safe_float(order, 'remainingAmount')
        filled = amount - remaining
        market = self.markets_by_id[order['currencyPair']]
        symbol = self.safe_string(market, 'symbol')
        cost = price * remaining
        trades = None
        tradeExchange = self.safe_value(order, 'trades')
        if tradeExchange is not None:
            trades = self.parse_trades(tradeExchange, market)
        return {
            'id': id,
            'symbol': symbol,
            'datetime': self.iso8601(timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': None,
            'status': status,
            'type': type,
            'side': side,
            'price': price,
            'amount': amount,
            'remaining': remaining,
            'cost': cost,
            'filled': filled,
            'trades': trades,
            'info': order,
            'fee': None,
        }

    def parse_order_status(self, status):
        statuses = {
            'open': 'open',
            'filled': 'closed',
            'canceled': 'canceled',
            'expired': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api']
        url += self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        if api == 'public':
            if query:
                url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            nonce = str(self.nonce())
            auth = nonce + self.apiKey
            signature = self.encode(self.hmac(self.encode(auth), self.encode(self.secret)))
            if method != 'GET':
                body = self.urlencode(query)
            headers = {
                'key': self.apiKey,
                'signature': signature.upper(),
                'nonce': nonce,
                'Content-Type': 'application/x-www-form-urlencoded',
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        # 100 API Key is missing
        # 101 Nonce is missing
        # 102 Signature is missing
        # 110 Nonce has to be integer
        # 111 Provided nonce is less or equal to the last nonce
        # 120 Invalid API key
        # 121 Signature is invalid
        # 122 Insufficient permissions
        # 123 IP address not allowed
        # 124 API key not confirmed by e-mail
        # 130 Invalid trading pair
        # 131 Invalid order id
        # 140 Only opened orders can be canceled
        # 150 Parameter {parameter} is invalid with error: {error}
        # 151 Invalid input short
        # 152 General error: {}
        # 160 Invalid currency code
        # 161 Invalid {}
        # 429 API rate limit exceeded
        # 503 API is down for maintenance
        if not isinstance(body, basestring):
            return
        if len(body) == 0:
            return
        if body[0] != '{':
            return
        status = self.safe_string(response, 'status')
        if status == 'error':
            errorCode = self.safe_string(response, 'errorCode')
            reason = self.safe_string(response, 'reason')
            exceptions = self.exceptions
            if errorCode in exceptions:
                raise exceptions[errorCode](reason)
            raise ExchangeError(reason)
