/******************************************************************************
 * Copyright © 2014-2016 The SuperNET Developers.                             *
 *                                                                            *
 * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at                  *
 * the top-level directory of this distribution for the individual copyright  *
 * holder information and the developer policies on copyright and licensing.  *
 *                                                                            *
 * Unless otherwise agreed in a custom licensing agreement, no part of the    *
 * SuperNET software, including this file may be copied, modified, propagated *
 * or distributed except according to the terms contained in the LICENSE file *
 *                                                                            *
 * Removal or modification of this copyright notice is prohibited.            *
 *                                                                            *
 ******************************************************************************/

#include "iguana777.h"

void iguana_walletlock(struct supernet_info *myinfo)
{
    memset(&myinfo->persistent_priv,0,sizeof(myinfo->persistent_priv));
    memset(myinfo->secret,0,sizeof(myinfo->secret));
    printf("wallet locked\n");
}

int32_t iguana_ismine(struct supernet_info *myinfo,uint8_t pubkey[65],uint8_t rmd160[20])
{
    /*int32_t i;
    for (i=0; i<myinfo->numaddrs; i++)
    {
        if ( memcmp(myinfo->addrs[i].rmd160,rmd160,sizeof(myinfo->addrs[i].rmd160)) == 0 )
        {
            memcpy(pubkey,myinfo->addrs[i].pubkey,bitcoin_pubkeylen(myinfo->addrs[i].pubkey));
            return(i);
        }
    }*/
    return(0);
}

uint8_t *iguana_rmdarray(struct iguana_info *coin,int32_t *numrmdsp,cJSON *array,int32_t firsti)
{
    int32_t i,n,j=0; char *coinaddr; uint8_t *addrtypes,*rmdarray = 0;
    *numrmdsp = 0;
    if ( array != 0 && (n= cJSON_GetArraySize(array)) > 0 )
    {
        *numrmdsp = n - firsti;
        rmdarray = calloc(1,(n-firsti) * 21);
        addrtypes = &rmdarray[(n-firsti) * 20];
        for (i=firsti; i<n; i++)
        {
            if ( (coinaddr= jstr(jitem(array,i),0)) != 0 )
            {
                bitcoin_addr2rmd160(&addrtypes[j],&rmdarray[20 * j],coinaddr);
                j++;
            }
        }
    }
    return(rmdarray);
}

struct iguana_waccount *iguana_waccountcreate(struct iguana_info *coin,char *account)
{
    struct iguana_waccount *waddr; int32_t len = (int32_t)strlen(account)+1;
    HASH_FIND(hh,coin->wallet,account,len,waddr);
    if ( waddr != 0 )
        return(waddr);
    waddr = mycalloc('w',1,sizeof(*waddr) + len);
    strcpy(waddr->account,account);
    HASH_ADD(hh,coin->wallet,account,len,waddr);
    return(waddr);
}

struct iguana_waccount *iguana_waccountfind(struct iguana_info *coin,char *account)
{
    struct iguana_waccount *waddr;
    HASH_FIND(hh,coin->wallet,account,strlen(account)+1,waddr);
    return(waddr);
}

struct iguana_waccount *iguana_waccountadd(struct iguana_info *coin,char *walletaccount,struct iguana_waddress *waddr)
{
    struct iguana_waccount *acct;
    if ( (acct= iguana_waccountfind(coin,walletaccount)) == 0 )
        acct = iguana_waccountcreate(coin,walletaccount);
    return(acct);
}

uint8_t *iguana_walletrmds(struct supernet_info *myinfo,struct iguana_info *coin,int32_t *numrmdsp)
{
    int32_t iter,n,m; struct iguana_waccount *acct,*tmp; uint8_t *pubkeys,*addrtypes,*rmdarray = 0; struct iguana_waddress *waddr,*tmp2;
    for (iter=n=m=0; iter<2; iter++)
    {
        HASH_ITER(hh,coin->wallet,acct,tmp)
        {
            HASH_ITER(hh,acct->waddrs,waddr,tmp2)
            {
                if ( iter == 0 )
                    n++;
                else if ( m < n )
                {
                    addrtypes[m] = waddr->type;
                    memcpy(&rmdarray[m * 20],waddr->rmd160,20);
                    memcpy(&pubkeys[m * 33],waddr->pubkey,33);
                    m++;
                }
            }
        }
        if ( iter == 0 )
        {
            rmdarray = calloc(n,20 + 1 + 33);
            addrtypes = &rmdarray[n * 20];
            pubkeys = &rmdarray[n * 21];
        }
    }
    return(rmdarray);
}

int32_t iguana_waccountswitch(struct iguana_info *coin,char *account,struct iguana_waccount *oldwaddr,int32_t oldind,char *coinaddr)
{
    // what if coinaddr is already in an account?
    //printf("change %s walletaccount.(%s) (%s) <- %s\n",coin->symbol,waddr->account,waddr->coinaddr,coinaddr);
    return(0);
}

struct iguana_waccount *iguana_waddressfind(struct iguana_info *coin,int32_t *indp,char *coinaddr)
{
    int32_t n=0; struct iguana_waccount *acct,*tmp; struct iguana_waddress *waddr,*tmp2;
    *indp = -1;
    HASH_ITER(hh,coin->wallet,acct,tmp)
    {
        HASH_ITER(hh,acct->waddrs,waddr,tmp2)
        {
            if ( strcmp(coinaddr,waddr->coinaddr) == 0 )
            {
                *indp = n;
                return(acct);
            }
            n++;
        }
    }
    return(0);
}

int32_t iguana_addressvalidate(struct iguana_info *coin,uint8_t *addrtypep,uint8_t rmd160[20],char *address)
{
    char checkaddr[64];
    bitcoin_addr2rmd160(addrtypep,rmd160,address);
    if ( bitcoin_address(checkaddr,*addrtypep,rmd160,20) == checkaddr && strcmp(address,checkaddr) == 0 && (*addrtypep == coin->chain->pubtype || *addrtypep == coin->chain->p2shtype) )
        return(0);
    else return(-1);
}

char *getnewaddress(struct supernet_info *myinfo,struct iguana_info *coin,char *account)
{
    struct iguana_waddress addr; char str[67]; cJSON *retjson = cJSON_CreateObject();
    if ( iguana_waddresscalc(coin->chain->pubtype,coin->chain->wiftype,&addr,rand256(1)) == 0 )
    {
        jaddstr(retjson,"result",addr.coinaddr);
        init_hexbytes_noT(str,addr.pubkey,33);
        jaddstr(retjson,"pubkey",str);
        jaddstr(retjson,"privkey",bits256_str(str,addr.privkey));
        jaddstr(retjson,"wif",addr.wifstr);
        init_hexbytes_noT(str,addr.rmd160,20);
        jaddstr(retjson,"rmd160",str);
        if ( iguana_waccountadd(coin,account,&addr) < 0 )
            jaddstr(retjson,"account","error adding to account");
        else jaddstr(retjson,"account",account);
    } else jaddstr(retjson,"error","cant create address");
    return(jprint(retjson,1));
}

char *getaccountaddress(struct supernet_info *myinfo,struct iguana_info *coin,char *account)
{
    struct iguana_waccount *wacct; struct iguana_waddress *waddr=0,addr; char str[67];cJSON *retjson;
    if ( account != 0 && account[0] != 0 )
    {
        if ( (wacct= iguana_waccountfind(coin,account)) == 0 )
        {
            if ( (waddr= iguana_waddresscalc(coin->chain->pubtype,coin->chain->wiftype,&addr,rand256(1))) == 0 )
                return(clonestr("{\"error\":\"cant generate address\"}"));
            iguana_waccountswitch(coin,account,0,-1,addr.coinaddr);
        }
        retjson = cJSON_CreateObject();
        jaddstr(retjson,"result",waddr->coinaddr);
        init_hexbytes_noT(str,addr.pubkey,33);
        jaddstr(retjson,"pubkey",str);
        jaddstr(retjson,"privkey",bits256_str(str,waddr->privkey));
        jaddstr(retjson,"wif",waddr->wifstr);
        init_hexbytes_noT(str,waddr->rmd160,20);
        jaddstr(retjson,"rmd160",str);
        jaddstr(retjson,"account",account);
        return(jprint(retjson,1));
    }
    return(clonestr("{\"error\":\"no account specified\"}"));
}

char *setaccount(struct supernet_info *myinfo,struct iguana_info *coin,char *account,char *coinaddr)
{
    struct iguana_waccount *wacct; uint8_t addrtype,rmd160[20]; struct iguana_waddress *waddr=0,addr; int32_t ind=-1;
    if ( coinaddr != 0 && coinaddr[0] != 0 && account != 0 && account[0] != 0 )
    {
        if ( iguana_addressvalidate(coin,&addrtype,rmd160,coinaddr) < 0 )
            return(clonestr("{\"error\":\"invalid coin address\"}"));
        if ( (wacct= iguana_waddressfind(coin,&ind,coinaddr)) == 0 )
        {
            if ( (waddr= iguana_waddresscalc(coin->chain->pubtype,coin->chain->wiftype,&addr,rand256(1))) == 0 )
                return(clonestr("{\"error\":\"cant generate address\"}"));
        }
        iguana_waccountswitch(coin,account,wacct,ind,coinaddr);
        return(clonestr("{\"result\":\"account set\"}"));
    }
    return(clonestr("{\"error\":\"need address and account\"}"));
}

char *getaccount(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr)
{
    struct iguana_waccount *wacct; uint8_t addrtype,rmd160[20]; cJSON *retjson; int32_t ind;
    if ( iguana_addressvalidate(coin,&addrtype,rmd160,coinaddr) < 0 )
        return(clonestr("{\"error\":\"invalid coin address\"}"));
    if ( (wacct= iguana_waddressfind(coin,&ind,coinaddr)) == 0 )
        return(clonestr("{\"result\":\"no account for address\"}"));
    retjson = cJSON_CreateObject();
    jaddstr(retjson,"result",wacct->account);
    return(jprint(retjson,1));
}

char *getaddressesbyaccount(struct supernet_info *myinfo,struct iguana_info *coin,char *account)
{
    struct iguana_waccount *subset; struct iguana_waddress *waddr,*tmp; cJSON *retjson,*array;
    retjson = cJSON_CreateObject();
    array = cJSON_CreateArray();
    if ( (subset= iguana_waccountfind(coin,account)) != 0 )
    {
        HASH_ITER(hh,subset->waddrs,waddr,tmp)
        {
            jaddistr(array,waddr->coinaddr);
        }
    } else jaddstr(retjson,"result","cant find account");
    jadd(retjson,"addresses",array);
    return(jprint(retjson,1));
}

char *sendtoaddress(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr,double amount,char *comment,char *comment2)
{
    uint8_t addrtype,rmd160[20];
    //sendtoaddress	<bitcoinaddress> <amount> [comment] [comment-to]	<amount> is a real and is rounded to 8 decimal places. Returns the transaction ID <txid> if successful.	Y
    if ( coinaddr != 0 && coinaddr[0] != 0 && amount > 0. )
    {
        if ( iguana_addressvalidate(coin,&addrtype,rmd160,coinaddr) < 0 )
            return(clonestr("{\"error\":\"invalid coin address\"}"));
        //amount = jdouble(params[1],0);
        //comment = jstr(params[2],0);
        //comment2 = jstr(params[3],0);
        printf("need to generate send %.8f to %s [%s] [%s]\n",dstr(amount),coinaddr,comment!=0?comment:"",comment2!=0?comment2:"");
    }
    return(clonestr("{\"error\":\"need address and amount\"}"));
}

char *jsuccess()
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

#include "../includes/iguana_apidefs.h"
#include "../includes/iguana_apideclares.h"

INT_ARRAY_STRING(bitcoinrpc,createmultisig,M,array,account)
{
    cJSON *retjson;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

char *iguana_addressconv(struct iguana_info *coin,char *destaddr,struct iguana_info *other,int32_t isp2sh,uint8_t rmd160[20])
{
    if ( bitcoin_address(destaddr,isp2sh != 0 ? other->chain->pubtype : other->chain->p2shtype,rmd160,20) == destaddr )
        return(destaddr);
    else return(0);
}

STRING_ARG(bitcoinrpc,validateaddress,address)
{
    cJSON *retjson; int32_t i; uint8_t addrtype,rmd160[20],pubkey[65]; struct iguana_info *other; char checkaddr[64],str[256];
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    if ( iguana_addressvalidate(coin,&addrtype,rmd160,address) < 0 )
        return(clonestr("{\"error\":\"invalid coin address\"}"));
    if ( strcmp(address,checkaddr) == 0 )
    {
        retjson = cJSON_CreateObject();
        jaddstr(retjson,"result","success");
        jaddnum(retjson,"addrtype",addrtype);
        init_hexbytes_noT(str,rmd160,sizeof(rmd160));
        jaddstr(retjson,"rmd160",str);
        if ( iguana_ismine(myinfo,pubkey,rmd160) > 0 )
        {
            init_hexbytes_noT(str,pubkey,bitcoin_pubkeylen(pubkey));
            jaddstr(retjson,"pubkey",str);
            cJSON_AddTrueToObject(retjson,"ismine");
        }
        else cJSON_AddFalseToObject(retjson,"ismine");
        for (i=0; i<IGUANA_MAXCOINS; i++)
        {
            if ( (other= Coins[i]) != 0 && strcmp(other->symbol,coin->symbol) != 0 )
            {
                iguana_addressconv(coin,str,other,addrtype == coin->chain->p2shtype,rmd160);
                jaddstr(retjson,other->symbol,str);
            }
        }
        return(jprint(retjson,1));
    } else return(clonestr("{\"error\":\"couldnt regenerate address\"}"));
}

ZERO_ARGS(bitcoinrpc,getinfo)
{
    cJSON *retjson;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    retjson = cJSON_CreateObject();
    jaddstr(retjson,"result",coin->statusstr);
    return(jprint(retjson,1));
}

ZERO_ARGS(bitcoinrpc,walletlock)
{
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    iguana_walletlock(myinfo);
    return(jsuccess());
}

TWOSTRINGS_AND_INT(bitcoinrpc,walletpassphrase,password,permanentfile,timeout)
{
    char *retstr;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    if ( timeout <= 0 )
        return(clonestr("{\"error\":\"timeout must be positive\"}"));
    retstr = SuperNET_login(IGUANA_CALLARGS,myinfo->handle,password,permanentfile,0);
    myinfo->expiration = (uint32_t)time(NULL) + timeout;
    return(retstr);
}

THREE_STRINGS(bitcoinrpc,encryptwallet,passphrase,password,permanentfile)
{
    char *retstr;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    if ( password == 0 || password[0] == 0 )
        password = passphrase;
    retstr = SuperNET_login(IGUANA_CALLARGS,myinfo->handle,password,permanentfile,passphrase);
    iguana_walletlock(myinfo);
    return(retstr);
}

FOUR_STRINGS(bitcoinrpc,walletpassphrasechange,oldpassword,newpassword,oldpermanentfile,newpermanentfile)
{
    char destfname[1024],*tmpstr,*loginstr,*passphrase,*retstr = 0; cJSON *tmpjson,*payload,*loginjson;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    if ( (tmpstr= SuperNET_login(IGUANA_CALLARGS,myinfo->handle,oldpassword,oldpermanentfile,0)) != 0 )
    {
        if ( (tmpjson= cJSON_Parse(retstr)) != 0 )
        {
            if ( (loginstr= SuperNET_login(IGUANA_CALLARGS,myinfo->handle,newpassword,newpermanentfile,0)) != 0 )
            {
                if ( (loginjson= cJSON_Parse(loginstr)) != 0 )
                {
                    if ( (passphrase= jstr(loginjson,"passphrase")) != 0 )
                    {
                        if ( (payload= jobj(loginjson,"payload")) != 0 )
                            jadd(loginjson,"payload",payload);
                        _SuperNET_encryptjson(destfname,passphrase,0,newpermanentfile,0,loginjson);
                        iguana_walletlock(myinfo);
                        retstr = SuperNET_login(IGUANA_CALLARGS,myinfo->handle,newpassword,newpermanentfile,0);
                    }
                    free_json(loginjson);
                }
                free(loginstr);
            }
            free_json(tmpjson);
        }
        free(tmpstr);
    }
    if ( retstr == 0 )
        retstr = clonestr("{\"error\":\"error changing walletpassphrase\"}");
    return(retstr);
}

TWO_STRINGS(bitcoinrpc,setaccount,address,account)
{
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(setaccount(myinfo,coin,account,address));
}

STRING_ARG(bitcoinrpc,getaccount,address)
{
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(getaccount(myinfo,coin,address));
}

STRING_ARG(bitcoinrpc,getnewaddress,account)
{
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(getnewaddress(myinfo,coin,account));
}

STRING_ARG(bitcoinrpc,getaccountaddress,account)
{
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(getaccountaddress(myinfo,coin,account));
}

TWOSTRINGS_AND_INT(bitcoinrpc,importprivkey,wif,account,rescan)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

STRING_ARG(bitcoinrpc,dumpprivkey,address)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

// multiple address
THREE_INTS(bitcoinrpc,getbalance,confirmations,includeempty,watchonly)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

STRING_ARG(bitcoinrpc,getaddressesbyaccount,account)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

STRING_AND_INT(bitcoinrpc,getreceivedbyaccount,account,includeempty)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

THREE_INTS(bitcoinrpc,listreceivedbyaccount,confirmations,includeempty,watchonly)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

THREE_INTS(bitcoinrpc,listreceivedbyaddress,minconf,includeempty,flag)
{
    //cJSON *retjson;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(jsuccess());
}

STRING_AND_THREEINTS(bitcoinrpc,listtransactions,account,count,skip,includewatchonly)
{
    //cJSON *retjson;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(jsuccess());
}

S_D_SS(bitcoinrpc,sendtoaddress,address,amount,comment,comment2)
{
    //cJSON *retjson;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(jsuccess());
}

SS_D_I_SS(bitcoinrpc,sendfrom,fromaccount,toaddress,amount,minconf,comment,comment2)
{
    //cJSON *retjson;
    if ( remoteaddr != 0 )
        return(clonestr("{\"error\":\"no remote\"}"));
    return(jsuccess());
}
DOUBLE_ARG(bitcoinrpc,settxfee,amount)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

SS_D_I_S(bitcoinrpc,move,fromaccount,toaccount,amount,minconf,comment)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

S_A_I_S(bitcoinrpc,sendmany,fromaccount,array,minconf,comment)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}


// entire wallet funcs
TWO_INTS(bitcoinrpc,listaccounts,minconf,includewatchonly)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

ZERO_ARGS(bitcoinrpc,listaddressgroupings)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

ZERO_ARGS(bitcoinrpc,checkwallet)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

ZERO_ARGS(bitcoinrpc,repairwallet)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

STRING_ARG(bitcoinrpc,dumpwallet,filename)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

STRING_ARG(bitcoinrpc,backupwallet,filename)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

STRING_ARG(bitcoinrpc,importwallet,filename)
{
    cJSON *retjson = cJSON_CreateObject();
    return(jprint(retjson,1));
}

#include "../includes/iguana_apiundefs.h"

/*
 
 sendtoaddress
 encryptwallet
 sendfrom
 walletlock
 walletpassphrase
 walletpassphrasechange
 
 validateaddress
 
 listreceivedbyaddress
 listtransactions
 
 not implemented yet but needed by GUI
 
 addmultisigaddress (for generating address)
 setaccount       (to give labels to address)
 */