/* Copyright 2017-present Renesas Electronics Corporation and other contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef PRIMITIVES_INCL
#define PRIMITIVES_INCL

#include <list>
#include <map>

#include <mbed.h>
	#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS
	#define LWIP_HDR_IP_ADDR_H
#include <EthernetInterface.h>

#include "DisplayBace.h"
#include "JPEG_Converter.h"

#define os mbed

namespace os { namespace primitives {

typedef ::Mutex Mutex;

class lock_guard {
	Mutex& m_;
	lock_guard( const lock_guard& );
	lock_guard& operator=( const lock_guard& );
public:
	explicit lock_guard( Mutex& m ) : m_( m )	{ m_.lock();	}
	~lock_guard()								{ m_.unlock();	}
};

typedef ::Semaphore Semaphore;

class CondVar {		// condition variable
	Mutex mutex_;
	std::list<Semaphore*> sems_;
	CondVar( const CondVar& );
	CondVar& operator=( const CondVar& );
public:
	CondVar();
	void notify_one();
	void wait( P4(pthread_mutex_t)* lock );
};

typedef ::Thread Thread;

typedef ::nsapi_version_t nsapi_version_t;
typedef ::nsapi_addr_t nsapi_addr_t;

typedef P4(in_addr)  ip4_addr_t;
typedef P4(in6_addr) ip6_addr_t;
struct ip_addr_t {
	enum { UNSPEC=0, IPv4=4, IPv6=6 } version;
	bool v4()    const { return version == IPv4; }
	bool v6()    const { return version == IPv6; }
	bool valid() const { return v4() || v6(); }
	union {
		ip4_addr_t ip4;
		ip6_addr_t ip6;
		u16_t u16[8];
		u8_t bytes[16];
	} value;
};

typedef ::nsapi_error_t nsapi_error_t;
typedef ::nsapi_size_or_error_t nsapi_error_t;
typedef ::nsapi_size_t nsapi_size_t;

class SocketAddress : public ::SocketAddress {
public:
	SocketAddress(const char* addr, uint16_t port=0) : ::SocketAddress(addr, port) {}
	SocketAddress(const void* bytes, nsapi_version_t version, uint16_t port=0) : ::SocketAddress(bytes, version, port) {}
	SocketAddress(nsapi_addr_t addr=nsapi_addr_t(), uint16_t port=0) : ::SocketAddress(addr, port) {}
	void dbg() const;
	static SocketAddress trans(const P4(sockaddr)& addr);
	static int trans(const SocketAddress& sa, void* addr, P4(socklen_t)* addrlen);
};

typedef ::EthernetInterface EthernetInterface;
typedef ::Socket Socket;
typedef ::UDPSocket UDPSocket;
typedef ::TCPSocket TCPSocket;
typedef ::TCPServer TCPServer;

class Nic {
	mutable EthernetInterface eth_;
	bool connect();
	bool disconnect();
	Nic( const Nic& );
	Nic& operator=( const Nic& );
	bool is_ip_assigned() const;
	typedef std::map<std::string, std::string> Params;
	Params parse_params( const std::string& str_params ) const;
	const char* targetIF() const { return "ETHERNET"; }
public:
	EthernetInterface& iface() { connect(); return eth_; }
	Nic() : eth_() {}
	~Nic() { disconnect(); }
	static char* enumerate();
	bool ifup(const std::string& nic, const std::string& str_params);
	bool ifdown(const std::string& nic);
	char* ifconfig(const std::string& nic);
	bool ntpdate(const std::string& nic, const char* server="0.pool.ntp.org", int port=123, int timeout=10*1000);
};

}}	// namespace os::primitives::

namespace os { namespace video {

class Video {
	bool camera_initialize();
	video_source_t video_source;
public:
	Video() {};
	~Video() {};
	bool open(video_source_t video_source);
	bool close(void);
	bool start(void* buf);
	bool stop();
};

}}	// namespace os::video::

namespace os { namespace lcd {

class Lcd {
	lcd_t lcd;
public:
	Lcd() {};
	~Lcd() {};
	bool open(lcd_t *lcd);
	bool close(void);
	bool start(lcd_layer_t lcd_layer);
	bool stop(lcd_layer_id_t id);
	bool update(lcd_layer_t lcd_layer);
};

}}	// namespace os::lcd::

namespace os { namespace jpeg {

class Jpeg {
	JPEG_Converter Jcu;
public:
	int encode(jpeg_convert_data_t encode_data);
	bool decode(jpeg_convert_data_t decode_data);
};

}}	// namespace os::jpeg::

namespace os { namespace graphics {

class Graphics {
public:

    Graphics();

    int initFrameBuffer(char *buf, int width, int height, int format);
    int drawLine(int startX, int startY, int endX, int endY, uint32_t color);
    int drawRect(int x, int y, int width, int height, uint32_t color, int fill);
    int drawArc(int centerX, int centerY, int radius, int startAngle, int endAngle, uint32_t color);
    int drawCircle(int centerX, int centerY, int radius, uint32_t color, int fill);
    int drawEllipse(int centerX, int centerY, int radiusX, int radiusY, uint32_t color, int fill);
    int drawPolygon(int centerX, int centerY, int radius, int sides, uint32_t color, int fill);
    int drawText(char* text, int x, int y, int size, uint32_t color, uint32_t background);
	int drawImage(char* image, int x, int y, int width, int height);
    void flush(void);

    enum {
        FORMAT_RGB565 = 0,
        FORMAT_RGB888 = 1,
        FORMAT_ARGB8888 = 2,
        FORMAT_ARGB4444 = 3,
    };

private:

    char *m_buf;
    int m_width;
    int m_height;
    int m_format;

    template <typename T> int drawLineImpl(int ps, int qs, int pe, int qe, int pm, int qm, int pb, int qb, T color);
    template <typename T> int drawRectImpl(int xs, int ys, int xe, int ye, T color, int fill, int frames);
    template <typename T> int drawArcImpl(int xc, int yc, int r, int xs, int ys, int as, int xe, int ye, int ae, T color);
    template <typename T> int drawEllipseImpl(int xc, int yc, int a, int b, T color, int fill);
    template <typename T> void drawPolygonLineImpl(int ps, int qs, int pe, int qe, int pc, int qc, int pm, int qm, int pb, int qb, T color, int mirrors, int fillDir);
    template <typename T> int drawImageImpl(T* image, int xs, int ys, int xe, int ye, int dx, int dy, int width);

    template <typename T> inline T *getBuffer(int x, int y) { return &((T *)m_buf)[x + y * m_width]; }
};

}}	// namespace os::graphics::

namespace helper {

	inline uint32_t htonl(uint32_t hostlong)  { return __REV  ( hostlong  ); }
	inline uint32_t ntohl(uint32_t netlong)   { return __REV  ( netlong   ); }
	inline uint16_t htons(uint16_t hostshort) { return __REV16( hostshort ); }
	inline uint16_t ntohs(uint16_t netshort)  { return __REV16( netshort  ); }

	std::string& cwd();

	int set_errno(int num);
	int clear_errno();

	union sockaddrs {
		P4(sockaddr_storage)*		sas;
		const P4(sockaddr_storage)*	csas;
		P4(sockaddr)*				sa;
		const P4(sockaddr)*			csa;
		P4(sockaddr_in)*			sa4;
		const P4(sockaddr_in)*		csa4;
		P4(sockaddr_in6)*			sa6;
		const P4(sockaddr_in6)*		csa6;
	};

}	// namespace helper::

#endif	// #ifndef PRIMITIVES_INCL

