#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef _STR_2_UNICODE
#define _STR_2_UNICODE
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define FILE_START        "//1. Automatically generated by u-boot-2019.04/tools/str2unicode, please do not edit.\n\n" \
                          "//2. How to revise osd_string_emnu.h?\n" \
                          "//   Revise osd_string.est first, then using the following cmd to update osd_string_emnu.h off-line:\n" \
                          "//   cd ../../../../u-boot-2019.04/tools;./str2unicode\n\n" \
                          "//3. If you want to make the new osd_string_emnu.h take effect, re-build all uboot again.\n\n" \
                          "#ifndef _OSD_STRING_H_\n" \
                          "#define _OSD_STRING_H_\n\n" \
                          "#define ID_MAGIC 0x554E\n"

#define FILE_END          "\n#endif\n"
#define EMNU_HEADER       "typedef enum{\n   "
#define EMNU_END          "\n} string_id;\n"
#define STRING_START      "//1. Automatically generated by u-boot-2019.04/tools/str2unicode, please do not edit.\n\n"
#define STRING_END        "\n};\n"
#define EST_FILE          "../Mediatek-internal/impl/upgrade/include/osd_string.est"
#define EMNU_FILE         "../Mediatek-internal/impl/upgrade/include/osd_string_emnu.h"
#define STRING_FILE       "../Mediatek-internal/impl/upgrade/include/language.h"
#define ID_MAGIC         0x554E   /* "UN" mean unicode*/
#define LANGUAGE         8
int new_line = 0;
int string_len = 0;
FILE *hex_file = NULL;
FILE *est_file = NULL;
FILE *emnu_file = NULL;

void write_8bit(unsigned char data, FILE* file)
{
    char hex_buffer[10];
    if ((data & 0xff) < 0x10)
    {
        snprintf(hex_buffer, sizeof(hex_buffer), "0x0%x,", data & 0xff);
    }
    else
    {
        snprintf(hex_buffer, sizeof(hex_buffer), "0x%x,", data & 0xff);
    }
    fputs(hex_buffer, file);
    new_line++;
    if (new_line != 0 && new_line % 16 == 0)
    {
        fputs("\n", file);
    }

}

void write_16bit(short data, FILE* file)
{
    write_8bit((data >> 8) & 0xff, file);
    write_8bit(data & 0xff, file);
}

void write_32bit(int data, FILE *file)
{
    write_16bit((data >> 16) & 0xffff, file);
    write_16bit(data & 0xffff, file);
}

void write_header(int startaddr, int strlen, FILE *file)
{
    startaddr += string_len;
    string_len += strlen;
    write_16bit(strlen, file);
    write_32bit(startaddr, file);
}

int write_enum_id(FILE *estfile, FILE *hexfile, int language)
{
    int i, j, ret = 0, id = 0;
    size_t len = 0;
    size_t cnt = 0;
    unsigned char *line = NULL;
    unsigned char *p_line = NULL;
    unsigned char * id_buffer[40];

    fputs(EMNU_HEADER, hexfile);
    while (-1 != (cnt = getline((char**)&line, &len, estfile)))
    {
        p_line = line;
        j = 0;
        while (j++ < cnt)
        {
            if ((*p_line == 0x00) && (*(p_line + 1) == 0x2e) && (*(p_line + 2) == 0x00) && (*(p_line + 3) == (language + 0x30)) && (*(p_line + 4) == 0x00) && (*(p_line + 5) == 0x3d))
            {
                for (i = 0; i < (p_line - line); i++)
                {
                    fputs((char*)&line[i], hexfile);
                }
                snprintf((char*)id_buffer, sizeof(id_buffer), " = 0x%x,\n   ", id << 16 | ID_MAGIC);
                fputs((char*)id_buffer, hexfile);
                id++;
                ret = 1;
                break;
            }
            else
            {
                p_line = p_line + 1;
            }
        }
    }
    fputs(EMNU_END, hexfile);
    rewind(estfile);
    if (line)
    {
        free(line);
    }

    return ret;
}

int parse_estfile(FILE *estfile, FILE *hexfile, int language)
{
    int i = 0, ret = 0;
    int id_num = 0, strcnt = 0;
    size_t len = 0;
    size_t cnt = 0;
    unsigned char *line = NULL;
    unsigned char *p_line = NULL;
    char string_header[200];

    while (-1 != (cnt = getline((char**)&line, &len, estfile)))/*check language and get ID number*/
    {
        p_line = line;
        i = 0;
        while (i++ < cnt)
        {
            if ((*p_line == 0x00) && (*(p_line + 1) == 0x2e) && (*(p_line + 2) == 0x00) && (*(p_line + 3) == (language + 0x30)) && (*(p_line + 4) == 0x00) && (*(p_line + 5) == 0x3d))
            {
                id_num++;
                ret = 1;
                break;
            }
            else
            {
                p_line = p_line + 1;
            }
        }
    }
    if (0 == ret)
    {
        snprintf(string_header, sizeof(string_header), "const unsigned char language%d[]=\n{\n", language);
        fputs(string_header, hexfile);
        fputs(STRING_END, hexfile);
        return 0;
    }
    snprintf(string_header, sizeof(string_header), "const unsigned char language%d[]=\n{\n", language);
    fputs(string_header, hexfile);
    rewind(estfile);

    while (-1 != (cnt = getline((char**)&line, &len, estfile))) /*write header*/
    {
        p_line = line;
        i = 0;
        while (i++ < cnt)
        {
            if ((*p_line == 0x00) && (*(p_line + 1) == 0x2e) && (*(p_line + 2) == 0x00) && (*(p_line + 3) == (language + 0x30)) && (*(p_line + 4) == 0x00) && (*(p_line + 5) == 0x3d))
            {
                strcnt = 0;
                for (i = i + 6; i < cnt; i++)
                {
                    if ((line[i] == 0x00 && line[i + 1] == 0x0a) || (line[i] == 0x0d && line[i + 1] == 0x00 && line[i + 2] == 0x0a))
                    {
                        break;
                    }
                    strcnt++;
                }
                write_header(id_num * 6, strcnt, hexfile);
                ret = 1;
                break;
            }
            else
            {
                p_line = p_line + 1;
            }
        }
    }
    rewind(estfile);

    while (-1 != (cnt = getline((char**)&line, &len, estfile))) /*write string*/
    {
        p_line = line;
        i = 0;
        while (i++ < cnt)
        {
            if ((*p_line == 0x00) && (*(p_line + 1) == 0x2e) && (*(p_line + 2) == 0x00) && (*(p_line + 3) == (language + 0x30)) && (*(p_line + 4) == 0x00) && (*(p_line + 5) == 0x3d))
            {
                for (i = i + 6; i < cnt; i++)
                {
                    if ((line[i] == 0x00 && line[i + 1] == 0x0a) || (line[i] == 0x0d && line[i + 1] == 0x00 && line[i + 2] == 0x0a))
                    {
                        break;
                    }
                    write_8bit(line[i], hexfile);
                }
                ret = 1;
                break;
            }
            else
            {
                p_line = p_line + 1;
            }
        }
    }

    rewind(estfile);
    fputs(STRING_END, hexfile);
    new_line = 0;
    string_len = 0;
    if (line)
    {
        free(line);
    }

    return ret;
}

int main(int argc, char *argv[])
{
    int ret = 0;
    int language = 0;
	int languagenum = 0;

    est_file = fopen(EST_FILE, "r");
    hex_file = fopen(STRING_FILE, "w");
    emnu_file = fopen(EMNU_FILE, "w");
    if (est_file == NULL)
    {
        printf("can't open est_file!\n");
        exit(0);
    }
    if (hex_file == NULL)
    {
        printf("can't open hex_file!\n");
        exit(0);
    }
	if (emnu_file == NULL)
    {
        printf("can't open emnu_file!\n");
        exit(0);
    }
    fputs(FILE_START, emnu_file);
    fputs(STRING_START, hex_file);
    ret = write_enum_id(est_file, emnu_file, 0);
    if (ret == 1)
    {
        printf("create Mediatek-internal/impl/upgrade/include/osd_string_emnu.h success!\n");
    }
    else
    {
        printf("create Mediatek-internal/impl/upgrade/include/osd_string_emnu.h false!\n");
    }

    while (language < LANGUAGE)
    {
        ret = parse_estfile(est_file, hex_file, language);
        if (ret == 1)
        {
            languagenum++;
        }
        language++;
    }
    printf("create Mediatek-internal/impl/upgrade/include/language.h success!\n%d languages are created!\n", languagenum);
    fputs(FILE_END, emnu_file);

    if (est_file)
    {
        fclose(est_file);
    }
    if (emnu_file)
    {
        fclose(emnu_file);
    }
    if (hex_file)
    {
        fclose(hex_file);
    }

    return 0;

}

#endif
