U0 TCPGlobalsInit()
{
    tcp_globals.bound_socket_tree = NULL;
    tcp_globals.next_source_port  = 0;
}

Bool IsTCPStateSync(CTCPSocket *tcp_socket)
{ 
    switch (tcp_socket->state)
    {
        case TCP_STATE_ESTABLISHED:
        case TCP_STATE_FIN_WAIT1:
        case TCP_STATE_FIN_WAIT2:
        case TCP_STATE_CLOSE_WAIT:
        case TCP_STATE_CLOSING:
        case TCP_STATE_LAST_ACK:
        case TCP_STATE_TIME_WAIT:
            return TRUE;
        default:
            return FALSE;
    }
}

// TODO: Merge IPV4Checksum, TCPChecksumPartial, and TCPChecksumFinal into 1.
//       All use same alg.
U16 TCPChecksumPartial(U32 sum, U8 *header, I64 length)
{ // same algorithm as IPV4Checksum, except sum is arg. First half.
  //todo. make names clearer, and better comments.
    I64  nleft = length;
    U16 *w = header;

    while (nleft > 1)
    {
        sum += *w++;
        nleft -= 2;
    }

    return sum;
}

U16 TCPChecksumFinal(U32 sum, U8 *header, I64 length)
{ // same algorithm as IPV4Checksum, except sum is arg. Both halves.
  //todo. make names clearer, and better comments.
    I64  nleft = length;
    U16 *w = header;

    while (nleft > 1)
    {
        sum += *w++;
        nleft -= 2;
    }

    // "mop up an odd byte, if necessary"
    if (nleft == 1)
    {
        sum += *w & 0x00FF;
    }

    // "add back carry outs from top 16 bits to low 16 bits"
    sum = sum >> 16 + sum & 0xFFFF; // "add hi 16 to low 16"
    sum += sum >> 16; // add carry
    return ~sum & 0xFFFF;

}

I64 TCPPacketAllocate(U8 **frame_out,
                      U32  source_ip,
                      U16  source_port,
                      U32  destination_ip,
                      U16  destination_port,
                      U32  seq_num,
                      U32  ack_num,
                      U8   flags,
                      I64  length)
{
    U8          *tcp_frame;
    I64          de_index;
    CTCPHeader  *header;

    de_index = IPV4PacketAllocate(&tcp_frame,
                                  IP_PROTOCOL_TCP,
                                  source_ip,
                                  destination_ip,
                                  sizeof(CTCPHeader) + length);
    if (de_index < 0)
    {
        NetLog("TCP PACKET ALLOCATE: Ethernet Frame Allocate failed.");
        return de_index;
    }

    header = tcp_frame;

    header->source_port         = EndianU16(source_port);
    header->destination_port    = EndianU16(destination_port);

    NetDebug("TCP PACKET ALLOCATE: Outputting header port src/dest 0x%0X/0x%0X:",
                header->source_port, header->destination_port);

    header->seq_num             = EndianU32(seq_num);
    header->ack_num             = EndianU32(ack_num);
    header->data_offset         = (sizeof(CTCPHeader) / 4) << 4; // ???
    header->flags               = flags;
    header->window_size         = EndianU16(TCP_WINDOW_SIZE / 2);// TODO: What is window size supposed to be ?
    header->checksum            = 0;
    header->urgent_pointer      = 0;

    *frame_out = tcp_frame + sizeof(CTCPHeader);

    return de_index;
}

U0 TCPPacketFinish(I64            de_index,
                   U32            source_ip,
                   U32            destination_ip,
                   U8            *frame,
                   I64            length,
                   CTCPAckQueue **send_buffer_out=NULL)
{
    CTCPHeader       *header        = frame - sizeof(CTCPHeader);
    CTCPPseudoHeader *pseudoheader  = CAlloc(sizeof(CTCPPseudoHeader));
    U32               checksum;
    CTCPAckQueue     *send_buffer;

    pseudoheader->source_address        = EndianU32(source_ip);
    pseudoheader->destination_address   = EndianU32(destination_ip);
    pseudoheader->zeros                 = 0;
    pseudoheader->protocol              = IP_PROTOCOL_TCP;
    pseudoheader->tcp_length            = EndianU16(sizeof(CTCPHeader) + length);

    checksum = TCPChecksumPartial(0, pseudoheader, sizeof(CTCPPseudoHeader));
    header->checksum = TCPChecksumFinal(checksum, header, sizeof(CTCPHeader) + length);

    if (send_buffer_out)
    {
        send_buffer = CAlloc(sizeof(CTCPAckQueue));

        send_buffer->time_sent          = tS;
        send_buffer->retries            = 0;
        send_buffer->start_seq_num      = EndianU32(header->seq_num);
        send_buffer->end_seq_num        = 0; // must set this upstream
        send_buffer->tcp_packet_size    = length;
        send_buffer->tcp_packet         = CAlloc(sizeof(CTCPHeader) + length);

        MemCopy(send_buffer->tcp_packet, frame, sizeof(CTCPHeader) + length);

        *send_buffer_out = send_buffer; // set output param as new send buffer
    }

    IPV4PacketFinish(de_index);
}

I64 TCPSend(U32 source_ip,
            U16 source_port,
            U32 destination_ip,
            U16 destination_port,
            U32 seq_num,
            U32 ack_num,
            U8  flags)
{ // send raw unsocketed TCP packet, flags and/or data. Does not store into ACK Queue (Send Buffer).

    U8 *payload_frame;
    I64 de_index = TCPPacketAllocate(&payload_frame,
                                     source_ip,
                                     source_port,
                                     destination_ip,
                                     destination_port,
                                     seq_num,
                                     ack_num,
                                     flags,
                                     0);
    if (de_index < 0)
    {
        NetErr("TCP SEND: TCP Packet Allocate failed.");
        return de_index;
    }

    TCPPacketFinish(de_index, source_ip, destination_ip, payload_frame, 0, NULL);

    return 0;
}

I64 TCPSendFlags(CTCPSocket *tcp_socket, U8 flags)
{ // send TCP packet, flags only. Stores into ACK Queue (Send Buffer) if flags SYN or FIN.

    U8                      *payload_frame;
    CSocketAddressStorage   *dest   = &tcp_socket->destination_address;
    CSocketAddressStorage   *source = &tcp_socket->source_address;
    CSocketAddressIPV4      *dest_ipv4;
    CSocketAddressIPV6      *dest_ipv6;
    CSocketAddressIPV4      *source_ipv4;
    CSocketAddressIPV6      *source_ipv6;
    I64                      de_index;
    CTCPAckQueue            *send_buffer     = NULL;
    CTCPAckQueue           **send_buffer_ptr = NULL;

    NetDebug("TCP SEND FLAGS: Attempting Send using param TCP socket");

    switch (dest->family)
    {
        case AF_INET:
            dest_ipv4 = dest;

            switch (source->family)
            {
                case AF_INET:
                    source_ipv4 = source;

                    NetDebug("TCP SEND FLAGS: Trying TCP Packet Allocate... port src/dest 0x%0X/0x%0X",
                                source_ipv4->port, dest_ipv4->port);

                    de_index = TCPPacketAllocate(&payload_frame,
                                                 source_ipv4->address.address,
                                                 EndianU16(source_ipv4->port),
                                                 dest_ipv4->address.address,
                                                 EndianU16(dest_ipv4->port),
                                                 tcp_socket->next_send_seq_num,
                                                 tcp_socket->next_recv_seq_num,
                                                 flags,
                                                 0);
                    if (de_index < 0)
                    {
                        NetErr("TCP SEND FLAGS: TCP Packet Allocate failed.");
                        return de_index;
                    }

                    break;
                case AF_INET6:
                    source_ipv6 = source;
                    NetErr("TCP SEND FLAGS: FIXME, IPV6 not implemented yet");
                    return -1;
                case AF_UNSPEC:
                    NetErr("TCP SEND FLAGS: Error, TCP Send from AF_UNSPEC\n");
                    return -1;
            }

            break;
        case AF_INET6:
            dest_ipv6 = dest;
            NetErr("TCP SEND FLAGS: FIXME, IPV6 not implemented yet");
            return -1;
        case AF_UNSPEC:
            NetErr("TCP SEND FLAGS: Error, TCP Send to AF_UNSPEC\n");
            return -1;
    }

    // at this point, a TCP packet has been allocated.
    // TODO: Is there a better way to manage links across different address types?

    if (Bt(&flags, TCPf_SYN))
        tcp_socket->next_send_seq_num++;

    if (Bt(&flags, TCPf_FIN))
        tcp_socket->next_send_seq_num++;

    if (Bt(&flags, TCPf_FIN) || Bt(&flags, TCPf_SYN))
    {
        send_buffer_ptr = &send_buffer;
    }


    switch (dest->family)
    {
        case AF_INET:
            dest_ipv4 = dest;

            switch (source->family)
            {
                case AF_INET:
                    source_ipv4 = source;

                    TCPPacketFinish(de_index,
                                    source_ipv4->address.address,
                                    dest_ipv4->address.address,
                                    payload_frame,
                                    0,
                                    send_buffer_ptr);

                    if (send_buffer)
                    { // modifying ptr should affect our send_buffer variable
                        send_buffer->end_seq_num = tcp_socket->next_send_seq_num;

                        // append it to the socket, ACK Queue needs to be ready (head)...
                        QueueInsertRev(send_buffer, tcp_socket->ack_queue);

                    }

                    break;
                case AF_INET6:
                    source_ipv6 = source;
                    NetErr("TCP SEND FLAGS: FIXME, IPV6 not implemented yet");
                    return -1;
                case AF_UNSPEC:
                    NetErr("TCP SEND FLAGS: Error, TCP Send from AF_UNSPEC\n");
                    return -1;
            }

            break;
        case AF_INET6:
            dest_ipv6 = dest;
            NetErr("TCP SEND FLAGS: FIXME, IPV6 not implemented yet");
            return -1;
        case AF_UNSPEC:
            NetErr("TCP SEND FLAGS: Error, TCP Send to AF_UNSPEC\n");
            return -1;
    }
}

I64 TCPSendData(CTCPSocket *tcp_socket, U8 flags, U8 *data, I64 length)
{ // send TCP packet, flags and data. Stores into ACK Queue (Send Buffer).

    U8                      *payload_frame;
    CSocketAddressStorage   *dest   = &tcp_socket->destination_address;
    CSocketAddressStorage   *source = &tcp_socket->source_address;
    CSocketAddressIPV4      *dest_ipv4;
    CSocketAddressIPV6      *dest_ipv6;
    CSocketAddressIPV4      *source_ipv4;
    CSocketAddressIPV6      *source_ipv6;
    I64                      de_index;
    CTCPAckQueue            *send_buffer;

    NetDebug("TCP SEND DATA: Attempting Send using param TCP socket");

    switch (dest->family)
    {
        case AF_INET:
            dest_ipv4 = dest;

            switch (source->family)
            {
                case AF_INET:
                    source_ipv4 = source;

                    NetDebug("TCP SEND DATA: Trying TCP Packet Allocate... port src/dest 0x%0X/0x%0X",
                                source_ipv4->port, dest_ipv4->port);

                    de_index = TCPPacketAllocate(&payload_frame,
                                                 source_ipv4->address.address,
                                                 EndianU16(source_ipv4->port),
                                                 dest_ipv4->address.address,
                                                 EndianU16(dest_ipv4->port),
                                                 tcp_socket->next_send_seq_num,
                                                 tcp_socket->next_recv_seq_num,
                                                 flags,
                                                 length);
                    if (de_index < 0)
                    {
                        NetLog("TCP SEND DATA: TCP Packet Allocate failed.");
                        return de_index;
                    }

                    break;
                case AF_INET6:
                    source_ipv6 = source;
                    NetErr("TCP SEND DATA: FIXME, IPV6 not implemented yet");
                    return -1;
                case AF_UNSPEC:
                    NetErr("TCP SEND DATA: Error, TCP Send from AF_UNSPEC\n");
                    return -1;
            }

            break;
        case AF_INET6:
            dest_ipv6 = dest;
            NetErr("TCP SEND DATA: FIXME, IPV6 not implemented yet");
            return -1;
        case AF_UNSPEC:
            NetErr("TCP SEND DATA: Error, TCP Send to AF_UNSPEC\n");
            return -1;
    }

    // at this point, a TCP packet has been allocated.
    // TODO: Is there a better way to manage links across different address types?

    if (length)
        MemCopy(payload_frame, data, length);

    if (Bt(&flags, TCPf_SYN))
        tcp_socket->next_send_seq_num++;

    tcp_socket->next_send_seq_num += length;

    if (Bt(&flags, TCPf_FIN))
        tcp_socket->next_send_seq_num++;

    switch (dest->family)
    {
        case AF_INET:
            dest_ipv4 = dest;

            switch (source->family)
            {
                case AF_INET:
                    source_ipv4 = source;

                    TCPPacketFinish(de_index,
                                    source_ipv4->address.address,
                                    dest_ipv4->address.address,
                                    payload_frame,
                                    length,
                                    &send_buffer);

                    send_buffer->end_seq_num = tcp_socket->next_send_seq_num;

                    // append it to the socket, ACK Queue needs to be ready (head)...
                    QueueInsertRev(send_buffer, tcp_socket->ack_queue);

                    break;
                case AF_INET6:
                    source_ipv6 = source;
                    NetErr("TCP SEND DATA: FIXME, IPV6 not implemented yet");
                    return -1;
                case AF_UNSPEC:
                    NetErr("TCP SEND DATA: Error, TCP Send from AF_UNSPEC\n");
                    return -1;
            }

            break;
        case AF_INET6:
            dest_ipv6 = dest;
            NetErr("TCP SEND DATA: FIXME, IPV6 not implemented yet");
            return -1;
        case AF_UNSPEC:
            NetErr("TCP SEND DATA: Error, TCP Send to AF_UNSPEC\n");
            return -1;
    }
}

I64 TCPPacketParse(CTCPHeader **header_out,
                   U8         **data_out,
                   I64         *length_out,
                   CIPV4Packet *packet)
{
    CTCPHeader       *header        = packet->data;
    I64               header_length = (header->data_offset >> 4) * 4; // ?? why
//  U32               checksum, sum;
//  CTCPPseudoHeader *pseudoheader  = CAlloc(sizeof(CTCPPseudoHeader));

    // TODO: Validate packet->length !

    // Verify Checksum:
/*  NetWarn("DEBUG: TCPPACKETPARSE:     0x%X = checksum from packet, verifying...", EndianU16(header->checksum));
    pseudoheader->source_address        = EndianU32(packet->source_ip_address);
    pseudoheader->destination_address   = EndianU32(packet->destination_ip_address);
    pseudoheader->zeros                 = 0;
    pseudoheader->protocol              = IP_PROTOCOL_TCP;
    pseudoheader->tcp_length            = EndianU16(packet->length);

    checksum = TCPChecksumPartial(0, pseudoheader, sizeof(CTCPPseudoHeader));
    NetWarn("DEBUG: TCPPACKETPARSE:     0x%X = partial checksum", checksum);

    Free(pseudoheader); // No longer needed, discard

    sum = TCPChecksumFinal(checksum, header, packet->length);

    if (sum != EndianU16(header->checksum))
    {
        NetErr("DEBUG: TCPPACKETPARSE:      0x%X = NOT MATCH", sum);
        //return -1; // is checksum not being 0 in received header going to mess this up?..
    }
    // Checksum verified.
*/

    *header_out = header;
    *data_out   = packet->data   + header_length;
    *length_out = packet->length - header_length;

    return 0;
}

U0 TCPAcknowledgePacket(CTCPSocket *tcp_socket, U32 segment_ack)
{
    F64              time = tS;
    F64              rtt;
    CTCPAckQueue    *send_buffer = tcp_socket->ack_queue->next;
    CTCPAckQueue    *send_buffer_temp;
    I64              segment_ack_relative;
    I64              send_next_relative;

    NetDebug("TCP ACKNOWLEDGE PACKET: Looking to see if there are any send buffers");

    while (send_buffer != tcp_socket->ack_queue)
    {
        segment_ack_relative    = (segment_ack - send_buffer->end_seq_num) & 0xFFFFFFFF;
        send_next_relative      = (tcp_socket->next_send_seq_num - send_buffer->end_seq_num) & 0xFFFFFFFF;
        // ???

        if (segment_ack_relative <= send_next_relative)
        {
            NetDebug("TCP ACKNOWLEDGE PACKET: Saw send buffer entry with ACK <= next relative send ACK, removing.");
            rtt = time - send_buffer->time_sent; // Round-Trip Time

            tcp_socket->srtt = (tcp_socket->srtt * TCP_SRTT_ALPHA) + ((1.0 - TCP_SRTT_ALPHA) * rtt);
            // Smoothed Round-Trip Time

            QueueRemove(send_buffer);
            Free(send_buffer->tcp_packet);

            send_buffer_temp = send_buffer->next; // QueueRemove doesn't change removed queue's links
            Free(send_buffer);

            send_buffer = send_buffer_temp;
            
        }
        else
            break;
    }
}

U0 TCPCheckACKQueue(CTCPSocket *tcp_socket)
{
    F64                      time           = tS;
    F64                      rto            = TCP_RTO_BETA * tcp_socket->srtt; // Retransmission Timeout, Smoothed Round-Trip Time
    CTCPAckQueue            *send_buffer    = tcp_socket->ack_queue->next;
    CTCPAckQueue            *first_buffer   = send_buffer;
    U8                      *tcp_frame;
    I64                      de_index;
    CSocketAddressStorage   *dest           = &tcp_socket->destination_address;
    CSocketAddressStorage   *source         = &tcp_socket->source_address;
    CSocketAddressIPV4      *dest_ipv4;
    CSocketAddressIPV6      *dest_ipv6;
    CSocketAddressIPV4      *source_ipv4;
    CSocketAddressIPV6      *source_ipv6;


    if (send_buffer == tcp_socket->ack_queue)
    {
//      NetWarn("TCP CHECK ACK QUEUE: ACK Queue empty, bailing early.");
        return;
    }

    if (rto < TCP_RTO_MIN)
        rto = TCP_RTO_MIN;
    if (rto < TCP_RTO_MAX)
        rto = TCP_RTO_MAX;

    do
    {
        if (time > send_buffer->time_sent + rto)
        {
            NetWarn("TCP CHECK ACK QUEUE: Retransmitting TCP packet in ACK Queue.");

            switch (dest->family)
            {
                case AF_INET:
                    dest_ipv4 = dest;

                    switch (source->family)
                    {
                        case AF_INET:
                            source_ipv4 = source;

                            de_index = IPV4PacketAllocate(&tcp_frame,
                                                          IP_PROTOCOL_TCP,
                                                          source_ipv4->address.address,
                                                          dest_ipv4->address.address,
                                                          send_buffer->tcp_packet_size);

                            if (de_index < 0)
                            {
                                NetErr("TCP CHECK ACK QUEUE: IPV4 Packet Allocate failed.");
                                return;// de_index;
                            }

                            MemCopy(&tcp_frame, send_buffer->tcp_packet, send_buffer->tcp_packet_size);

                            IPV4PacketFinish(de_index);

                            break;

                        case AF_INET6:
                            source_ipv6 = source;
                            NetErr("TCP CHECK ACK QUEUE: FIXME, IPV6 not implemented yet");
                            return;// -1;
                        case AF_UNSPEC:
                            NetErr("TCP CHECK ACK QUEUE: Error, TCP Send from AF_UNSPEC\n");
                            return;// -1;
                    }

                    break;
                case AF_INET6:
                    dest_ipv6 = dest;
                    NetErr("TCP CHECK ACK QUEUE: FIXME, IPV6 not implemented yet");
                    return;// -1;
                case AF_UNSPEC:
                    NetErr("TCP CHECK ACK QUEUE: Error, TCP Send to AF_UNSPEC\n");
                    return;// -1;
            }

            send_buffer->time_sent = tS;

            // If it gets here, retransmit was successful.

            // Move to the END OF THE CHAIN (BACK OF THE QUEUE. SO, REQUEUE.)
            QueueRemove(send_buffer);
            QueueInsertRev(send_buffer, tcp_socket->ack_queue);

            send_buffer = tcp_socket->ack_queue->next;

        }
        else
            break;
    }
    while (send_buffer != first_buffer);
}

CTCPSocket TCPSocket(U16 domain=AF_UNSPEC)
{
    U16                  type           = SOCKET_STREAM;
    CTCPSocket          *tcp_socket     = CAlloc(sizeof(CTCPSocket));
    CTCPAckQueue        *ack_queue      = CAlloc(sizeof(CTCPAckQueue));
    CTCPAcceptQueue     *accept_queue   = CAlloc(sizeof(CTCPAcceptQueue));
    CSocketAddressIPV4  *ipv4_address;
    CSocketAddressIPV6  *ipv6_address;

    tcp_socket->socket  = Socket(domain, type);
    tcp_socket->state   = TCP_STATE_CLOSED;

    tcp_socket->source_address.family = domain; // INET, INET6, or unspecified

    switch (domain)
    {
        case AF_INET:
            ipv4_address = &tcp_socket->source_address;

            ipv4_address->address.address = IPV4AddressGet;
            // do we need to set local port? does that only make sense to do later?
            // CAlloc will leave it at 0

            break;

        case AF_INET6:
            ipv6_address = &tcp_socket->source_address;
            NetErr("TCP SOCKET: FIXME, IPV6 not implemented yet");
            return NULL;

        case AF_UNSPEC:
            NetErr("TCP SOCKET: Error TCP Socket to AF_UNSPEC");
            return NULL;
    }

    QueueInit(ack_queue);  // init send buffer queue head
    tcp_socket->ack_queue = ack_queue;

    QueueInit(accept_queue); // init pending connection queue
    tcp_socket->accept_queue = accept_queue;

    tcp_socket->receive_buffer_size = TCP_WINDOW_SIZE;
    tcp_socket->receive_buffer = CAlloc(TCP_WINDOW_SIZE);

    tcp_socket->max_segment_size = TCP_MSS;

    return tcp_socket;
}

I64 TCPSocketBind(CTCPSocket *tcp_socket, CSocketAddressStorage *address)
{ // Binds a TCP socket to address, which contains the local port and remote address to use.
    CTCPTreeNode       *head = tcp_globals.bound_socket_tree;
    CTCPTreeNode       *temp_node;
    CSocketAddressIPV4 *ipv4_address;
    CSocketAddressIPV4 *ipv4_destination;
    CSocketAddressIPV4 *ipv4_source;
    CSocketAddressIPV6 *ipv6_address;
    CSocketAddressIPV6 *ipv6_destination;
    U16                 port;

    if (!SocketBind(tcp_socket->socket))
    {
        NetErr("TCP SOCKET BIND: Failed, Socket state-machine must be in READY state.");
        return -1;
    }

    if (tcp_socket->state != TCP_STATE_CLOSED)
    {
        NetErr("TCP SOCKET BIND: Failed, TCP Socket must be in CLOSED state.");
        return -1;
    }

    switch (address->family)
    {
        case AF_INET:

            if (tcp_socket->source_address.family == AF_INET6)
            {
                NetErr("TCP SOCKET BIND: FIXME, IPV6->IPV4 TCP BIND");
                return -1;
            }

            ipv4_address = address;
            ipv4_destination = &tcp_socket->destination_address;

            ipv4_destination->family            = AF_INET;
            ipv4_destination->address.address   = ipv4_address->address.address; // bind socket to address in parameter.
//          ipv4_destination->port              = ipv4_address->port; // ... consistency would say keep in Big Endian ...

            ipv4_source = &tcp_socket->source_address;

            ipv4_source->port = ipv4_address->port;

            port = EndianU16(ipv4_address->port); // port member should be Big Endian,  so now we're going L.E (?)

            break;

        case AF_INET6:

            if (tcp_socket->source_address.family == AF_INET)
            {
                NetErr("TCP SOCKET BIND: Incompatible Address type.");
                return -1;
            }

            ipv6_address = address;
            ipv6_destination = &tcp_socket->destination_address;
            // ...
            // ...

            port = EndianU16(ipv6_address->port); // port member should be Big Endian,  so now we're going L.E (?)

            NetErr("TCP SOCKET BIND: FIXME, IPV6 TCP BIND");

            break;

        case AF_UNSPEC:
            NetErr("TCP SOCKET BIND: Error, AF_UNSPEC TCP BIND -- param family");
            break;
    }

    if (head)
    {
        // look for our port.
        temp_node = TCPTreeNodeFind(port, head);

        if (temp_node)
        { // if we find we have bound sockets at port, check address before adding to queue
            switch (address->family)
            {
                case AF_INET:
                    if (TCPTreeNodeQueueIPV4Find(ipv4_destination->address.address, temp_node, TRUE))
                    {
                        NetErr("TCP SOCKET BIND: Address already in Bound Socket Tree !");
                        return -1;
                    }
                    else
                    { // if no address match, free to add socket to the node queue
                        NetDebug("TCP SOCKET BIND: No address match, adding socket to node queue");
                        TCPTreeNodeQueueAdd(tcp_socket, temp_node);
                    }

                    break;

                case AF_INET6:
                    NetErr("TCP SOCKET BIND: FIXME, IPV6 TCP BIND");
                    break;

                case AF_UNSPEC:
                    NetErr("TCP SOCKET BIND: Error, AF_UNSPEC TCP BIND -- found in bound tree");
                    break;
            }
        }
        else
        { // if we get no node back from port search, we didn't find it and are free to add a new node.
            temp_node = TCPTreeNodeParamAdd(port, head); // add new node with port, return its *.
            TCPTreeNodeQueueAdd(tcp_socket, temp_node);
        }
    }
    else // if no bound sockets, we init the tree as a new node
    {
        tcp_globals.bound_socket_tree = head = TCPTreeNodeParamInit(port); //... shouuuld be in L.E .. ?
        TCPTreeNodeQueueAdd(tcp_socket, head); // add the tcp socket to the port queue
        // maybe more checks to do before this, dunno rn.
    }


    // do we need to do anything else after bind add to tree? ...


    switch (tcp_socket->socket->state)
    {
        case SOCKET_STATE_BIND_REQ: //  if BIND request success, set BOUND.
            tcp_socket->socket->state = SOCKET_STATE_BOUND;
            break;

        default:
            NetErr("TCP SOCKET BIND: Failed, Misconfigured Socket state-machine.");
            return -1;
    }

    return 0;
}

I64 TCPSocketClose(CTCPSocket *tcp_socket)
{
    CTCPTreeNode        *head = tcp_globals.bound_socket_tree;
    CTCPTreeNode        *node;
    CTCPTreeQueue       *queue;
    CTCPAckQueue        *send_buffer;
    CTCPAcceptQueue     *pending;
    CSocketAddressIPV4  *address = &tcp_socket->source_address;
    I64                  timeout;

    SocketClose(tcp_socket->socket); // TODO: testing on closing a socket while another task is using it

    switch (tcp_socket->state)
    {
        case TCP_STATE_ESTABLISHED:
            while (TCPSendFlags(tcp_socket, TCPF_FIN | TCPF_ACK) < 0)
            {
                TCPCheckACKQueue(tcp_socket);
                Sleep(1);
            }

            tcp_socket->state = TCP_STATE_FIN_WAIT1;

            // Shrine has TODOs:    - What states are allowed here?
            //                      - This can block forever if our recv buffer fills up
            //                        but remote insists on sending more.
            // Implementing timeout here to force close.
            timeout = counts.jiffies + tcp_socket->timeout * JIFFY_FREQ / 1000;

            while (tcp_socket->state == TCP_STATE_FIN_WAIT1 &&
                   tcp_socket->first_unacked_seq != tcp_socket->next_send_seq_num &&
                   counts.jiffies < timeout)
            {
                TCPCheckACKQueue(tcp_socket);
                Sleep(1);
            }

            if (tcp_socket->state == TCP_STATE_FIN_WAIT1)
                tcp_socket->state = TCP_STATE_FIN_WAIT2;

            timeout = counts.jiffies + tcp_socket->timeout * JIFFY_FREQ / 1000;
            while (tcp_socket->state == TCP_STATE_FIN_WAIT2 && counts.jiffies < timeout)
                Sleep(1);

            break;

        case TCP_STATE_CLOSE_WAIT:
            while (TCPSendFlags(tcp_socket, TCPF_FIN | TCPF_ACK) < 0)
            {
                TCPCheckACKQueue(tcp_socket);
                Sleep(1);
            }

            if (tcp_socket->state == TCP_STATE_CLOSE_WAIT)
                tcp_socket->state = TCP_STATE_LAST_ACK;

            timeout = counts.jiffies + tcp_socket->timeout * JIFFY_FREQ / 1000;
            while (tcp_socket->state == TCP_STATE_LAST_ACK &&
                   tcp_socket->first_unacked_seq != tcp_socket->next_send_seq_num &&
                   counts.jiffies < timeout)
            {
                TCPCheckACKQueue(tcp_socket);
                Sleep(1);
            }

            break;
    }

    if (IsTCPStateSync(tcp_socket))
        TCPSendFlags(tcp_socket, TCPF_RST);

    // remove from bound list, free associated allocs

    node = TCPTreeNodeFind(EndianU16(address->port), head);

    if (node)
        queue = TCPTreeNodeQueueSocketFind(tcp_socket, node);
    else
    {
        Debug("TODO: Didn't find node at socket during TCPSocketClose!\n");
        return -1;
    }

    if (queue)
    {
        TCPTreeNodeQueueSocketSinglePop(tcp_socket, node);

        if (node->queue == node->queue->next)
        { // if we popped the only queue on the node, remove the node.
            if (node == head)
            { // head is the global. if node is the global, change it and add branches.
                if (node->left)
                {
                    tcp_globals.bound_socket_tree = head = node->left;
                    if (node->right)
                        TCPTreeNodeAdd(node->right, head);
                }
                else if (node->right)
                    tcp_globals.bound_socket_tree = node->right;
                else
                    tcp_globals.bound_socket_tree = NULL;
            }
            else // if node is not the global, just pop it from the tree
                TCPTreeNodeSinglePop(node->value, head);

            Free(node);
        }

        Free(tcp_socket->socket);

        NetWarn("TCP SOCKET CLOSE: Freeing socket, ACK queue, accept() queue, & receive buffer.");
        Free(tcp_socket->receive_buffer);

        send_buffer = tcp_socket->ack_queue->next;
        while (send_buffer != tcp_socket->ack_queue)
        {
            NetWarn("TCP SOCKET CLOSE: Freeing send buffer @ 0x%0X", send_buffer);
            QueueRemove(send_buffer);
            Free(send_buffer->tcp_packet);
            Free(send_buffer);
            send_buffer = tcp_socket->ack_queue->next;
        }

        pending = tcp_socket->accept_queue->next;
        while (pending != tcp_socket->accept_queue)
        {
            NetWarn("TCP SOCKET CLOSE: Freeing pending connection @ 0x%0X", pending);
            QueueRemove(pending);
            Free(pending);
            pending = tcp_socket->accept_queue->next;
        }


        Free(tcp_socket);
        Free(queue);
    }
    else
    {
        Debug("TODO: Didn't find queue at socket during TCPSocketClose!\n");
        return -1;
    }
    
    return 0;

}

I64 TCPSocketConnect(CTCPSocket *tcp_socket, CSocketAddressStorage *address)
{ // Bind and connect a socket to the destination address and port in address param, local port automatic.
    U16                 source_port = EndianU16(0x8000 + tcp_globals.next_source_port++ & 0x7FFF);
    CTCPTreeNode       *head = tcp_globals.bound_socket_tree;
    CTCPTreeNode       *temp_node;
    CSocketAddressIPV4 *ipv4_address;
    CSocketAddressIPV4 *ipv4_destination;
    CSocketAddressIPV4 *ipv4_source;
    CSocketAddressIPV6 *ipv6_address;
    CSocketAddressIPV6 *ipv6_destination;
    I64                 timeout;
    U16                 port;


    if (!SocketConnect(tcp_socket->socket))
    {
        NetErr("TCP SOCKET CONNECT: Failed, Socket state-machine must be in READY state.");
        return -1;
    }

    if (tcp_socket->state != TCP_STATE_CLOSED)
    {
        NetErr("TCP SOCKET CONNECT: TCP Socket must be in CLOSED state.");
        return -1;
    }

    switch (address->family)
    {
        case AF_INET:

            if (tcp_socket->source_address.family == AF_INET6)
            {
                NetErr("TCP SOCKET CONNECT: FIXME, IPV6->IPV4 TCP CONNECT");
                return -1;
            }

            ipv4_address = address;
            ipv4_destination = &tcp_socket->destination_address;

            ipv4_destination->family            = AF_INET;
            ipv4_destination->address.address   = ipv4_address->address.address; // bind socket to address in parameter.
            ipv4_destination->port              = ipv4_address->port; // ... consistency would say keep in Big Endian ...

            ipv4_source = &tcp_socket->source_address;

            ipv4_source->port = source_port;

            port = EndianU16(source_port); // port member should be Big Endian,  so now we're going L.E (?)

            break;

        case AF_INET6:

            if (tcp_socket->source_address.family == AF_INET)
            {
                NetErr("TCP SOCKET CONNECT: Incompatible Address type.");
                return -1;
            }

            ipv6_address = address;
            ipv6_destination = &tcp_socket->destination_address;
            // ...
            // ...

            port = EndianU16(source_port); // port member should be Big Endian,  so now we're going L.E (?)

            NetErr("TCP SOCKET CONNECT: FIXME, IPV6 TCP CONNECT");

            break;

        case AF_UNSPEC:
            NetErr("TCP SOCKET CONNECT: Error, AF_UNSPEC TCP CONNECT -- param family");
            break;
    }

    if (head)
    {
        // look for our port.
        temp_node = TCPTreeNodeFind(port, head);

        if (temp_node)
        { // if we find we have bound sockets at port, check address before adding to queue
            switch (address->family)
            {
                case AF_INET:
                    if (TCPTreeNodeQueueIPV4Find(ipv4_destination->address.address, temp_node, TRUE))
                    {
                        NetErr("TCP SOCKET CONNECT: Address already in Bound Socket Tree !");
                        return -1;
                    }
                    else
                    { // if no address match, free to add socket to the node queue
                        NetDebug("TCP SOCKET CONNECT: No address match, adding socket to node queue");
                        TCPTreeNodeQueueAdd(tcp_socket, temp_node);
                    }

                    break;

                case AF_INET6:
                    NetErr("TCP SOCKET CONNECT: FIXME, IPV6 TCP CONNECT");
                    break;

                case AF_UNSPEC:
                    NetErr("TCP SOCKET CONNECT: Error, AF_UNSPEC TCP CONNECT -- found in bound tree");
                    break;
            }
        }
        else
        { // if we get no node back from port search, we didn't find it and are free to add a new node.
            temp_node = TCPTreeNodeParamAdd(port, head); // add new node with port, return its *.
            TCPTreeNodeQueueAdd(tcp_socket, temp_node);
        }
    }
    else // if no bound sockets, we init the tree as a new node
    {
        tcp_globals.bound_socket_tree = head = TCPTreeNodeParamInit(port); //... shouuuld be in L.E .. ?
        TCPTreeNodeQueueAdd(tcp_socket, head); // add the tcp socket to the port queue
        // maybe more checks to do before this, dunno rn.
    }

    tcp_socket->connection_time = tS;
    tcp_socket->receive_window  = TCP_WINDOW_SIZE;

    tcp_socket->state = TCP_STATE_SYN_SENT;
    TCPSendFlags(tcp_socket, TCPF_SYN);

    timeout = counts.jiffies + tcp_socket->timeout * JIFFY_FREQ / 1000;
    while (counts.jiffies < timeout)
    {
        switch (tcp_socket->state)
        {
            case TCP_STATE_ESTABLISHED:
            case TCP_STATE_CLOSED:
                timeout = 0; // break out of while loop
                break;

            default:
                Sleep(1);
        }
    }

    if (tcp_socket->state != TCP_STATE_ESTABLISHED)
    {
        NetErr("TCP SOCKET CONNECT: Failed to establish TCP Socket connection.");
        return -1;
    }

    switch (tcp_socket->socket->state)
    {
        case SOCKET_STATE_CONNECT_REQ: //   if CONNECT request success, set OPEN.
            tcp_socket->socket->state = SOCKET_STATE_OPEN;
            break;

        default:
            NetErr("TCP SOCKET CONNECT: Failed, Misconfigured Socket state-machine.");
            return -1;
    }

    return 0;
}

I64 TCPSocketListen(CTCPSocket *tcp_socket, I64 backlog_size)
{ // Set a bound socket to Listen for incoming connections. Backlog size is max amount of waiting connections allowed
    if (!SocketListen(tcp_socket->socket))
    {
        NetErr("TCP SOCKET LISTEN: Socket state-machine must be in BOUND state.");
        return -1;
    }

    if (tcp_socket->state != TCP_STATE_CLOSED)
    {
        NetErr("TCP SOCKET LISTEN: TCP Socket must be in CLOSED state.");
        return -1;
    }

    tcp_socket->state = TCP_STATE_LISTEN;
    tcp_socket->accept_queue_limit = backlog_size;

    switch (tcp_socket->socket->state)
    {
        case SOCKET_STATE_LISTEN_REQ: //    if LISTEN request success, set BOUND.
            tcp_socket->socket->state = SOCKET_STATE_LISTENING;
            break;

        default:
            NetErr("TCP SOCKET BIND: Failed, Misconfigured Socket state-machine.");
            return -1;
    }

    return 0;
}

CTCPSocket *TCPSocketAccept(CTCPSocket *tcp_socket)
{ // Accepts & creates a new socket, uses timeout inherited from Listening socket.
    CTCPAcceptQueue     *pending;
    CTCPSocket          *new_socket;
    CSocketAddressIPV4  *temp_addr;
    CSocketAddressIPV4   ipv4_address;
    I64                  timeout;

    if (!SocketAccept(tcp_socket->socket))
    {
        NetErr("TCP SOCKET ACCEPT: Failed, Socket state-machine must be in LISTENING state.");
        return NULL;
    }

    if (tcp_socket->state != TCP_STATE_LISTEN)
    {
        NetErr("TCP SOCKET LISTEN: TCP Socket must be in LISTEN state.");
        return NULL;
    }

    timeout = counts.jiffies + tcp_socket->timeout * JIFFY_FREQ / 1000;
    while ((pending = tcp_socket->accept_queue->next) == tcp_socket->accept_queue)
    {
        if (counts.jiffies > timeout)
            return NULL;
        else
            Sleep(1);
            // Yield;
    }

    QueueRemove(pending); // whether successful accept() or not, remove pending connection.

// TODO: rework accept logic to handle IPV6 addresses
    new_socket = TCPSocket(AF_INET);

    new_socket->next_recv_seq_num   = ++pending->segment_seq_num;
    new_socket->connection_time     = tS;
    new_socket->receive_window      = TCP_WINDOW_SIZE;
    new_socket->timeout             = tcp_socket->timeout;

    temp_addr = &tcp_socket->source_address;

    ipv4_address.family             = AF_INET;
//  ipv4_address.port               = pending->port;
    ipv4_address.port               = temp_addr->port;
    ipv4_address.address.address    = pending->ipv4_address;

    NetDebug("TCP SOCKET ACCEPT: Attempting to Bind to pending connection %0X @ src/dst ports %d,%d.",
                pending->ipv4_address, ipv4_address.port, pending->port);

    if (TCPSocketBind(new_socket, &ipv4_address) == -1)
    {
        Free(pending);
        return NULL;
    }

    temp_addr = &new_socket->destination_address;

    temp_addr->port = pending->port;

    new_socket->state = TCP_STATE_SYN_RECEIVED;

    NetDebug("TCP SOCKET ACCEPT: Attempting Send Flags SYN ACK back to requester.");
    TCPSendFlags(new_socket, TCPF_SYN | TCPF_ACK);

    timeout = counts.jiffies + new_socket->timeout * JIFFY_FREQ / 1000;
    while (counts.jiffies < timeout)
    {
        switch (new_socket->state)
        {
            case TCP_STATE_ESTABLISHED:
            case TCP_STATE_CLOSED:
                timeout = 0; // break out of while loop
                break;

            default:
                Sleep(1);
        }
    }

    if (new_socket->state != TCP_STATE_ESTABLISHED)
    {
        TCPSocketClose(new_socket);
        Free(pending);
        return NULL;
    }

    Free(pending);

    new_socket->socket->state = SOCKET_STATE_OPEN;

    return new_socket;
}

I64 TCPSocketReceive(CTCPSocket *tcp_socket, U8 *buffer, I64 length)
{
    I64 read_position;
    I64 write_position;
    I64 read_total = 0;
    I64 step;
    I64 timeout;

    if (!SocketReceive(tcp_socket->socket))
    {
        NetErr("TCP SOCKET RECEIVE: Failed, Socket state-machine must be in OPEN or BOUND state.");
        return NULL;
    }

    timeout = counts.jiffies + tcp_socket->timeout * JIFFY_FREQ / 1000;

    while ((tcp_socket->state == TCP_STATE_ESTABLISHED  ||
            tcp_socket->state == TCP_STATE_FIN_WAIT1    ||
            tcp_socket->state == TCP_STATE_CLOSE_WAIT)  && // allowing receive during closing state
           tcp_socket->read_position == tcp_socket->write_position)
    {
        TCPCheckACKQueue(tcp_socket);
        Sleep(1);

        if (counts.jiffies > timeout)
            break;
    }

    if ((tcp_socket->state != TCP_STATE_ESTABLISHED || tcp_socket->state == TCP_STATE_FIN_WAIT1) &&
        tcp_socket->read_position == tcp_socket->write_position || length == 0)
        return 0;


    if (counts.jiffies > timeout)
    {
        if (tcp_socket->timeout != 0) // Don't flood NetLog on non-blocking receives.
            NetErr("TCP SOCKET RECEIVE: Timed out.");

        return -1;
    }


    read_position   = tcp_socket->read_position;
    write_position  = tcp_socket->write_position;

    if (write_position < read_position)
    {
        step = tcp_socket->receive_buffer_size - read_position;

        if (step > length)
            step = length;

        MemCopy(buffer, tcp_socket->receive_buffer + read_position, step);
        buffer += step;
        length -= step;
//      read_position = (read_position + step) & (tcp_socket->receive_buffer_size - 1);
        read_position = (read_position + step) % tcp_socket->receive_buffer_size;
        read_total += step;
    }

    if (length > 0)
    {
        step = write_position - read_position;

        if (step > length)
            step = length;

        MemCopy(buffer, tcp_socket->receive_buffer + read_position, step);
        buffer += step;
        length -= step;
        read_position += step;
        read_total += step;
    }

    tcp_socket->read_position = read_position;

    return read_total;
}

I64 TCPSocketReceiveLine(CTCPSocket *tcp_socket, U8 *buffer, I64 size)
{
    I64 received = 0;

    while (received + 1 < size)
    {
        if (TCPSocketReceive(tcp_socket, buffer + received, 1) <= 0)
        {
            NetErr("TCP SOCKET RECEIVE LINE: Failed at TCPSocketReceive.");
            return -1;
        }
        if (buffer[received] == '\n')
            break;
        else if (buffer[received] != '\r')
            received++;
    }

    // Shrine has 'FIXME: safe but incorrect behavior on overflow'
    buffer[received] = 0; // terminate string
    return received;
}

I64 TCPSocketSend(CTCPSocket *tcp_socket, U8 *buffer, I64 length)
{
    I64 sent_total = 0;
    I64 timeout = counts.jiffies + tcp_socket->timeout * JIFFY_FREQ / 1000;
    I64 send_length;

    if (!SocketSend(tcp_socket->socket))
    {
        NetErr("TCP SOCKET SEND: Failed, Socket state-machine must be in OPEN state.");
        return -1;
    }

    while ((tcp_socket->state == TCP_STATE_ESTABLISHED || tcp_socket->state == TCP_STATE_CLOSE_WAIT) &&
           length)
    {
        send_length = (tcp_socket->first_unacked_seq + tcp_socket->send_window - tcp_socket->next_send_seq_num) & 0xFFFFFFFF;

        // Shrine has TODO: Keep trying, tie to timeout: RFC 793 "Managing The Window"

        if (send_length == 0)
        {
            if (sent_total > 0)
                break;
            else
            {
                TCPCheckACKQueue(tcp_socket);
                Sleep(1);
            }
        }
        else
        {
            if (send_length > length)
                send_length = length;

            if (send_length > tcp_socket->max_segment_size)
                send_length = tcp_socket->max_segment_size;

            NetDebug("TCP SOCKET SEND: Trying TCPSendData() of %d bytes.", send_length);
            if (TCPSendData(tcp_socket, TCPF_ACK, buffer, send_length) < 0)
            { // Stall until outgoing data acknowledged.
                if (sent_total > 0)
                    break;
                else
                {
                    TCPCheckACKQueue(tcp_socket);
                    Sleep(1);
                }

            }
            else
            {
                buffer      += send_length;
                length      -= send_length;
                sent_total  += send_length;
            }
        }

        if (counts.jiffies > timeout)
        {
            if (tcp_socket->timeout != 0) // Don't flood NetLog on non-blocking sends.
                NetErr("TCP SOCKET SEND: Timed out.");

            break;
        }
    }

    return sent_total;
}

I64 TCPSocketSendAll(CTCPSocket *tcp_socket, U8 *buffer, I64 length)
{
    I64 total   = 0;
    I64 sent    = 0;

    while (length)
    {
        sent = TCPSocketSend(tcp_socket, buffer, length);

        if (sent > 0)
        {
            buffer  += sent;
            total   += sent;
            length  -= sent;
        }
        else
            break;
    }

    return total;
}

I64 TCPSocketSendString(CTCPSocket *tcp_socket, U8 *string)
{
    return TCPSocketSendAll(tcp_socket, string, StrLen(string));
}

CTCPSocket *TCPConnectionCreate(U8 *hostname, U16 port)
{
    CSocketAddressIPV4   socket_addr;
    CAddressInfo        *current;
    CAddressInfo        *info = NULL;
    I64                  error = DNSAddressInfoGet(hostname, NULL, &info);
    CTCPSocket          *result;

    if (error < 0)
    {
        NetErr("TCP CONNECTION CREATE: Failed at DNSAddressInfoGet.");
        AddressInfoFree(info);
        return NULL;
    }

    socket_addr.family = AF_INET;
    socket_addr.port = EndianU16(port);
    socket_addr.address.address = INADDR_ANY;

    current = info;
    while (current)
    {

        if (current->family == AF_INET && (current->socket_type == 0 || current->socket_type == SOCKET_STREAM))
        { // TODO: IPV6 support !
            socket_addr.address.address = EndianU32(current->address(CSocketAddressIPV4 *)->address.address); // ?? why are bits flipped here
            AddressInfoFree(info);

            result = TCPSocket(AF_INET);
            if (!result)
            {
                NetErr("TCP CONNECTION CREATE: Failed to create new TCP Socket.");
                return NULL;
            }
            result->timeout = TCP_TIMEOUT * 3; // generous
            error = TCPSocketConnect(result, &socket_addr);
            if (error < 0)
            {
                NetErr("TCP CONNECTION CREATE: Failed to connect TCP Socket to address.");
                TCPSocketClose(result);
                return error;
            }

            return result;
        }

        current = current->next;
    }

    NetErr("TCP CONNECTION CREATE: Failed to find a suitable address.");

    AddressInfoFree(info);
    return NULL;

}

U0 TCPTreeNodeRep(CTCPTreeNode *node)
{
    CTCPTreeQueue       *queue = node->queue->next;
    CTCPSocket          *socket;
    CSocketAddressIPV4  *ipv4_addr;
    CSocketAddressIPV6  *ipv6_addr;
    U8                  *string;
    CTCPAcceptQueue     *pending;
    

    "Local Port $YELLOW$%d$FG$ (TCP Node @ $CYAN$0x%X$FG$):\n", node->value, node;

    while (queue != node->queue)
    {

        socket = queue->socket;

        switch (socket->destination_address.family)
        {

            case AF_UNSPEC:
                break;

            case AF_INET:
                ipv4_addr = &socket->destination_address;
                string = MStrPrint("%d.%d.%d.%d",
                            ipv4_addr->address.address.u8[3],
                            ipv4_addr->address.address.u8[2],
                            ipv4_addr->address.address.u8[1],
                            ipv4_addr->address.address.u8[0]); // todo: kludge, endianness...

                "   $BROWN$%s$FG$ Destination Port $YELLOW$%d$FG$ (TCP Tree Queue @ $CYAN$0x%X$FG$):\n",
                    string, EndianU16(ipv4_addr->port), queue;
                Free(string);

                break;

            case AF_INET6:
                ipv6_addr = &socket->destination_address;

                break;

            default:
                break;

        }

        "       Timeout: %dms\n", socket->timeout;

        pending = socket->accept_queue->next;

        while (pending != socket->accept_queue->next)
        {
            "       Pending connection from $BROWN$%d.%d.%d.%d$FG$:",
                pending->ipv4_address.u8[3],
                pending->ipv4_address.u8[2],
                pending->ipv4_address.u8[1],
                pending->ipv4_address.u8[0]; //todo: kludge, endianness?

            pending = pending->next;
        }


        queue = queue->next;
    }

    "\n";
}

U0 TCPRep()
{
    CTCPTreeNode    *node = tcp_globals.bound_socket_tree;
    CTCPRepEntry    *head;
    CTCPRepEntry    *entry;
    CTCPRepEntry    *temp_entry;

    "$LTBLUE$TCP Report:$FG$\n\n";

    if (node)
    {
        head = CAlloc(sizeof(CTCPRepEntry));
        QueueInit(head); // no QueueRemove the head

        entry = CAlloc(sizeof(CTCPRepEntry));
        entry->node = node;
        QueueInsert(entry, head);

        // perform depth-first-search while Entry Queue has nodes not fully visited.
        while (entry != head)
        {
            if (entry->node->left)
            { // if node has one, add an Entry for the left branch, continue loop.
                temp_entry = CAlloc(sizeof(CTCPRepEntry));
                temp_entry->node = entry->node->left;
                QueueInsertRev(temp_entry, head);

                // if left branch, but no right: toss early, now fully traveled.
                if (!entry->node->right)
                {
                    QueueRemove(entry);
                    TCPTreeNodeRep(entry->node);
                    Free(entry);
                }

                entry = temp_entry;
            }
            else if (entry->node->right)
            { // if no left, but right: add right to queue, pop Entry, Rep, set entry to right.
                temp_entry = CAlloc(sizeof(CTCPRepEntry));
                temp_entry->node = entry->node->right;
                QueueInsertRev(temp_entry, head);

                QueueRemove(entry);
                TCPTreeNodeRep(entry->node);
                Free(entry);

                entry = temp_entry;
            }
            else
            { // pop Entry, Rep, if last Entry in Queue has right add it, pop & Rep travelled Entry, entry = right.
                QueueRemove(entry);
                TCPTreeNodeRep(entry->node);
                Free(entry);

                if (head->last != head)
                {
                    temp_entry = head->last;

                    if (temp_entry->node->right)
                    {
                        entry = temp_entry;

                        temp_entry = CAlloc(sizeof(CTCPRepEntry));
                        temp_entry->node = entry->node->right;
                        QueueInsertRev(temp_entry, head);

                        QueueRemove(entry);
                        TCPTreeNodeRep(entry->node);
                        Free(entry);

                        entry = temp_entry;
                    }
                    else
                    {
                        QueueRemove(temp_entry);
                        TCPTreeNodeRep(temp_entry->node);
                        Free(temp_entry);

                        entry = head->last;
                    }
                }
                else
                    break;
            }
        }

        Free(head);
    }
    else
        "No TCP Sockets currently bound.\n\n";
}


TCPGlobalsInit;