/* 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.
 */

#include <mbed.h>
#include "posix4mbed.h"
#include "peripheral_io_4mbed.h"
#include "primitives.h"

using namespace os::primitives;

inline const char* safe_str(const char* in) { return in ? in : "<null>"; }


#if ENABLE_MODULE_VIDEO==1
P4(video_h) P4(video_open)(video_source_t video_source)
{
	TRACE(ENTER "video_open()");
	P4(video_h) rc = video::open(video_source);
	TRACE(LEAVE "video_open() = %p", rc);
	return rc;
}

bool P4(video_start)(P4(video_h) handle, void* buf)
{
	TRACE(ENTER "video_start()");
	bool rc = handle ? video::start(handle, buf) : false;
	TRACE(LEAVE "video_start() = %d", rc);
	return rc;
}

bool P4(video_stop)(P4(video_h) handle)
{
	TRACE(ENTER "video_stop()");
	bool rc = handle ? video::stop(handle) : false;
	TRACE(LEAVE "video_stop() = %d", rc);
	return rc;
}

bool P4(video_close)(P4(video_h) handle)
{
	TRACE(ENTER "video_close()");
	bool rc = handle ? video::close(handle) : false;
	TRACE(LEAVE "video_close() = %d", rc);
	return rc;
}

namespace os { namespace video {

	union handle_instance {
		P4(video_h) handle;
		Video* instance;
		handle_instance(P4(video_h) h) : handle( h ) {}
	};

}}	// namespace os::video

P4(video_h) os::video::open(video_source_t video_source)
{
	Video* instance = new Video;
	if( instance && !instance->open(video_source) ) {
		delete instance;
		return 0;
	}
	return instance;
}

bool os::video::close(P4(video_h) handle)
{
	Video* instance = handle_instance( handle ).instance;
	bool rc = instance->close();
	delete instance;
	return rc;
}

bool os::video::start(P4(video_h) handle, void* buf)
{
	Video* instance = handle_instance( handle ).instance;
	return instance->start( buf );
}

bool os::video::stop(P4(video_h) handle)
{
	Video* instance = handle_instance( handle ).instance;
	return instance->stop();
}

#endif	// #if ENABLE_MODULE_VIDEO==1


#if ENABLE_MODULE_DISPLAY==1
P4(lcd_h) P4(lcd_open)(lcd_t *lcd)
{
	TRACE(ENTER "lcd_open()");
	P4(lcd_h) rc = lcd::open(lcd);
	TRACE(LEAVE "lcd_open() = %p", rc);
	return rc;
}

bool P4(lcd_close)(P4(lcd_h) handle)
{
	TRACE(ENTER "lcd_close()");
	bool rc = handle ? lcd::close(handle) : false;
	TRACE(LEAVE "lcd_close() = %d", rc);
	return rc;
}

bool P4(lcd_start)(P4(lcd_h) handle, lcd_layer_t lcd_layer)
{
	TRACE(ENTER "lcd_start()");
	bool rc = handle ? lcd::start(handle, lcd_layer) : false;
	TRACE(LEAVE "lcd_start() = %d", rc);
	return rc;
}

bool P4(lcd_stop)(P4(lcd_h) handle, lcd_layer_id_t id)
{
	TRACE(ENTER "lcd_stop()");
	bool rc = handle ? lcd::stop(handle, id) : false;
	TRACE(LEAVE "lcd_stop() = %d", rc);
	return rc;
}

bool P4(lcd_update)(P4(lcd_h) handle, lcd_layer_t lcd_layer)
{
	TRACE(ENTER "lcd_update()");
	bool rc = handle ? lcd::update(handle, lcd_layer) : false;
	TRACE(LEAVE "lcd_update() = %d", rc);
	return rc;
}

namespace os { namespace lcd {

	union handle_instance {
		P4(lcd_h) handle;
		Lcd* instance;
		handle_instance(P4(lcd_h) h) : handle( h ) {}
	};

}}	// namespace os::lcd

P4(lcd_h) os::lcd::open(lcd_t *lcd)
{
	Lcd* instance = new Lcd;
	if( instance && !instance->open(lcd) ) {
		delete instance;
		return 0;
	}
	return instance;
}

bool os::lcd::close(P4(lcd_h) handle)
{
	Lcd* instance = handle_instance( handle ).instance;
	bool rc = instance->close();
	delete instance;
	return rc;
}

bool os::lcd::start(P4(lcd_h) handle, lcd_layer_t lcd_layer)
{
	Lcd* instance = handle_instance( handle ).instance;
	return instance->start(lcd_layer);
}

bool os::lcd::stop(P4(lcd_h) handle, lcd_layer_id_t id)
{
	Lcd* instance = handle_instance( handle ).instance;
	return instance->stop(id);
}

bool os::lcd::update(P4(lcd_h) handle, lcd_layer_t lcd_layer)
{
	Lcd* instance = handle_instance( handle ).instance;
	return instance->update(lcd_layer);
}

#endif	// #if ENABLE_MODULE_DISPLAY==1


#if ENABLE_MODULE_JPEG==1
int P4(jpeg_encode)(jpeg_convert_data_t encode_data)
{
	TRACE(ENTER "jpeg_encode()");
	int rc = jpeg::encode(encode_data);
	TRACE(LEAVE "jpeg_encode() = %d", rc);
	return rc;
}

bool P4(jpeg_decode)(jpeg_convert_data_t decode_data)
{
	TRACE(ENTER "jpeg_decode()");
	bool rc = jpeg::decode(decode_data);
	TRACE(LEAVE "jpeg_decode() = %d", rc);
	return rc;
}

namespace os { namespace jpeg {

	Jpeg jpeg;

}}	// namespace os::jpeg

int os::jpeg::encode(jpeg_convert_data_t encode_data)
{
	return jpeg.encode(encode_data);
}

bool os::jpeg::decode(jpeg_convert_data_t decode_data)
{
	return jpeg.decode(decode_data);
}

#endif	// #if ENABLE_MODULE_JPEG==1


#if ENABLE_MODULE_GRAPHICS==1
P4(graphics_h) P4(initFrameBuffer)(char *buf, int width, int height, int format)
{
	TRACE(ENTER "initFrameBuffer()");
	P4(graphics_h) rc = graphics::initFrameBuffer(buf, width, height, format);
	TRACE(LEAVE "initFrameBuffer() = %p", rc);
	return rc;
}

int P4(deinitFrameBuffer)(P4(graphics_h) handle)
{
	TRACE(ENTER "deinitFrameBuffer()");
	int rc = handle ? graphics::deinitFrameBuffer(handle) : -1;
	TRACE(LEAVE "deinitFrameBuffer() = %d", rc);
	return rc;
}

int P4(drawLine)(P4(graphics_h) handle, int startX, int startY, int endX, int endY, uint32_t color)
{
	TRACE(ENTER "drawLine()");
	int rc = handle ? graphics::drawLine(handle, startX, startY, endX, endY, color) : -1;
	TRACE(LEAVE "drawLine() = %d", rc);
	return rc;
}

int P4(drawRect)(P4(graphics_h) handle, int x, int y, int width, int height, uint32_t color, bool fill)
{
	TRACE(ENTER "drawRect()");
	int rc = handle ? graphics::drawRect(handle, x, y, width, height, color, fill) : -1;
	TRACE(LEAVE "drawRect() = %d", rc);
	return rc;
}

int P4(drawArc)(P4(graphics_h) handle, int centerX, int centerY, int radius, int startAngle, int endAngle, uint32_t color)
{
	TRACE(ENTER "drawArc()");
	int rc = handle ? graphics::drawArc(handle, centerX, centerY, radius, startAngle, endAngle, color) : -1;
	TRACE(LEAVE "drawArc() = %d", rc);
	return rc;
}

int P4(drawCircle)(P4(graphics_h) handle, int centerX, int centerY, int radius, uint32_t color, bool fill)
{
	TRACE(ENTER "drawCircle()");
	int rc = handle ? graphics::drawCircle(handle, centerX, centerY, radius, color, fill) : -1;
	TRACE(LEAVE "drawCircle() = %d", rc);
	return rc;
}

int P4(drawEllipse)(P4(graphics_h) handle, int centerX, int centerY, int radiusX, int radiusY, uint32_t color, bool fill)
{
	TRACE(ENTER "drawEllipse()");
	int rc = handle ? graphics::drawEllipse(handle, centerX, centerY, radiusX, radiusY, color, fill) : -1;
	TRACE(LEAVE "drawEllipse() = %d", rc);
	return rc;
}

int P4(drawPolygon)(P4(graphics_h) handle, int centerX, int centerY, int radius, int sides, uint32_t color, bool fill)
{
	TRACE(ENTER "drawPolygon()");
	int rc = handle ? graphics::drawPolygon(handle, centerX, centerY, radius, sides, color, fill) : -1;
	TRACE(LEAVE "drawPolygon() = %d", rc);
	return rc;
}

int P4(drawText)(P4(graphics_h) handle, char* text, int x, int y, int size, uint32_t color, uint32_t background)
{
	TRACE(ENTER "drawText()");
	int rc = handle ? graphics::drawText(handle, text, x, y, size, color, background) : -1;
	TRACE(LEAVE "drawText() = %d", rc);
	return rc;
}

int P4(drawImage)(P4(graphics_h) handle, char* image, int x, int y, int width, int height)
{
	TRACE(ENTER "drawImage()");
	int rc = handle ? graphics::drawImage(handle, image, x, y, width, height) : -1;
	TRACE(LEAVE "drawImage() = %d", rc);
	return rc;
}

namespace os { namespace graphics {

	union handle_instance {
		P4(graphics_h) handle;
		Graphics* instance;
		handle_instance(P4(graphics_h) h) : handle( h ) {}
	};

	static int convert_buffer_format(display_pixel_format_t format) {
		switch(format) {
		case DISPLAY_PIXELFORMAT_RGB565:	return Graphics::FORMAT_RGB565;
		case DISPLAY_PIXELFORMAT_RGB888:	return Graphics::FORMAT_RGB888;
		case DISPLAY_PIXELFORMAT_ARGB8888:	return Graphics::FORMAT_ARGB8888;
		case DISPLAY_PIXELFORMAT_ARGB4444:	return Graphics::FORMAT_ARGB4444;
		default:							return -1;
		}
	}
}}	// namespace os::graphics

P4(graphics_h) os::graphics::initFrameBuffer(char *buf, int width, int height, int format)
{
	Graphics* instance = new Graphics;
	if( instance && (instance->initFrameBuffer(buf, width, height, convert_buffer_format((display_pixel_format_t)format)) < 0 ) ) {
		delete instance;
		return 0;
	}
	return instance;
}

int os::graphics::deinitFrameBuffer(P4(graphics_h) handle)
{
	Graphics* instance = handle_instance( handle ).instance;
	delete instance;
	return 0;
}

int os::graphics::drawLine(P4(graphics_h) handle, int startX, int startY, int endX, int endY, uint32_t color)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawLine(startX, startY, endX, endY, color);
	instance->flush();
	return ret;
}

int os::graphics::drawRect(P4(graphics_h) handle, int x, int y, int width, int height, uint32_t color, bool fill)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawRect(x, y, width, height, color, (fill ? 1 : 0));
	instance->flush();
	return ret;
}

int os::graphics::drawArc(P4(graphics_h) handle, int centerX, int centerY, int radius, int startAngle, int endAngle, uint32_t color)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawArc(centerX, centerY, radius, startAngle, endAngle, color);
	instance->flush();
	return ret;
}

int os::graphics::drawCircle(P4(graphics_h) handle, int centerX, int centerY, int radius, uint32_t color, bool fill)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawCircle(centerX, centerY, radius, color, (fill ? 1 : 0));
	instance->flush();
	return ret;
}

int os::graphics::drawEllipse(P4(graphics_h) handle, int centerX, int centerY, int radiusX, int radiusY, uint32_t color, bool fill)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawEllipse(centerX, centerY, radiusX, radiusY, color, (fill ? 1 : 0));
	instance->flush();
	return ret;
}

int os::graphics::drawPolygon(P4(graphics_h) handle, int centerX, int centerY, int radius, int sides, uint32_t color, bool fill)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawPolygon(centerX, centerY, radius, sides, color, (fill ? 1 : 0));
	instance->flush();
	return ret;
}

int os::graphics::drawText(P4(graphics_h) handle, char* text, int x, int y, int size, uint32_t color, uint32_t background)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawText(text, x, y, size, color, background);
	instance->flush();
	return ret;
}

int os::graphics::drawImage(P4(graphics_h) handle, char* image, int x, int y, int width, int height)
{
	Graphics* instance = handle_instance( handle ).instance;
	int ret = instance->drawImage(image, x, y, width, height);
	instance->flush();
	return ret;
}

#endif	// #if ENABLE_MODULE_GRAPHICS==1

