package p2p



import "github.com/romana/rlog"

import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/crypto"



// when 2 peers communiate either both are in sync or async
// if async, reply to the request below, with your state and other will supply you  list of block ids
// handle BC_NOTIFY_REQUEST_CHAIN
func Handle_BC_Notify_Chain(connection *Connection,
	i_command_header *Levin_Header, buf []byte) {

	// deserialize data header
	var i_data_header Levin_Data_Header // incoming data header

	err := i_data_header.DeSerialize(buf)

	if err != nil {
		connection.logger.Debugf("We should destroy connection here, data header cnot deserialized")
		connection.Exit = true
		return
	}

	buf = i_data_header.Data[11:] // 11 bytes boost header, ignore it

	// decode remain data length ( though we know it from buffer size, but still verify it )

	data_length, done := Decode_Boost_Varint(buf)
	buf = buf[done:]

	if data_length == 0 {
		rlog.Tracef(4, "Peer says it does not have even genesis block, so disconnect")
		connection.Exit = true
		return
	}
	if (data_length % 32) != 0 { // sanity check
		rlog.Tracef(4, "We should destroy connection here, packet mismatch")
		connection.Exit = true
		return
	}

	rlog.Tracef(4, "Number of hashes %d \n", data_length/32)

	var block_list []crypto.Hash

	for i := uint64(0); i < data_length/32; i++ {
		var bhash crypto.Hash
		copy(bhash[:], buf[i*32:(i+1)*32])

		block_list = append(block_list, bhash)

		rlog.Tracef(5,"%2d hash  %x\n", i, bhash[:])

	}

	// the data is like this, first 10 blocks, then block are in 2^n power and the last block is genesis

	// make sure the genesis block is same

	if block_list[len(block_list)-1] != globals.Config.Genesis_Block_Hash {
		connection.logger.Debugf("Peer's genesis block is different from our, so disconnect")
		connection.Exit = true
		return
	}

	// we must give user our version of the chain
	start_height := uint64(0)

	for i := 0; i < len(block_list); i++ { // find the common point in our chain
		if chain.Block_Exists(block_list[i]) {
			start_height = chain.Load_Height_for_BL_ID(block_list[i])
			rlog.Tracef(4,"Found common point in chain at hash %x\n", block_list[i])
			break
		}
	}

	// send atleast 16001 block or till the top
	stop_height := chain.Get_Height()

	if (stop_height - start_height) > 1001 { // send MAX 512 KB block hashes
		stop_height = start_height + 1002
	}

	block_list = block_list[:0]

	for i := start_height; i < stop_height; i++ {
		hash, _ := chain.Load_BL_ID_at_Height(i)
		block_list = append(block_list, hash)
	}

	rlog.Tracef(2,"Prepared list of %d block header to send \n", len(block_list))

	Send_BC_Notify_Response_Chain_Entry(connection, block_list, start_height, chain.Get_Height(), 1)

}

// header from boost packet
//0060   00 00 00 01 11 01 01 01 01 02 01 [] 01 04 09 62 6c  ..............bl
//0070   6f 63 6b 5f 69 64 73 0a [] 81 0a 9b a2 3e fe 50 5f  ock_ids.....>.P_

// send the Peer our chain status, so he can give us the latest chain or updated block_ids
// this is only sent when we are in different from peers
func Send_BC_Notify_Chain_Command(connection *Connection) {

	connection.Lock()

	header_bytes := []byte{ /*0x01, 0x04,*/ 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x0a}

	_ = header_bytes
	// now append boost variant length, and then append all the hashes

	var block_list []crypto.Hash

	// add your blocks here

	our_height := chain.Get_Height()
	if our_height < 20 { // if height is less than 20, fetch from genesis block

	} else { // send blocks in reverse
		for i := uint64(1); our_height < 11; i++ {
			hash, err := chain.Load_BL_ID_at_Height(our_height - i)
			_ = err
			block_list = append(block_list, hash)
		}

		our_height = our_height - 11

		// now seend all our block id in log, 2nd, 4th, 8th,  etc
		for ; our_height > 0; our_height = our_height >> 1 {
			hash, err := chain.Load_BL_ID_at_Height(our_height)
			_ = err
			block_list = append(block_list, hash)

		}
	}

	// final block is always genesis block
	block_list = append(block_list, globals.Config.Genesis_Block_Hash)

	buf := make([]byte, 8, 8)
	done := Encode_Boost_Varint(buf, uint64(len(block_list)*32)) // encode length of buffer
	buf = buf[:done]

	var o_command_header Levin_Header
	var o_data_header Levin_Data_Header

	o_data_header.Data = append(header_bytes, buf...)

	// convert and append all hashes to bytes
	for _, hash := range block_list {
		o_data_header.Data = append(o_data_header.Data, hash[:32]...)
	}

	o_data_bytes, _ := o_data_header.Serialize()

	o_data_bytes[9] = 0x4

	o_command_header.CB = uint64(len(o_data_bytes))

	o_command_header.Command = BC_NOTIFY_REQUEST_CHAIN
	o_command_header.ReturnData = false
	o_command_header.Flags = LEVIN_PACKET_REQUEST

	o_command_header_bytes, _ := o_command_header.Serialize()

	connection.Conn.Write(o_command_header_bytes)
	connection.Conn.Write(o_data_bytes)

	//fmt.Printf("len of command header %d\n", len(o_command_header_bytes))
	//fmt.Printf("len of data header %d\n", len(o_data_bytes))

	connection.Unlock()
}
