<?php
namespace TronTool;

// use common\helpers\RedisHelper;

class TronApi{
  protected $fullNode;
  protected $solidityNode;
  protected $eventNode;

  static function mainNet(){
    $time = time();
    $key = 'trc20_'.$time;
    // if(!RedisHelper::isAllow($key,1,5)){
    //   sleep(1);
    // }
    return new self('https://api.trongrid.io');
  }

  static function testNet(){
    return new self('https://api.shasta.trongrid.io');
  }

  static function testNetNilo(){
      return new self('https://nile.trongrid.io');
  }

  function __construct($fullNodeUrl,$solidityNodeUrl=null,$eventNodeUrl=null){
    if(is_null($solidityNodeUrl)){
      $solidityNodeUrl = $fullNodeUrl;
    }
    if(is_null($eventNodeUrl)){
      $eventNodeUrl = $fullNodeUrl;
    }
    $this->fullNode = new NodeClient($fullNodeUrl);
    $this->solidityNode = new NodeClient($solidityNodeUrl);
    $this->eventNode = new NodeClient($eventNodeUrl);
  }

  function getNextMaintenanceTime(){
    return $this->fullNode->get('/wallet/getnextmaintenancetime');
  }

  function timeUntilNextVoteCycle(){
    $args = func_get_args();
    return $this->getNextMaintenanceTime(...$args);
  }

  function broadcastTransaction($tx){
    return $this->fullNode->post('/wallet/broadcasttransaction',$tx);
  }

  function sendRawTransaction(){
    $args = func_get_args();
    return $this->broadcastTransaction(...$args);
  }
  /*
  function createTransaction($to,$amount,$from){
    $payload = [
      'to_address' => Address::decode($to),
      'owner_address' => Address::decode($from),
      'amount' => $amount
    ];
    return $this->fullNode->post('/wallet/createtransaction',$payload);
  }
  */

  function sendTrx(){
    $args = func_get_args();
    return $this->createTransaction(...$args);
  }

  function getContractEvents($contractAddress,$since){
    $api = '/event/contract/' . $contractAddress;
    $payload = [ 'since' => $since, 'sort' => 'block_timestamp' ];
    return $this->eventNode->get($api,$payload);
  }

  function getTransactionEvents($txid){
    $api = '/event/transaction/' . $txid;
    return $this->eventNode->get($api,[]);
  }

  /*
  function triggerSmartContract($contractAddress,$functionSelector,$parameter,$fromAddress,$feeLimit=1000000000,$callValue=0,$bandwidthLimit=0){
    $payload = [
      'contract_address' => Address::decode($contractAddress),
      'function_selector' => $functionSelector,
      'parameter' => $parameter,
      'owner_address' =>  Address::decode($fromAddress),
      'fee_limit'     =>  $feeLimit,
      'call_value'    =>  $callValue,
      'consume_user_resource_percent' =>  $bandwidthLimit,
    ];
    return $this->fullNode->post('/wallet/triggersmartcontract', $payload);
  }
  */

  function getAccount($address,$confirmed=true){
    $payload = [
      'address' => Address::decode($address)
    ];
    
    if($confirmed){
      $res = $this->fullNode->get('/wallet/getaccount',$payload);
      return $res;
    }else{
      return $this->solidityNode->get('/walletsolidity/getaccount',$payload);
    }
  }

  function getBalance($address,$confirmed=true){
    $accountInfo = $this->getAccount($address,$confirmed);
    if(!isset($accountInfo->balance)){
      throw new \Exception('Balance error. Maybe you should send 1.1 trx to this address to activate it.');
    }
    return $accountInfo->balance;
  }

  function getUncomfirmedBalance($address){
    return $this->getBalance($address,false);
  }

  function getAccountNet($address){
    $payload = [
      'address' => Address::decode($address)
    ];
    return $this->fullNode->post('/wallet/getaccountnet',$payload);
  }

  function getBandwidth(){
    $args = func_get_args();
    return $this->getAccountNet(...$args);
  }

  function getAccountResource($address){
    $payload = [
      'address' => Address::decode($address)
    ];
    return $this->fullNode->post('/wallet/getaccountresource',$payload);
  }

  function getContract($address){
    $payload = [
      'value' => Address::decode($address)
    ];
    return $this->fullNode->get('/wallet/getcontract',$payload);
  }

  function getChainParameters(){
    return $this->fullNode->get('/wallet/getchainparameters',[]);
  }

  function getNodeInfo(){
    return $this->fullNode->get('/wallet/nodeinfo',[]);
  }

  function listNodes(){
    return $this->fullNode->get('/wallet/listnodes',[]);
  }

  //get|post?
  function getNowBlock($confirmed=true){
    if($confirmed){
      return $this->solidityNode->get('/walletsolidity/getnowblock',[]);
    }else{
      return $this->fullNode->get('/wallet/getnowblock',[]);
    }
  }

  function getCurrentBlock(){
    $args = func_get_args();
    return $this->getNowBlock(...$args);
  }

  function getBlockById($hash){
    $payload = [
      'value' => $hash
    ];
    return $this->fullNode->post('/wallet/getblockbyid',$payload);
  }

  function getBlockByHash(){
    $args = func_get_args();
    return $this->getBlockById(...$args);
  }

  function getBlockByNum($num){
    $payload = [
      'num' => $num
    ];
    return $this->fullNode->post('/wallet/getblockbynum',$payload);
  }

  function getBlockByNumber(){
    $args = func_get_args();
    return $this->getBlockByNum(...$args);
  }

  function getBlockByLimitNext($start,$end){
    $payload = [
      'startNum' => $start,
      'endNum' => $end
    ];
    return $this->fullNode->get('/wallet/getblockbylimitnext',$payload);
  }

  function getBlockRange(){
    $args = func_get_args();
    return $this->getBlockByLimitNext(...$args);
  }

  function getTransactionById($txid,$confirmed=true){
    $payload = [
      'value' => $txid
    ];
    if($confirmed){
      return $this->solidityNode->post('/walletsolidity/gettransactionbyid',$payload);
    }else{
      return $this->fullNode->post('/wallet/gettransactionbyid',$payload);
    }
  }

  function getTransaction(){
    $args = func_get_args();
    return $this->gettransactionbyid(...$args);
  }

  function getConfirmedTransaction(){
    $args = func_get_args();
    return $this->getTransactionById(...$args);
  }

  function getTransactionInfoById($txid,$confirmed=true){
    $payload = [
      'value' => $txid
    ];
    if($confirmed){
      return $this->solidityNode->post('/walletsolidity/gettransactioninfobyid',$payload);
    }else{
      return $this->fullNode->post('/wallet/gettransactioninfobyid',$payload);
    }
  }

  function getTransactionInfo(){
    $args = func_get_args();
    return $this->getTransactionInfoById(...$args);
  }

  function getUnconfirmedTransactionInfo($txid){
    return $this->getTransactionInfoById($txid,false);
  }


  //all|from|to
  function getTransactionsByAddress($address,$direction='from',$offset=0,$limit=30){
    $payload = [
      'account' => [
        'address' => Address::decode($address)
      ],
      'offset' => $offset,
      'limit' => $limit
    ];
    $api = '/walletextension/gettransactions' . $direction . 'this';
    return $this->solidityNode->post($api,$payload);
  }

  function getReward($address,$confirmed=true){
    $payload = [
      'address' => Address::decode($address)
    ];
    if($confirmed){
      return $this->solidityNode->post('/walletsolidity/getreward',$payload);
    }else{
      return $this->fullNode->post('/wallet/getreward',$payload);
    }
  }

  function getUnconfirmedReward($address){
    return $this->getReward($address,false);
  }

  function getApprovedList($tx){
    return $this->fullNode->post('/wallet/getapprovedlist',$tx);
  }

  function getSignWeight($tx){
    return $this->fullNode->post('/wallet/getsignweight',$tx);
  }

  function listWitnesses($confirmed=true){
    if($confirmed){
      return $this->solidityNode->get('/walletsolidity/listwitnesses',[]);
    }else{
      return $this->fullNode->get('/walletsolidity/listwitnesses',[]);
    }
  }

  function listSuperRepresentatives(){
    $args = func_get_args();
    return $this->listWitnesses(...$args);
  }

  /*txbuilder*/
  function createTransaction($to,$amount,$from){
    $payload = [
      'to_address' => Address::decode($to),
      'owner_address' => Address::decode($from),
      'amount' => $amount
    ];
    $ret = $this->fullNode->post('/wallet/createtransaction',$payload);
    return $ret;
  }

  function transferAsset($to,$asset,$amount,$from){
    $payload = [
      'to_address' => Address::decode($to),
      'asset_name' => bin2hex($asset),
      'amount' => $amount,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/transferasset',$payload);
  }

  function sendAsset(){
    $args = func_get_args();
    return $this->transferAsset(...$args);
  }

  function sendToken(){
    $args = func_get_args();
    return $this->transferAsset(...$args);
  }

  function createAssetIssue($name,$abbr,$desc,$url,$supply,$trxRatio,$tokenRatio,$start,$end,$limit,$publicLimit,$frozenAmount,$frozenDays,$precision,$from){
    $payload = [
      'name' => bin2hex($name),
      'addr' => bin2hex($abbr),
      'total_supply' => $supply,
      'precision' => $precision,
      'trx_num' => $trxRatio,
      'num' => $tokenRatio,
      'start_time' => $start,
      'end_time' => $end,
      'description' => bin2hex($desc),
      'url' => bin2hex($url),
      'free_asset_net_limit' => $limit,
      'public_free_asset_net_limit' => $publicLimit,
      'frozen_supply' => [
        'frozen_amount' => $frozenAmount,
        'frozen_days' => $frozenDays
      ],
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/createassetissue',$payload);
  }

  function createToken(){
    $args = func_get_args();
    return $this->createAssetIssue(...$args);
  }

  function createAsset(){
    $args = func_get_args();
    return $this->createAssetIssue(...$args);
  }

  function updateAsset($url,$desc,$limit,$publicLimit,$from){
    $payload = [
      'url' => bin2hex($url),
      'description' => bin2hex($desc),
      'new_limit' => $limit,
      'new_public_limit' => $publicLimit,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/updateasset',$payload);
  }

  function updateToken(){
    $args = func_get_args();
    return $this->updateAsset(...$args);
  }

  function participateAssetIssue($to,$asset,$amount,$from){
    $payload = [
      'to_address' => Address::decode($to),
      'asset_name' => bin2hex($asset),
      'amount' => $amount,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/participateassetissue',$payload);
  }

  function purchaseAsset(){
    $args = func_get_args();
    return $this->participateAssetIssue(...$args);
  }

  function purchaseToken(){
    $args = func_get_args();
    return $this->participateAssetIssue(...$args);
  }

  function getAssetIssueById($token,$confirmed=true){
    $payload = [
      'value' => $token
    ];
    if($confirmed){
      return $this->solidityNode->post('/walletsolidity/getassetissuebyid',$payload);
    }else{
      return $this->fullNode->post('/wallet/getassetissuebyid',$payload);
    }
  }

  function getTokenById(){
    $args = func_get_args();
    return $this->getAssetIssueById(...$args);
  }

  function getAssetIssueByName($token){
    $payload = [
      'value' => bin2hex($token)
    ];
    return $this->fullNode->post('/wallet/getassetissuebyname',$payload);
  }

  function getTokenFromId(){
    $args = func_get_args();
    return $this->getAssetIssueByName(...$args);
  }

  function getAssetIssueList(){
    return $this->fullNode->get('/wallet/getassetissuelist',[]);
  }

  function listTokens(){
    $args = func_get_args();
    return $this->getAssetIssueList(...$args);
  }

  function getAssetIssueListByName($token){
    $payload = [
      'value' => bin2hex($token)
    ];
    return $this->fullNode->post('/wallet/getassetissuelistbyname',$payload);
  }

  function getTokenListByName(){
    $args = func_get_args();
    return $this->getAssetIssueListByName(...$args);
  }

  function getAssetIssueByAccount($address){
    $payload = [
      'address' => Address::decode($address)
    ];
    return $this->fullNode->post('/wallet/getassetissuebyaccount',$payload);
  }

  function getTokenIssuedByAddress(){
    $args = func_get_args();
    return $this->getAssetIssueByAccount(...$args);
  }

  function freezeBalance($balance,$duration,$type,$from,$receiver=null){
    $payload = [
      'freeze_balance' => $balance,
      'freeze_duration' => $duration,
      'resource' => $type,
      'owner_address' => Address::decode($from),
      'receiver_address' => Address::decode($receiver)
    ];
    return $this->fullNode->post('/wallet/freezebalance',$payload);
  }

  function unfreezeBalance($type,$from,$receiver=null){
    $payload = [
      'resource' => $type,
      'owner_address' => Address::decode($from),
      'receiver' => Address::decode($receiver)
    ];
    return $this->fullNode->post('/wallet/unfreezebalance',$payload);
  }

  function withdrawBalance($address){
    $payload = [
      'owner_address' => Address::decode($address)
    ];
    return $this->fullNode->post('/wallet/withdrawbalance',$payload);
  }

  function withdrawBlockRewards(){
    $args = func_get_args();
    return $this->withdrawBalance(...$args);
  }

  function createWitness($address,$url){
    $payload = [
      'owner_address' => Address::decode($address),
      'url' => bin2hex($url)
    ];
    return $this->fullNode->post('/wallet/createwitness',$payload);
  }

  function applyForSR(){
    $args = func_get_args();
    return $this->createWitness(...$args);
  }

  function getBrokerage($address,$confirmed=true){
    $payload = [
      'address' => Address::decode($address)
    ];
    if($confirmed){
      return $this->solidityNode->post('/walletsolidity/getbrokerage',$payload);
    }else{
      return $this->fullNode->post('/wallet/getbrokerage',$payload);
    }
  }

  function getUncomfirmedBrokerage($address){
    return $this->getBrokerage($address,false);
  }

  function voteWitnessAccount($address,$votes){
    $payload = [
      'owner_address' => Address::decode($address),
      'votes' => $votes
    ];
    return $this->fullNode->post('/wallet/votewitnessaccount',$payload);
  }

  function vote(){
    $args = func_get_args();
    return $this->voteWitnessAccount(...$args);
  }

  //trc20
  function deployContract($abi,$bytecode,$parameter,$name,$value,$from){
    $payload = [
      'abi' => $abi,
      'bytecode' => $bytecode,
      'parameter' => $parameter,
      'name' => $name,
      'call_value' => $value,
      'owner_address' => Address::decode($from),
      'fee_limit' => 1000000000,
      'origin_energy_limit' => 10000000,
      'consume_user_resource_percent' => 100
    ];
    return $this->fullNode->post('/wallet/deploycontract',$payload);
  }

  function createSmartContract(){
    $args = func_get_args();
    return $this->deployContract(...$args);
  }

  function triggerSmartContract($contract,$function,$parameter,$value,$from){
    $payload = [
      'contract_address' => Address::decode($contract),
      'function_selector' => $function,
      'parameter' => $parameter,
      'call_value' => $value,
      'owner_address' => Address::decode($from),
      'fee_limit' => 1000000000
    ];
    return $this->fullNode->post('/wallet/triggersmartcontract',$payload);
  }

  function triggerConstantSmartContract($contract,$function,$parameter,$value,$from,$confirmed=true){
    $payload = [
      'contract_address' => Address::decode($contract),
      'function_selector' => $function,
      'parameter' => $parameter,
      'call_value' => $value,
      'owner_address' => Address::decode($from),
      'fee_limit' => 1000000000
    ];
    if($confirmed){
      return $this->solidityNode->post('/walletsolidity/triggerconstantsmartcontract',$payload);
    }else{
      return $this->fullNode->post('/wallet/triggerconstantsmartcontract',$payload);
    }
  }

  function clearAbi($contract,$from){
    $payload = [
      'contract_address' => Address::decode($contract),
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/clearabi',$payload);
  }

  function updateSetting($contract,$userPercent,$from){
    $payload = [
      'contract_address' => Address::decode($contract),
      'consume_user_resource_percent' => $userPercent,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/updatesetting',$payload);
  }

  function updateEnergyLimit($contract,$limit,$from){
    $payload = [
      'contract_address' => Address::decode($contract),
      'origin_energy_limit' => $limit,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/updateenergylimit',$payload);
  }

  function updateBrokerage($brokerage,$from){
    $payload = [
      'brokerage' => $brokerage,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/updatebrokerage',$payload);
  }

  function updateAccount($name,$from){
    $payload = [
      'account_name' => bin2hex($name),
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/updateaccount',$payload);
  }

  function accountPermissionUpdate($ownerPermits,$witnessPermits,$activePermits,$from){
    $payload = [
      'owner' => $ownerPermits,
      'witness' => $witnessPermits,
      'active' => $activePermits,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/accountpermissionupdate',$payload);
  }

  function setAccountId($id,$from){
    $payload = [
      'account_id' => $id,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/setaccountid',$payload);
  }

  //dex
  function proposalCreate($parameters,$from){
    $payload = [
      'parameters' => $parameters,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/proposalcreate',$payload);
  }

  function createProposal(){
    $args = func_get_args();
    return $this->proposalCreate(...$args);
  }

  function listProposals(){
    return $this->fullNode->post('/wallet/listproposals',[]);
  }

  function proposalDelete($id,$from){
    $payload = [
      'proposal_id' => $id,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/proposaldelete',$payload);
  }

  function deleteProposal(){
    $args = func_get_args();
    return $this->proposalDelete(...$args);
  }

  function proposalApprove($id,$from){
    $payload = [
      'proposal_id' => $id,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/proposalapprove',$payload);
  }

  function voteProposal(){
    $args = func_get_args();
    return $this->proposalApprove(...$args);
  }

  function exchangeCreate($token1,$balance1,$token2,$balance2,$from){
    $payload = [
      'first_token_id' => bin2hex($token1),
      'first_token_balance' => $balance1,
      'second_token_id' => bin2hex($token2),
      'second_token_balance' => $balance2,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/exchangecreate',$payload);
  }

  function listExchanges(){
    return $this->fullNode->post('/wallet/listexchanges',[]);
  }

  function getPaginatedExchangeList($offset=0,$limit=30){
    $payload = [
      'offset' => $offset,
      'limit' => $limit
    ];
    return $this->fullNode->post('/wallet/getpaginatedexchangelist',$payload);
  }

  function listExchangePaginated(){
    $args = func_get_args();
    return $this->getPaginatedExchangeList(...$args);
  }

  function exchangeInject($exchange,$token,$quant,$from){
    $payload = [
      'exchange_id' => $exchange,
      'token_id' => bin2hex($token),
      'quant' => $quant,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/exchangeinject',$payload);
  }

  function injectExchangeToken(){
    $args = func_get_args();
    return $this->exchangeInject(...$args);
  }

  function exchangeWithdraw($exchange,$token,$quant,$from){
    $payload = [
      'exchange_id' => $exchange,
      'token_id' => bin2hex($token),
      'quant' => $quant,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/exchangewithdraw',$payload);
  }

  function withdrawExchangeTokens(){
    $args = func_get_args();
    return $this->exchangeWithdraw(...$args);
  }

  function exchangeTransaction($exchange,$token,$quant,$expected,$from){
    $payload = [
      'exchange_id' => $exchange,
      'token_id' => bin2hex($token),
      'quant' => $quant,
      'expected' => $expected,
      'owner_address' => Address::decode($from)
    ];
    return $this->fullNode->post('/wallet/exchangetransaction',$payload);
  }

  function getExchangeById($id){
    $payload = [
      'id' => $id
    ];
    return $this->fullNode->post('/wallet/getexchangebyid',$payload);
  }

  function tradeExchangeTokens(){
    $args = func_get_args();
    return $this->exchangeTransaction(...$args);
  }
}

