// u8g2 Persian Reshaper
// Copyright (C) 2019 Ramin Sangesari
// https://github.com/idreamsi/u8g2-persian-reshaper

//#include <Arduino.h>
#include <U8g2lib.h>
#include "pixia.h"
#include "idreams.h"

U8G2_SSD1306_128X64_ALT0_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);   // same as the NONAME variant, but may solve the "every 2nd line skipped" problem
//U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 12, /* dc=*/ 4, /* reset=*/ 6);	// Arduboy (Production, Kickstarter Edition)
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* reset=*/ 8);
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 21, /* data=*/ 22, /* reset=*/ U8X8_PIN_NONE);   // ESP32 Thing, pure SW emulated I2C
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17); 


//----------------------------------------------------------------------------
//#define DEBUG
#define LCDWidth                        u8g2.getDisplayWidth()
#define ALINE_CENTER(t)                 ((LCDWidth - (u8g2.getUTF8Width(t))) / 2)
#define ALINE_RIGHT(t)                  (LCDWidth - u8g2.getUTF8Width(t))
#define ALINE_LEFT                      0
#define N_DISTINCT_CHARACTERS           62
#define IS_UNICODE(c)                   (((c) & 0xc0) == 0xc0)
#define VERSION				1
//----------------------------------------------------------------------------
typedef struct prGlyph {
  int AsciiCode;
  char* codeGlyph;
  char* isoGlyph;
  char* iniGlyph;
  char* midGlyph;
  char* endGlyph;
};

const prGlyph prForms[] PROGMEM = {
    // Ascii Code, Code, Isolated, Initial, Medial, Final
    {193, "\u0621", "\uFE80", "\uFE80", "\uFE80", "\uFE80" },     //1 HAMZA ء	[*]
    {194, "\u0622", "\uFE81", "\uFE81", "\uFE82", "\uFE82" },     //2 ALEF_MADDA آ [*]
    {195, "\u0623", "\uFE83", "\uFE83", "\uFE84", "\uFE84" },     //3 ALEF_HAMZA_ABOVE أ [*]
    {196, "\u0624", "\uFE85", "\uFE85", "\uFE86", "\uFE86" },     //4 WAW_HAMZA ؤ [*]
    {197, "\u0625", "\uFE87", "\uFE87", "\uFE88", "\uFE88" },     //5 ALEF_HAMZA_BELOW إ [*]
    {198, "\u0626", "\uFE89", "\uFE8B", "\uFE8C", "\uFE8A" },     //6 YEH_HAMZA ئ [*]
    {199, "\u0627", "\uFE8D", "\uFE8D", "\uFE8E", "\uFE8E" },     //7 ALEF ا [*]
    {200, "\u0628", "\uFE8F", "\uFE91", "\uFE92", "\uFE90" },     //8 BEH ب
    {555, "\u0629", "\uFE93", "\uFE93", "\uFE94", "\uFE94" },     //9 TEH_MARBUTA ة [*]
    {202, "\u062A", "\uFE95", "\uFE97", "\uFE98", "\uFE96" }, 	  //10 TEH ت
    {203, "\u062B", "\uFE99", "\uFE9B", "\uFE9C", "\uFE9A" }, 	  //11 THEH ث
    {204, "\u062C", "\uFE9D", "\uFE9F", "\uFEA0", "\uFE9E" }, 	  //12 JEEM ج
    {205, "\u062D", "\uFEA1", "\uFEA3", "\uFEA4", "\uFEA2" }, 	  //13 HAH ح
    {206, "\u062E", "\uFEA5", "\uFEA7", "\uFEA8", "\uFEA6" }, 	  //14 KHAH خ
    {207, "\u062F", "\uFEA9", "\uFEA9", "\uFEAA", "\uFEAA" },     //15 DAL د [*]
    {208, "\u0630", "\uFEAB", "\uFEAB", "\uFEAC", "\uFEAC" },     //16 THAL ذ [*]
    {209, "\u0631", "\uFEAD", "\uFEAD", "\uFEAE", "\uFEAE" },     //17 REH ر [*]
    {210, "\u0632", "\uFEAF", "\uFEAF", "\uFEB0", "\uFEB0" },     //18 ZAIN ز [*]
    {184, "\u0698", "\uFB8A", "\uFB8A", "\uFB8B", "\uFB8B" },     //19 ZHEH ژ [*]
    {211, "\u0633", "\uFEB1", "\uFEB3", "\uFEB4", "\uFEB2" }, 	  //20 SEEN 
    {212, "\u0634", "\uFEB5", "\uFEB7", "\uFEB8", "\uFEB6" }, 	  //21 SHEEN 
    {213, "\u0635", "\uFEB9", "\uFEBB", "\uFEBC", "\uFEBA" }, 	  //22 SAD ص
    {214, "\u0636", "\uFEBD", "\uFEBF", "\uFEC0", "\uFEBE" }, 	  //23 DAD ض
    {215, "\u0637", "\uFEC1", "\uFEC3", "\uFEC4", "\uFEC2" }, 	  //24 TAH ط
    {216, "\u0638", "\uFEC5", "\uFEC7", "\uFEC8", "\uFEC6" }, 	  //25 ZAH ظ
    {217, "\u0639", "\uFEC9", "\uFECB", "\uFECC", "\uFECA" }, 	  //26 AIN ع
    {218, "\u063A", "\uFECD", "\uFECF", "\uFED0", "\uFECE" }, 	  //27 GHAIN غ
    {160, "\u0640", "\u0640", "\u0640", "\u0640", "\u0640" },     //28 TATWEEL ـ
    {161, "\u0641", "\uFED1", "\uFED3", "\uFED4", "\uFED2" }, 	  //29 FEH ف
    {162, "\u0642", "\uFED5", "\uFED7", "\uFED8", "\uFED6" }, 	  //30 QAF ق
    {163, "\u0643", "\uFED9", "\uFEDB", "\uFEDC", "\uFEDA" },     //31 KAF Arabic ك
    {164, "\u0644", "\uFEDD", "\uFEDF", "\uFEE0", "\uFEDE" }, 	  //32 LAM ل
    {165, "\u0645", "\uFEE1", "\uFEE3", "\uFEE4", "\uFEE2" }, 	  //33 MEEM م
    {228, "\u0646", "\uFEE5", "\uFEE7", "\uFEE8", "\uFEE6" }, 	  //34 NOON ن
    {167, "\u0647", "\uFEE9", "\uFEEB", "\uFEEC", "\uFEEA" }, 	  //35 HEH ه
    {168, "\u0648", "\uFEED", "\uFEED", "\uFEEE", "\uFEEE" },     //36 WAW و [*]
    {169, "\u0649", "\uFEEF", "\uFEEF", "\uFEF0", "\uFEF0" },     //37 ALEF_MAKSURA [*]
    {170, "\u064A", "\uFEF1", "\uFEF3", "\uFEF4", "\uFEF2" },  	  //38 YEH Arabic ي 
    {172, "\u06CC", "\uFBFC", "\uFBFE", "\uFBFF", "\uFBFD" }, 	  //39 YEH Farsi ی
    {141, "\u0686", "\uFB7A", "\uFB7C", "\uFB7D", "\uFB7B" }, 	  //40 CHEH چ
    {222, "\u067E", "\uFB56", "\uFB58", "\uFB59", "\uFB57" }, 	  //41 Peh پ
    {144, "\u06AF", "\uFB92", "\uFB94", "\uFB95", "\uFB93" }, 	  //42 Gaf گ
    {201, "\u06A9", "\uFB8E", "\uFB90", "\uFB91", "\uFB8F" },  	  //43 Kaf ک
    {32, "\u0020", "\u0020", "\u0020", "\u0020", "\u0020" },      //44 Space
    {44, "\u060C", "\u060C", "\u060C", "\u060C", "\u060C" },	  //45 Kama
    {20, "\u200C", "\u200C", "\u200C", "\u200C","\u200C" }, 	  //46 half-space
    {58, "\u003A", "\u003A", "\u003A", "\u003A", "\u003A" },      //47 :
    {187, "\u061B", "\u061B", "\u061B", "\u061B", "\u061B" },     //48 ؛
    {46, "\u002E", "\u002E", "\u002E", "\u002E", "\u002E" },      //49 .
    {191, "\u061F", "\u061F", "\u061F", "\u061F", "\u061F" },     //50 ؟
    {48, "\u06F0", "\u06F0", "\u06F0", "\u06F0", "\u06F0" },      //51 0
    {49, "\u06F1", "\u06F1", "\u06F1", "\u06F1", "\u06F1" },      //52 1
    {50, "\u06F2", "\u06F2", "\u06F2", "\u06F2", "\u06F2" },      //53 2
    {51, "\u06F3", "\u06F3", "\u06F3", "\u06F3", "\u06F3" },      //54 3
    {52, "\u06F4", "\u06F4", "\u06F4", "\u06F4", "\u06F4" },      //55 4
    {53, "\u06F5", "\u06F5", "\u06F5", "\u06F5", "\u06F5" },      //56 5
    {54, "\u06F6", "\u06F6", "\u06F6", "\u06F6", "\u06F6" },      //57 6
    {55, "\u06F7", "\u06F7", "\u06F7", "\u06F7", "\u06F7" },      //58 7
    {56, "\u06F8", "\u06F8", "\u06F8", "\u06F8", "\u06F8" },      //59 8
    {57, "\u06F9", "\u06F9", "\u06F9", "\u06F9", "\u06F9" },      //60 9
    {41, "\u0028", "\u0028", "\u0028", "\u0028", "\u0028" },      //61 (  
    {40, "\u0029", "\u0029", "\u0029", "\u0029", "\u0029" }       //62 )
};
//----------------------------------------------------------------------------
bool isFromTheSet1(unsigned char ch){
    const unsigned char theSet1[18] = {
        32, '\0', 199, 194, 207, 208, 209, 210,
        184, 168, 191, 40, 41, 46, 33, 44, 58, 248};
    int i = 0;
    while (i < 18)
    {
        if(ch == theSet1[i])
            return true;        
        ++i;
    }
    return false;
}
//----------------------------------------------------------------------------
bool isFromTheSet2(unsigned char ch){
    const unsigned char theSet1[10] = {
        32, '\0', 191, 40, 41, 46, 33, 44,
        58, 248 };
    int i = 0;
    while (i < 10)
    {
        if(ch == theSet1[i])
            return true;        
        ++i;
    }
    return false;
}
//----------------------------------------------------------------------------
int FindGlyph(unsigned char chFind){
  for (int i = 0; i < N_DISTINCT_CHARACTERS; i++) {
    if (pgm_read_word(&(prForms[i].AsciiCode)) == chFind) {
	return i;
	break;
    }
  }
  return -1;
}
//----------------------------------------------------------------------------
String prReshaper(char *Text){
  
  String prBuffer = "";
  int stat = 0;
  unsigned char pLetter = ' ';    	//Previous word
  unsigned char letter;             	//Letter
  unsigned char nLetter;     	      	//Next word
  unsigned char temp;
  
  while(temp = *Text++){
    //is Number ?
    if (temp >= '0' && temp <= '9') {
      //d = temp - '0';
      letter = temp;
    }
    else if(temp >= 128){ 
      letter = *Text++;
      letter += 32;
      temp += 32;
      if(letter == 207){
        if(temp == 218 || temp == 250){ 
          letter = 144; //گ
        }
      }
      else if(letter == 166)
        { 
        if(temp == 218 || temp == 250){ //چ
          letter = 141;
        }
        else
        { 
          letter = 228; //ن
        }
      }
    } 
    else
    {
      letter = temp;
    }
	//
    if(letter == 172)
    {
      if(temp == 248 || temp == 32)
      {
        letter = 44;
      }
    }        
    temp = *Text++;
    if(temp >= 128)
    {
      nLetter = *Text++; 
      nLetter += 32; 
      temp += 32;
      if(nLetter == 207)
      {
        if(temp == 218 || temp == 250)
        { 
          nLetter = 144; //گ
        }
      }
    else if(nLetter == 166)
    {
      if(temp == 218 || temp == 250)
      { //چ
        nLetter = 141; 
      }
      else
      {
        nLetter = 228; //ن
      }
    }
    *Text--;
    *Text--;
    }
    else
    {
      nLetter = temp; 
      *Text--;
    }
	//
    if(nLetter == 172)
    {
      if(temp == 248 || temp == 32)
      {
        nLetter = 44;
      }
    }      
  int isunk = 0; 
	/*
	Final: at the end of the word.
	Medial: at the middle of the word.
	Initial: at the beginning of the word.
	Isolated: the character alone (not part of a word).
	*/
    if (isFromTheSet1(pLetter))
      if (isFromTheSet2(nLetter))
        stat = 0;  //Isolated
      else
        stat = 1;  //Initial
    else
      if (isFromTheSet2(nLetter))
        stat = 2;  //Final
      else
        stat = 3;  //Medial

	int number = FindGlyph(letter);
  
  #ifdef DEBUG
	  Serial.print("Letter code: ");
	  Serial.println(letter);
	  Serial.print("Number is: ");
	  Serial.println(number);
	  Serial.print("Pos: ");
	  Serial.println(stat);
	  Serial.println("--------------");
  #endif

	switch (stat){
		case 0: //Isolated
      //Serial.println("Isolated");
			prBuffer += (char*)((prForms[number].isoGlyph));		
      //Serial.println("END Isolated");
			break;
		case 1: //Initial
      //Serial.println("Initial");
			prBuffer += (char*)(prForms[number].iniGlyph);
      //Serial.println("END Initial");
			break;
		case 2: //Final
      //Serial.println("Final");
			prBuffer += (char*)(prForms[number].endGlyph);
      //Serial.println("END Final");
			break;
		case 3: //Medial
      //Serial.println("Medial");
			prBuffer += (char*)(prForms[number].midGlyph);
      //Serial.println("END Medial");
			break;		
		default:
			isunk = 1;
			break;	
	}			  
    if(isunk == 0)
      pLetter = letter;  
  }
  //utf8rev(prBuffer.c_str());
  utf8rev((char *)prBuffer.c_str());
  //Serial.println("END");
  return prBuffer; 
}
//----------------------------------------------------------------------------
// https://stackoverflow.com/questions/199260/how-do-i-reverse-a-utf-8-string-in-place
void utf8rev(char *str){
    /* this assumes that str is valid UTF-8 */
    char    *scanl, *scanr, *scanr2, c;
    /* first reverse the string */
    for (scanl = str, scanr = str + strlen(str); scanl < scanr;)
        c = *scanl, *scanl++= *--scanr, *scanr= c;
    /* then scan all bytes and reverse each multibyte character */
    for (scanl = scanr = str; c = *scanr++;) {
        if ( (c & 0x80) == 0) // ASCII char
            scanl = scanr;
        else if ( (c & 0xc0) == 0xc0 ) { // start of multibyte
            scanr2 = scanr;
            switch (scanr - scanl) {
                case 4: c = *scanl, *scanl++= *--scanr, *scanr = c; // fallthrough
                case 3: // fallthrough
                case 2: c = *scanl, *scanl++= *--scanr, *scanr = c;
            }
            scanr = scanl = scanr2;
        }
    }
}
//----------------------------------------------------------------------------
//UTF-8 strlen function
int strlen_utf8(char *s) {
   int i = 0, j = 0;
   while (s[i]) {
     if ((s[i] & 0xc0) != 0x80) j++;
     i++;
   }
   return j;
}
//----------------------------------------------------------------------------
void setup(void) {
  Serial.begin(9600);
  u8g2.begin();
}
//----------------------------------------------------------------------------
void loop(void){
  
  String msg1, msg2;
  char txt[] = "پرشین ری شیپر";
  char num[] = "نسخه 1";
  
  //utf8rev(num);                                           //For correct representation, the numbers must be reversed.
  msg1 = prReshaper(txt);
  msg2 = prReshaper(num);
  //msg2 = String (utf8rev(prReshaper(num).c_str()));       //For correct representation, the numbers must be reversed.
  
  u8g2.enableUTF8Print();
  
  //Set font
  //u8g2.setFont(u8g2_font_samim_12_t_all);                 //Number view in both modes. (English & Persian)
  //u8g2.setFont(u8g2_font_iranian_sans_16_t_all);          //Number view in both modes. (English & Persian)
  //u8g2.setFont(u8g2_font_ganj_nameh_sans16_t_all);        //No number view.
  //u8g2.setFont(u8g2_font_samim_fd_16_t_all);              //By default, this font displays numbers in Persian.
  
  u8g2.firstPage();
  do {
    //u8g2.setFont(u8g2_font_q_pixia);
    u8g2.setFont(idreams_17);
    u8g2.setCursor(ALINE_CENTER(msg1.c_str()), 25);
    u8g2.print(msg1);
    u8g2.setFont(u8g2_font_iranian_sans_10_t_all);
    u8g2.setCursor(ALINE_CENTER(msg2.c_str()), 45);
    u8g2.print(msg2);
  } while(u8g2.nextPage());
  delay(10000);
}