package p2plib

import (
	"fmt"
	"net"
	"time"

	"github.com/fatih/color"
)

type Peer struct {
	ID       string   `json:"id"` //in the future, this will be the peer hash
	IP       string   `json:"ip"`
	Port     string   `json:"port"`
	RESTPort string   `json:"restport"`
	Role     string   `json:"role"` //client or server
	Conn     net.Conn `json:"conn"`
}

type PeersList struct {
	PeerID string
	Peers  []Peer    `json:"peerslist"`
	Date   time.Time `json:"date"`
}

type PeersConnections struct {
	Incoming  PeersList
	Outcoming PeersList
	Network   PeersList //the peers that have been received in the lists from other peers
}

type ThisPeer struct {
	Running          bool
	ID               string
	RunningPeer      Peer
	PeersConnections PeersConnections
}

var globalTP ThisPeer

func PeerIsInPeersList(p Peer, pl []Peer) int {
	r := -1
	for i, peer := range pl {
		if peer.IP+":"+peer.Port == p.IP+":"+p.Port {
			r = i
		}
	}
	return r
}

func DeletePeerFromPeersList(p Peer, pl *PeersList) {
	i := PeerIsInPeersList(p, pl.Peers)
	if i != -1 {
		//delete peer from pl.Peers
		pl.Peers = append(pl.Peers[:i], pl.Peers[i+1:]...)
	}
}
func AppendPeerIfNoExist(pl PeersList, p Peer) PeersList {
	i := PeerIsInPeersList(p, pl.Peers)
	if i == -1 {
		pl.Peers = append(pl.Peers, p)
	}
	return pl
}
func UpdateNetworkPeersList(conn net.Conn, newPeersList PeersList) {
	for _, peer := range newPeersList.Peers {
		if GetIPPortFromConn(conn) == peer.IP+":"+peer.Port {
			peer.ID = newPeersList.PeerID
			color.Yellow(peer.ID)
		}
		i := PeerIsInPeersList(peer, globalTP.PeersConnections.Network.Peers)
		if i == -1 {
			globalTP.PeersConnections.Network.Peers = append(globalTP.PeersConnections.Network.Peers, peer)
		} else {
			fmt.Println(globalTP.PeersConnections.Network.Peers[i])
			globalTP.PeersConnections.Network.Peers[i].ID = peer.ID
		}
	}
}
func SearchPeerAndUpdate(p Peer) {
	for _, peer := range globalTP.PeersConnections.Outcoming.Peers {
		color.Red(p.IP + ":" + p.Port)
		color.Yellow(peer.IP + ":" + peer.Port)
		if p.IP+":"+p.Port == peer.IP+":"+peer.Port {
			peer.ID = p.ID
		}
	}
}

//send the outcomingPeersList to all the peers except the peer p that has send the outcomingPeersList
func PropagatePeersList(p Peer) {
	for _, peer := range globalTP.PeersConnections.Network.Peers {
		if peer.Conn != nil {
			if peer.ID != p.ID && p.ID != "" {
				color.Yellow(peer.ID + " - " + p.ID)
				var msg Msg
				msg.Construct("PeersList", "here my outcomingPeersList")
				msg.PeersList = globalTP.PeersConnections.Outcoming
				msgB := msg.ToBytes()
				_, err := peer.Conn.Write(msgB)
				check(err)
			} else {
				/*
					for the moment, this is not being called, due that in the IncomingPeersList,
					there is no peer.ID, so in the comparation wih the peer that has send the
					peersList, is comparing ID with "", so nevere enters this 'else' section

					maybe it's not needed. TODO check if it's needed the PeerList_Response
					For the moment is working without it
				*/
				//to the peer that has sent the peerList, we send our PeersList

				var msg Msg
				msg.Construct("PeersList_Response", "here my outcomingPeersList")
				msg.PeersList = globalTP.PeersConnections.Outcoming
				msgB := msg.ToBytes()
				_, err := peer.Conn.Write(msgB)
				check(err)
			}
		} else {
			//connect to peer
			if peer.ID != p.ID && peer.ID != globalTP.RunningPeer.ID {
				if PeerIsInPeersList(peer, globalTP.PeersConnections.Outcoming.Peers) == -1 {
					color.Red("no connection, connecting to peer: " + peer.Port)
					ConnectToPeer(peer)
				}
			}

		}
	}
}
func PrintPeersList() {
	fmt.Println("")
	color.Blue("runningPeer.ID: " + globalTP.RunningPeer.ID)
	color.Green("OUTCOMING PEERSLIST:")
	for _, peer := range globalTP.PeersConnections.Outcoming.Peers {
		fmt.Println(peer)
	}
	color.Green("INCOMING PEERSLIST:")
	for _, peer := range globalTP.PeersConnections.Incoming.Peers {
		fmt.Println(peer)
	}

	color.Green("NETWORK PEERSLIST:")
	for _, peer := range globalTP.PeersConnections.Network.Peers {
		fmt.Println(peer)
	}
	fmt.Println("")
}

//send the block to all the peers of the outcomingPeersList
/*func PropagateBlock(b Block) {
	//prepare the msg to send to all connected peers
	var msg Msg
	msg.construct("Block", "new block")
	msg.Block = b
	msgB := msg.toBytes()
	for _, peer := range outcomingPeersList.Peers {
		if peer.Conn != nil {
			_, err := peer.Conn.Write(msgB)
			check(err)
		}
	}
}*/
