/* 
 * File:   TFT_ILI9341.h
 * Author: Gabor
 *
 * Created on 2016. szeptember 4., 18:30
 */

#ifndef TFT_ILI9341_H
#define	TFT_ILI9341_H

#include <HardwareProfile.h>
#include <Types.h>
#include <Delays.h>

#include <cstdio>
#include <cstdarg>
#include <cwchar>

#include "Images.h"

using namespace System;
using namespace System::Types;

#define ILI9341_TFTWIDTH  240
#define ILI9341_TFTHEIGHT 320

#define KEEP_CS_LOW
typedef enum {
    PORTRAIT,
    LANDSCAPE,
    INV_PORTRAIT,
    INV_LANDSCAPE
} orient_e;

#define RGBCONVERT(color) (uint16_t) ((color & 0xF80000) >> 8 |  (color & 0xFC00) >> 5 | (color & 0xF8) >> 3)
#define RGB565CONVERT(red, green, blue) (uint16_t) (((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3))

struct {
    const static uint16_t Black =           RGB565CONVERT(0, 0, 0);
    const static uint16_t BrightBlue =      RGB565CONVERT(0, 0, 255);
    const static uint16_t BrightGreen =    RGB565CONVERT(0, 255, 0);
    const static uint16_t BrightCyan =      RGB565CONVERT(0, 255, 255);
    const static uint16_t BrightRed =       RGB565CONVERT(255, 0, 0);
    const static uint16_t BrightMagenta =  RGB565CONVERT(255, 0, 255);
    const static uint16_t BrightYellow =   RGB565CONVERT(255, 255, 0);
    const static uint16_t Blue =           RGB565CONVERT(0, 0, 128);
    const static uint16_t Green =          RGB565CONVERT(0, 128, 0);
    const static uint16_t Cyan =           RGB565CONVERT(0, 128, 128);
    const static uint16_t Red =            RGB565CONVERT(128, 0, 0);
    const static uint16_t Magenta =         RGB565CONVERT(128, 0, 128);
    const static uint16_t Brown =          RGB565CONVERT(255, 128, 0);
    const static uint16_t LightGray =      RGB565CONVERT(128, 128, 128);
    const static uint16_t DarkGray =       RGB565CONVERT(64, 64, 64);
    const static uint16_t LightBlue =       RGB565CONVERT(128, 128, 255);
    const static uint16_t LightGreen =     RGB565CONVERT(128, 255, 128);
    const static uint16_t LightCyan =       RGB565CONVERT(128, 255, 255);
    const static uint16_t LightRed =       RGB565CONVERT(255, 128, 128);
    const static uint16_t LightMagenta =    RGB565CONVERT(255, 128, 255);
    const static uint16_t Yellow =         RGB565CONVERT(255, 255, 128);
    const static uint16_t White =          RGB565CONVERT(255, 255, 255);
    const static uint16_t Gray0 =           RGB565CONVERT(224, 224, 224);
    const static uint16_t Gray1 =          RGB565CONVERT(192, 192, 192);
    const static uint16_t Gray2 =          RGB565CONVERT(160, 160, 160);
    const static uint16_t Gray3 =          RGB565CONVERT(128, 128, 128);
    const static uint16_t Gray4 =          RGB565CONVERT(96, 96, 96);
    const static uint16_t Gray5 =          RGB565CONVERT(64, 64, 64);
    const static uint16_t Gray6 =          RGB565CONVERT(32, 32, 32);

    static uint16_t fromRgb(uint8_t r, uint8_t g, uint8_t b) {
        return (r >> 3) << 11 | (g >> 2) << 5 | b >> 3;
    }
} Color;


#ifdef __XC32
#define TFT_ILI9341 _TFT_ILI9341
class _TFT_ILI9341
#else
class TFT_ILI9341
#endif
{
public:
    void setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
    
    void initialize(orient_e orientation = PORTRAIT);

    void drawPixel(uint16_t x, uint16_t y, uint16_t color);

    int32_t drawChar(wchar_t ch);
    int32_t drawChar(int16_t x, int16_t y, wchar_t ch);
    int32_t drawChar(wchar_t ch, uint16_t foreColor, uint16_t backColor, const uint8_t* font);
    int32_t drawChar(int16_t x, int16_t y, wchar_t ch, uint16_t foreColor, uint16_t backColor, const uint8_t* font);

    void clearScreen();
    void fillScreen(uint16_t color);

    void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
    void drawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
    void drawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);

    void drawRectangle(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
    void fillRectangle(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
    void drawRoundRectangle(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
    void fillRoundRectangle(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);

    void setOrientation(orient_e orientation);
    void invertDisplay(bool i);

    void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
    void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color);
    void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
    void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color);

    void drawEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color);
    void fillEllipse(int16_t x0, int16_t y0, int16_t rx, int16_t ry, uint16_t color);

    void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
    void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);

    void drawImage(const Image* image);
    void drawBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_t w, int16_t h);

    void cursor(int16_t x, int16_t y);
    void cursor(int16_t x, int16_t y, const uint8_t* font);
    int16_t Xcursor() const { return cursorX; }
    int16_t Ycursor() const { return cursorY; }
    void textColor(uint16_t color);
    void textColor(uint16_t foreColor, uint16_t backColor);

    const uint8_t* textFont(const uint8_t* font);
    int32_t print(const wchar_t* f, ...);
    int32_t print(const uint8_t* font, const wchar_t* f, ...);
    int32_t print(int16_t x, int16_t y, const wchar_t* f, ...);
    int32_t print(int16_t x, int16_t y, const uint8_t* font, const wchar_t* f, ...);
    int32_t print(uint16_t foreColor, uint16_t backColor, const uint8_t* font, const wchar_t* f, ...);
    int32_t print(int16_t x, int16_t y, uint16_t foreColor, uint16_t backColor, const uint8_t *font, const wchar_t* f, ...);

    orient_e getOrientation();

    int16_t height();
    int16_t width();
    int16_t textWidth(const wchar_t *string);
    int16_t fontHeight();

    void fadeIn(uint32_t time);
    void fadeOut(uint32_t time);
    void Sleep(bool enter);
protected:
    int16_t _width, _height; // Display w/h as modified by current orientation
    int16_t cursorX, cursorY; // Text cursor position and width padding

    uint16_t fColor, bColor, fontsloaded;

    const uint8_t *font;
    uint32_t _fontFirstChar;
    uint32_t _fontLastChar;
    int16_t _fontHeight;
    orient_e orientation; // Display orientation (0-3)
private:

    typedef struct {
        byte fontID; // User assigned value
        byte res1 : 4; // Reserved for future use (must be set to 0).
        byte orient : 2; // Orientation
        byte res2 : 2; // Reserved for future use (must be set to 0).
        uint16_t firstChar; // Character code of first character (e.g. 32).
        uint16_t lastChar; // Character code of last character in font (e.g. 3006).
        byte height; // Font characters height in pixels.
        byte reserved; // Reserved for future use (must be set to 0).
    } FONT_HEADER;

    typedef struct {
        byte width;
        byte offsetLSB;
        uint16_t offsetMSB;
    } GLYPH_ENTRY;

    int32_t print(const wchar_t *f, _Va_list args);

    void writeCommand(uint8_t c);
    void writeData(uint8_t d);
    

    template <typename T> static void swap(T& a, T& b);
};
#ifdef __XC32
#undef TFT_ILI9341
typedef _TFT_ILI9341 TFT_ILI9341;
#endif


#endif	/* TFT_ILI9341_H */

