/******************************************************************************
 * Copyright © 2014-2015 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"

int64_t iguana_packetsallocated(struct iguana_info *coin) { return(coin->R.packetsallocated - coin->R.packetsfreed); };

uint8_t *iguana_decompress(struct iguana_info *coin,int32_t height,int32_t *datalenp,uint8_t *bits,int32_t numbits,int32_t origdatalen)
{
    uint32_t hdrlen,checklen;
    memcpy(&hdrlen,bits,sizeof(hdrlen));
    bits = &bits[sizeof(hdrlen)];
    *datalenp = 0;
    if ( (hdrlen & (1 << 31)) != 0 )
    {
        hdrlen ^= (1 << 31);
        if ( (hdrlen >> 3) == origdatalen )
        {
            *datalenp = origdatalen;
            return(bits);
        } else printf("\n>>>>>>>>> iguana_decompress.%d numbits.%d %d != origlen.%d\n",height,hdrlen,hdrlen>>3,origdatalen), getchar();
    }
    else if ( hconv_bitlen(hdrlen) == hconv_bitlen(numbits) )
    {
        if ( (checklen= ramcoder_decompress(coin->R.decompressed,sizeof(coin->R.decompressed),bits,hdrlen,bits256_zero)) == origdatalen )
        {
            //printf("DECOMPRESSED %d to %d\n",hconv_bitlen(hdrlen),checklen);
            *datalenp = origdatalen;
            return(coin->R.decompressed);
        }
        else
        {
            printf("\n>>>>>>>>> iguana_decompress.%d hdrlen.%d checklen.%d != origdatalen.%d\n",height,hdrlen,checklen,origdatalen);
            int32_t j;
            for (j=0; j<hconv_bitlen(numbits); j++)
                printf("%02x ",bits[j]);
            printf("compressed.%d\n",numbits/8);
            getchar();
        }
    }
    else
    {
        printf("\n>>>>>>>>>> iguana_decompress.%d hdrlen.%d != numbits.%d\n",height,hdrlen,numbits);
        int32_t j;
        for (j=0; j<=numbits/8; j++)
            printf("%02x ",bits[j]);
        printf("compressed.%d\n",numbits/8);
        getchar();
    }
    return(0);
}

/*struct iguana_msgtx *iguana_validpending(struct iguana_info *coin,struct iguana_pending *ptr,struct iguana_block *space)
{
    struct iguana_block *checkblock; uint8_t *data; int32_t datalen,len; struct iguana_msgtx *tx = 0;
    *space = ptr->block;
    if ( coin->R.recvblocks == 0 || ptr->block.height >= coin->R.numwaitingbits )
    {
        printf("illegal pending height.%d vs %d\n",ptr->block.height,coin->R.numwaitingbits);
        return(0);
    }
    if ( ptr->origdatalen > 0 && ptr->block.height < coin->longestchain && ptr->block.height < coin->blocks.hwmheight )
    {
        if ( (checkblock= iguana_block(coin,space,ptr->block.height)) != 0 )
        {
            if ( iguana_blockcmp(coin,checkblock,space,1) == 0 )
            {
                data = iguana_decompress(coin,ptr->block.height,&datalen,ptr->data,ptr->datalen << 3,ptr->origdatalen);
                //printf("parsed.%d vs max.%d height.%d data.%p\n",coin->blocks.parsedblocks,coin->R.numwaitingbits,ptr->block.height,data);
                if ( data != 0 && iguana_setdependencies(coin,space) == ptr->block.height )
                {
                    if ( (tx= iguana_gentxarray(coin,&len,space,data,datalen)) != 0 && len == datalen )
                        return(tx);
                } else printf("iguana_validpending: error gentx block.%d\n",coin->blocks.parsedblocks);
            } else printf("iguana_validpending: error setting vars block.%d\n",ptr->block.height);
            if ( tx != 0 )
                iguana_freetx(tx,ptr->numtx);
        } else printf("iguana_validpending cant get checkblock %d vs hwmheight.%d\n",ptr->block.height,coin->blocks.hwmheight);
    }
    return(0);
}*/

/*int32_t iguana_processrecv(struct iguana_info *coin)
{
    int32_t height; struct iguana_block space; struct iguana_msgtx *tx = 0;
    struct iguana_pending *ptr = 0; int32_t retval = -1;
    height = coin->blocks.parsedblocks;
    if ( coin->R.recvblocks != 0 && height < coin->R.numwaitingbits )
    {
        if ( (ptr= coin->R.recvblocks[height]) != 0 )
        {
            //printf("iguana_processrecv height.%d %p\n",height,ptr);
            coin->R.recvblocks[height] = 0;
            if ( (tx= iguana_validpending(coin,ptr,&space)) != 0 )
            {
                retval = iguana_parseblock(coin,&space,tx,ptr->numtx);
                if ( space.L.numunspents+space.numvouts != coin->latest.dep.numunspents )
                    printf("block->firstvout+block->numvouts (%d+%d) != %d coin->latest.deps.numunspentinds\n",space.L.numunspents,space.numvouts,coin->latest.dep.numunspents), getchar();
                if ( retval < 0 )
                    printf("iguana_processrecv: error parsing block.%d tx.%p\n",ptr->block.height,tx);
                if ( tx != 0 )
                    iguana_freetx(tx,ptr->numtx);
            } else printf("error getting pending %d %p\n",height,ptr);
            if ( coin->R.maprecvdata == 0 )
            {
                coin->R.packetsfreed += ptr->allocsize;
                myfree(ptr,ptr->allocsize);
            }
        }
        else if ( time(NULL) > coin->parsetime+1 )
        {
            coin->parsetime = (uint32_t)time(NULL);
            printf("backstop.%d %s\n",height,bits256_str(iguana_blockhash(coin,height)));
            bits256 hash2 = iguana_blockhash(coin,height);
            iguana_request_data(coin,coin->peers.ranked[0],&hash2,1,MSG_BLOCK,1);
            iguana_waitclear(coin,height);
            iguana_waitstart(coin,height);
            iguana_updatewaiting(coin,height+1,100);
        }
    } else printf("processrecv: no recvbits!\n");
    return(retval);
}

int32_t iguana_recvblock(struct iguana_info *coin,struct iguana_peer *addr,struct iguana_block *block,struct iguana_msgtx *tx,int32_t numtx,uint8_t *data,int32_t origdatalen)
{
    struct iguana_pending *ptr; int32_t allocsize,checklen,numbits; uint32_t datalen,hdrlen;
    if ( coin->R.recvblocks == 0 || coin->R.recvblocks[block->height] != 0 )
    {
        coin->sleeptime++;
        if ( coin->sleeptime > 10000 )
            coin->sleeptime = 10000;
        if ( 0 && addr != coin->peers.localaddr )
            printf("%s recv duplicate at height.%d sleepmillis %.3f\n",addr->ipaddr,block->height,(double)coin->sleeptime/1000.); // add validation/merging
    }
    else
    {
        coin->sleeptime *= .995;
        if ( coin->sleeptime < 1000 )
            coin->sleeptime = 1000;
        // validate block here
        datalen = origdatalen;
        hdrlen = (1 << 31) | (datalen << 3);
        coin->R.srcdatalen += datalen;
        if ( 0 && (numbits= ramcoder_compress(coin->R.compressed,sizeof(coin->R.compressed),data,datalen,coin->R.histo,bits256_zero)) > 0 )
        {
            memset(coin->R.checkbuf,0,datalen);
            if ( (checklen= ramcoder_decompress(coin->R.checkbuf,sizeof(coin->R.checkbuf),coin->R.compressed,numbits,bits256_zero)) == datalen )
            {
                if ( memcmp(coin->R.checkbuf,data,datalen) == 0 )
                {
                    hdrlen = numbits;
                    data = coin->R.compressed;
                    printf("height.%d datalen.%d -> numbits.%d %d compression ratio %.3f [%.4f]\n",block->height,datalen,numbits,hconv_bitlen(numbits),(double)datalen/hconv_bitlen(numbits),(double)coin->R.srcdatalen/(coin->R.compressedtotal+hconv_bitlen(numbits)+sizeof(hdrlen)));
                    datalen = hconv_bitlen(numbits);
                } else printf("ramcoder data datalen.%d compare error\n",datalen), getchar();
            }
            else printf("ramcoder codec error origdatalen.%d numbits.%d datalen. %d -> %d\n",origdatalen,numbits,datalen,checklen), getchar();
        } //else printf("ramcoder compress error %d -> numbits.%d\n",datalen,numbits), getchar();
        coin->R.compressedtotal += (datalen + sizeof(hdrlen));
        allocsize = (int32_t)(sizeof(*ptr) + datalen + sizeof(hdrlen));
        if ( coin->R.maprecvdata != 0 )
        {
            ptr = iguana_tmpalloc(coin,"recv",&coin->R.RSPACE,allocsize);
            if ( block->height > coin->R.RSPACE.maxheight )
                coin->R.RSPACE.maxheight = block->height;
            ptr->next = (int32_t)((long)iguana_tmpalloc(coin,"recv",&coin->R.RSPACE,0) - (long)ptr);
        }
        else
        {
            ptr = mycalloc('P',1,allocsize);
            coin->R.packetsallocated += allocsize;
        }
        ptr->allocsize = allocsize;
        ptr->datalen = datalen;
        memcpy(ptr->data,&hdrlen,sizeof(hdrlen));
        memcpy(&ptr->data[sizeof(hdrlen)],data,datalen);
        ptr->ipbits = addr != 0 ? addr->ipbits : 0;
        ptr->block = *block;
        ptr->numtx = numtx;
        ptr->origdatalen = origdatalen;
        if ( (rand() % 1000) == 0 )
            printf("%s recv.%d ptr.%p datalen.%d orig.%d %.3f | parsed.%d hwm.%d longest.%d | %d/%d elapsed %.2f\n",addr != 0 ? addr->ipaddr : "local",block->height,ptr,datalen,origdatalen,(double)origdatalen/datalen,coin->blocks.parsedblocks,coin->blocks.hwmheight,coin->longestchain,iguana_updatewaiting(coin,coin->blocks.parsedblocks,coin->width*10),coin->width*10,(double)(time(NULL)-coin->starttime)/60.);
        coin->R.recvblocks[block->height] = ptr;
    }
    return(0);
}*/