//
// Ethernet interface common operations library
//
// Department of Mechatronics
// Tallinn University of Technology
// © Copyrights 2012
//

//
// Take use of pin and timer operations
//
#include "pin.h"
#if defined (__AVR_ATmega128__)
#include "timer128.h"
#elif defined (__AVR_ATmega2561__)
#include "timer2561.h"
#endif

//
// Include Ethernet definitions
//
#include "module/ethernet.h"
#include "module/enc28j60.h"

#define __PROG_TYPES_COMPAT__
#include "module/ip_arp_udp_tcp.h"
#include "module/websrv_help_functions.h"
#include "module/net.h"
#include "module/dnslkup.h"
#include "module/dhcp_client.h"

//
// Include Homelab definitions
//
#include "pin.h"
#include "spi.h"
#include "delay.h"

//
// Include AVR definitions
//
#include <avr/io.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <avr/pgmspace.h>

//
// Please modify the following lines. mac and ip have to be unique
// in your local area network. You can not have the same numbers in
// two devices:
static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x29};

uint8_t otherside_www_ip[4]; // dns will fill this
// My own IP (DHCP will provide a value for it):
static uint8_t myip[4];
// Default gateway (DHCP will provide a value for it):
static uint8_t gwip[4];
static uint8_t gwmac[6];
uint8_t otherside_www_gwmac[6];
// Netmask (DHCP will provide a value for it):
static uint8_t netmask[4];
char urlvarstr[32];

// packet buffer
#define BUFFER_SIZE 650
static uint8_t buf[BUFFER_SIZE+1];
static uint8_t start_web_client=0;
static uint8_t web_client_sendok=0;
static int8_t processing_state=0;

//
// Set ethernet driver into standby mode if not used
// Ethernet driver not needed to init
//
void ethernet_standby (void)
{
	pin cseth = PIN(G, 1);
	pin rste = PIN(E, 5);
	pin_setup_output(cseth);
	pin_setup_output(rste);
	
	//SPI_init();
	pin_set(rste);
	pin_clear(cseth);
	SPI_write(0xFF);		//soft reset
	_delay_ms(10);
	pin_set(cseth);
	pin_clear(cseth);
	SPI_write(64 + 0x1E);	//write command + ECON2 adr
	SPI_write(0b00101000);	//bits PWRSV, VRPS
	_delay_ms(1);
	pin_set(cseth);
}

//
//	Init Ethernet driver for our MAC and IP
//
void ethernet_init(uint8_t *mac, uint8_t *ip)
{
	// initialize enc28j60
    enc28j60Init(mac);
    enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
    hw_delay_ms(12); // 12ms
        
    // Magjack leds configuration, see enc28j60 datasheet, page 11
    // LEDB = yellow, LEDA = green
    // 0x476 is PHLCON LEDA = links status, LEDB = receive/transmit
    // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
    enc28j60PhyWrite(PHLCON,0x476);
    hw_delay_ms(12); // 12ms

    // init the ethernet/ip layer:
    init_udp_or_www_server(mac,ip);
}

//
//	Poll until valid data packet from Ethernet is received
//
uint16_t ethernet_get_packet (uint16_t maxlen, uint8_t* packet) 
{
   return(enc28j60PacketReceive(maxlen, packet));
}

//
// Load html data from program memory into TCP send buffer
//
uint16_t ethernet_load_data (uint8_t *buf,uint16_t pos, const prog_char *progmem_s)
{
	return(fill_tcp_data_p(buf,pos, progmem_s));
	
}

//
// Load html data from program memory into TCP send buffer
//
uint16_t ethernet_load_data_var (uint8_t *buf,uint16_t pos, char *string)
{
    return(fill_tcp_data(buf,pos, string));

}

//
// Display html page with preloaded data
//
void ethernet_print_webpage (uint8_t *buf,uint16_t dlen)
{
	www_server_reply(buf,dlen);
}

//
//	Send pong and wait TCP packet. Return URL.
//
uint16_t ethernet_analyse_packet(uint8_t *buf,uint16_t plen)
{
	return(packetloop_arp_icmp_tcp(buf,plen));
}

// we were ping-ed by somebody, store the ip of the ping sender
// and trigger an upload to http://tuxgraphics.org/cgi-bin/upld
// This is just for testing and demonstration purpose
//
// the __attribute__((unused)) is a gcc compiler directive to avoid warnings about unsed variables.
void browserresult_callback(uint16_t webstatuscode,uint16_t datapos __attribute__((unused)), uint16_t len __attribute__((unused)))
{
    if (webstatuscode==200)
    {
        web_client_sendok++;
    }
}

// the __attribute__((unused)) is a gcc compiler directive to avoid warnings about unsed variables.
void arpresolver_result_callback(uint8_t *ip __attribute__((unused)),uint8_t reference_number,uint8_t *mac)
{
    uint8_t i=0;
    if (reference_number==TRANS_NUM_GWMAC)
    {
        // copy mac address over:
        while(i<6)
        {
            gwmac[i]=mac[i];
            i++;
        }
    }
    if (reference_number==TRANS_NUM_WEBMAC)
    {
        // copy mac address over:
        while(i<6)
        {
            otherside_www_gwmac[i]=mac[i];
            i++;
        }
    }
}

void ethernet_init_client()
{
    /*initialize enc28j60*/
    enc28j60Init(mymac);
    enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
    _delay_loop_1(0); // 60us

    /* Magjack leds configuration, see enc28j60 datasheet, page 11 */
    // LEDB=yellow LEDA=green
    //
    // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
    // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
    enc28j60PhyWrite(PHLCON,0x476);

}

void ethernet_getDNSIP()
{
    uint16_t plen;
    uint8_t rval;
    // DHCP handling. Get the initial IP
    rval=0;
    init_mac(mymac);
    dhcp_6sec_tick();
    dhcp_6sec_tick();
    dhcp_6sec_tick();
    dhcp_6sec_tick();
    dhcp_6sec_tick();
    dhcp_6sec_tick();
    while(rval==0)
    {
        plen=enc28j60PacketReceive(BUFFER_SIZE, buf);
        buf[BUFFER_SIZE]='\0';
        rval=packetloop_dhcp_initial_ip_assignment(buf,plen,mymac[5]);
    }
    // we have an IP:
    dhcp_get_my_ip(myip,netmask,gwip);
    client_ifconfig(myip,netmask);
    if (gwip[0]==0)
    {
        // we must have a gateway returned from the dhcp server
        // otherwise this code will not work
        while(1); // stop here
    }
    // we have a gateway.
    // find the mac address of the gateway (e.g your dsl router).
    get_mac_with_arp(gwip,TRANS_NUM_GWMAC,&arpresolver_result_callback);
    while(get_mac_with_arp_wait())
    {
        // to process the ARP reply we must call the packetloop
        plen=enc28j60PacketReceive(BUFFER_SIZE, buf);
        packetloop_arp_icmp_tcp(buf,plen);
    }

    if (string_is_ipv4(WEBSERVER_VHOST))
    {
        // the the webserver_vhost is not a domain name but alreay
        // an IP address written as sting
        parse_ip(otherside_www_ip,WEBSERVER_VHOST);
        processing_state=2; // no need to do any dns look-up
    }
}

void ethernet_get_server()
{
    uint16_t dat_p,plen;

    while(1)
    {
        plen=enc28j60PacketReceive(BUFFER_SIZE, buf);
        buf[BUFFER_SIZE]='\0'; // http is an ascii protocol. Make sure we have a string terminator.
        // DHCP renew IP:
        //plen=packetloop_dhcp_renewhandler(buf,plen); // for this to work you have to call dhcp_6sec_tick() every 6 sec
        dat_p=packetloop_arp_icmp_tcp(buf,plen);

        if(plen==0)
        {
            // we are idle here
            if (processing_state==0)
            {
                if (!enc28j60linkup()) continue; // only for dnslkup_request we have to check if the link is up.

                processing_state=1;
                dnslkup_request(buf,WEBSERVER_VHOST,gwmac);
                continue;
            }
            if (processing_state==1 && dnslkup_haveanswer())
            {
                dnslkup_get_ip(otherside_www_ip);
                processing_state=2;
            }
            if (processing_state==2)
            {
                if (route_via_gw(otherside_www_ip))
                {
                    for(uint8_t i = 0; i < 6; i++)
                        otherside_www_gwmac[i]=gwmac[i];
                    processing_state=4;
                }
                else
                {
                    get_mac_with_arp(otherside_www_ip,TRANS_NUM_WEBMAC,&arpresolver_result_callback);
                    processing_state=3;
                }
                continue;
            }
            if (processing_state==3 && get_mac_with_arp_wait()==0)
            {
                // we have otherside_www_gwmac
                processing_state=4;
            }
            if (processing_state==4)
            {
                processing_state=5;
                // we are ready to upload the first measurement data after startup:
                if (start_web_client==0) start_web_client=1;

            }
            //----------
            if (start_web_client==1)
            {
				return;
            }

            continue;
        }
        if(dat_p==0)  // plen!=0
        {
            udp_client_check_for_dns_answer(buf,plen);
            continue;
        }
    }
}



