#if defined(ARDUINO_ARCH_esp8266) || defined(ARDUINO_ARCH_ESP8266) ||\
	defined (ARDUINO_ARCH_ESP32) || defined(ADAFRUIT_FEATHER_M0)

#include <Arduino.h>
#include <stdbool.h>
#include <stdint.h>

#include "interface.h"
#include "link_interface.h"
#include "arduino.h"
#if defined(ARDUINO_ARCH_ESP8266)
	#include <ESP8266WiFi.h>
	#define TO_IPADDRESS(x) x
#elif defined(ARDUINO_ARCH_ESP32)
	#include <WiFi.h>
	#define TO_IPADDRESS(x) x
#elif defined(ADAFRUIT_FEATHER_M0)
	#include <SPI.h>
	#include <WiFi101.h>
	#define TO_IPADDRESS(x) IPAddress(x)
#else /* ADAFRUIT_FEATHER_M0 */
	#error Wifi not supported on current device
#endif /* ADAFRUIT_FEATHER_M0 */

WiFiClient client;
bool link_continuation = false;

struct LinkSettings link_settings =
	{ .serverMode=true
	, .port=COMM_PORT
#ifdef COMM_MQTT
	, .host=COMM_HOST
#else /* COMM_MQTT */
	, .host=NULL
#endif /* COMM_MQTT */
	};

struct LinkSettings get_link_settings ()
{
	return link_settings;
}

bool link_input_available(void)
{
#ifndef ARDUINO_ARCH_ESP32
	if (!client.connected()) {
#ifdef HAVE_OLEDSHIELD
		print_to_display("Server disconnected\n");
		delay(1000);
#endif /* HAVE_OLEDSHIELD */
		reset();
	}
#endif /* !defined(ARDUINO_ARCH_ESP32) */
	return client.available();
}

uint8_t link_read_byte(void)
{
	while (!link_input_available())
		msdelay(5);
	return client.read();
}

void link_write_byte(uint8_t b)
{
	client.write(b);
}

void open_link(struct LinkSettings ls)
{
	const char *ssids[] = SSIDS;
	const char *wpas[] = WPAS;

	link_settings = ls;

#if defined(ADAFRUIT_FEATHER_M0)
	WiFi.setPins(8, 7, 4, 2);
#else /* ADAFRUIT_FEATHER_M0 */
	WiFi.mode(WIFI_STA);
	WiFi.setSleepMode(WIFI_LIGHT_SLEEP);
#endif /* ADAFRUIT_FEATHER_M0 */

	int ssid = -1;

	if (!link_continuation) {
		do {
			Serial.println("Scanning...");
	#ifdef HAVE_OLEDSHIELD
			print_to_display("Scanning..\n");
	#endif /* HAVE_OLEDSHIELD */
			int n = WiFi.scanNetworks();
			Serial.print("Found ");
			Serial.print(n);
			Serial.println(" networks");
	#ifdef HAVE_OLEDSHIELD
			print_to_display(String(n).c_str());
			print_to_display(" found\n");
			flush_display();
	#endif /* HAVE_OLEDSHIELD */
			if (n == 0) {
	#ifdef HAVE_OLEDSHIELD
				print_to_display("none found\n");
				flush_display();
	#endif /* HAVE_OLEDSHIELD */
				Serial.println("No networks found");
				delay(1000);
				continue;
			}
			for (unsigned j = 0; j<sizeof(ssids)/sizeof(char *); j++) {
				for (int i = 0; i < n; i++) {
					if (strcmp(String(WiFi.SSID(i)).c_str(),
							ssids[j]) == 0) {
	#ifdef HAVE_OLEDSHIELD
						print_to_display("Try ");
						print_to_display(WiFi.SSID(i).c_str());
						print_to_display("\n");
						flush_display();
	#endif /* HAVE_OLEDSHIELD */
						Serial.print("Connect to: ");
						Serial.println(WiFi.SSID(i));
						ssid = j;
					}
				}
			}
		} while (ssid == -1);
#ifndef ADAFRUIT_FEATHER_M0
		WiFi.scanDelete();
#endif
		WiFi.begin(ssids[ssid], wpas[ssid]);
	} else {
		WiFi.begin();
	}
	while (WiFi.status() != WL_CONNECTED) {
		delay(1000);
		Serial.print(".");
#ifdef HAVE_OLEDSHIELD
		print_to_display(".");
		flush_display();
#endif /* HAVE_OLEDSHIELD */
	}
	Serial.print("\n");

#ifdef ADAFRUIT_FEATHER_M0
	WiFi.maxLowPowerMode();
#endif

#ifdef HAVE_OLEDSHIELD
	clear_display();
	print_to_display("Connected to: ");
	print_to_display(ssids[ssid]);
	print_to_display(" ");
	print_to_display(WiFi.localIP().toString().c_str());
	print_to_display(":");
	print_to_display(String(ls.port).c_str());
	print_to_display("\n");
	flush_display();
#endif /* HAVE_OLEDSHIELD */
	Serial.println("WiFi connected");
	Serial.println("IP address: ");
	Serial.println(TO_IPADDRESS(WiFi.localIP()));

	if (ls.serverMode) {
		WiFiServer server(ls.port);

		server.begin();
#ifndef ADAFRUIT_FEATHER_M0
		server.setNoDelay(true);
#endif
		Serial.print("Server started on port: ");
		Serial.println(ls.port);

		Serial.println("Waiting for a client to connect.");
		while (!(client = server.available())) {
			delay(10);
		}

#ifdef HAVE_OLEDSHIELD
		print_to_display("Client:\n");
		print_to_display(client.remoteIP().toString().c_str());
		flush_display();
#endif /* HAVE_OLEDSHIELD */
		Serial.println("Client connected\n");

	} else {
		if (!client.connect(ls.host, ls.port)) {
			Serial.println("Connecting to server failed.");
			reset();
		}
	}
}

void close_link(bool temporary)
{
	link_continuation = temporary;
	client.stop();
#ifdef ADAFRUIT_FEATHER_M0
	WiFi.end();
#endif
}

#endif /* defined(ARDUINO_ARCH_esp8266) || defined(ARDUINO_ARCH_ESP8266) ||\
	defined (ARDUINO_ARCH_ESP32) || defined(ADAFRUIT_FEATHER_M0)*/

