/*
 *
 * UNICON - The Console Chinese & I18N
 * Copyright (c) 1999-2000
 *
 * This file is part of UNICON, a console Chinese & I18N
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * See the file COPYING directory of this archive
 * Author: see CREDITS
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <sys/ioctl.h>
#if (__GNU_LIBRARY__ >= 6)
#include <sys/perm.h>
#else
#include <linux/types.h>
#include <linux/termios.h>
#endif
#include <linux/vt.h>
#include <termios.h>

#include <UniKey.hpp>

CUniKey::CUniKey (char *szDevice, int resize)
{
    fd = open (szDevice, 0);
    if (fd < 0)
    {
        printf ("Can't open %s\n", szDevice);
        exit (-1);
    }
    TestFBExist ();
    GetVideoInfo (&MyVideoInfo);
    ChineseInput_X = 0;
    ChineseInput_Y = MyVideoInfo.pixel_height/MyVideoInfo.font_height - 1;
    SelectionXMax = MyVideoInfo.pixel_width/MyVideoInfo.font_width - 2;
    ResizeTerm (resize);
}

CUniKey::~CUniKey ()
{
    ResizeTerm (1);
    close (fd);
}

int CUniKey::WriteOsKeys (TTY_KEY_T *ui)
{
//    int ret_val = ioctl(fd, UNI_INPUT_SET_INFO, (char *) ui);
    int ret_val;
    ret_val = ioctl(fd, UNI_INPUT_SET_INFO, (char *) ui);
    if (ret_val < 0)
    {
        printf ("error in Writing Key.....\n");
    }
    return ui->nTotal;
}

int CUniKey::ReadOsKeys (TTY_KEY_T *ui)
{
    fd_set readset;
    struct timeval timeout;
    extern int bExitInput;
    do
    {
        FD_ZERO (&readset);
        FD_SET (fd, &readset);
        timeout.tv_sec = 120;
        timeout.tv_usec = 0;
        if (select (fd + 1, &readset, NULL, NULL, &timeout) <= 0)
           continue;
        ui->nTty = GetCurrentTTY ();
//        printf ("UniKey::GetCurrentTty()=%d\n", ui->nTty);
        int ret_val = ioctl(fd, UNI_INPUT_GET_INFO, (TTY_KEY_T *) ui);

        if (ret_val < 0)
        {
            printf ("error in Reading Key.....\n");
            return -1;
        }
//        if (ui->op & FLUSH_INPUTMETHOD) 
//        fprintf (stderr, "UniKey::nTty=%d, ch=%c\n", ui->nTty, ui->buf[0]);
//        fflush (stderr);
        if (ui->nTotal != 0 || 
            ui->op & FLUSH_BOTTOM || 
            ui->op & FLUSH_INPUTMETHOD) 
            break;
    }
    while (bExitInput == 0);
    return ui->nTotal;
}

int CUniKey::WriteKeys (int nTty, char *szKey, int total)
{
    TTY_KEY_T ui;
    ui.nTty = nTty;
    char *p = szKey;
    int n, a = total;
/*
    printf ("Write Key::nTty = %d\n", nTty);
    for (int i = 0; i < total; i++)
        printf ("key=%c, 0x%x,", szKey[i], szKey[i]);
    printf ("\n");
*/
    do
    {
        if (total > (int) sizeof (ui.buf))
            n = sizeof (ui.buf);
        else
            n = total;
        ui.nTotal = n;
        memcpy (ui.buf, p, n);
        WriteOsKeys (&ui);
        total -= n;
        p += n;
    }
    while (sizeof (ui.buf) == n);
    return a;
}

int CUniKey::ReadKeys (TTY_KEY_T *ui)
{
    return ReadOsKeys (ui);
}

/* System Dependent Input Display */
void CUniKey::InputAreaSPut 
              (int x, int y, u_char ch, VColor fg, VColor bg)
{
    AsciiPut_T a;
    a.x = x;
    a.y = y;
    a.cl = (long) fg;
    a.ch = ch;
    int ret_val = ioctl(fd, UNI_INPUT_PUT_ASCII, &a);
    if (ret_val < 0) {
       printf ("get info failed. in fb_input_sput ()\n");
       exit(-1);
    }
}

void CUniKey::InputAreaWPut 
            (int x, int y, u_char ch1, u_char ch2, VColor fg, VColor bg)
{
    ChinesePut_T a;
    a.x = x;
    a.y = y;
    a.cl = (long) fg;
    a.c1 = ch2;
    a.c2 = ch1;
    int ret_val = ioctl (fd, UNI_INPUT_PUT_CHINESE, &a);
    if (ret_val < 0) {
       printf ("get info failed in InputAreaWPut ().\n");
       exit(-1);
    }
}

void CUniKey::InputAreaClear 
           (int x1, int y1, int x2, int y2, VColor color)
{
    int ret_val; //  = ioctl (fd, UNI_INPUT_CLS_BOTTOM, &color);
    ret_val = ioctl (fd, UNI_INPUT_CLS_BOTTOM, &color);
    if (ret_val < 0) {
       printf ("get info failed.in fb_clear_input ()\n");
       exit(-1);
    }
}

void CUniKey::GetVideoInfo (MyVideoInfo_T *p)
{
    VtInfo_T VtInfo;
    int ret_val; // = ioctl(fd, UNI_INPUT_GET_VT_INFO, &VtInfo);
    ret_val = ioctl(fd, UNI_INPUT_GET_VT_INFO, &VtInfo);
    if (ret_val < 0) {
       printf ("get info failed in fb_init ().\n");
       exit(-1);
    }
    p->vt_has_resized = VtInfo.vt_has_resized;
    p->pixel_width = VtInfo.width;
    p->pixel_height = VtInfo.height;

#ifdef __CLIB24_SUPPORT__
    p->font_height = 24;
    p->font_width = 12;
#else
    p->font_height = 16;
    p->font_width = 8;
#endif
}

void CUniKey::SetVtChangeSize ()
{
    int ret_val; // = ioctl(fd, UNI_INPUT_SET_RESIZE_FLAG, (char *) NULL);
    ret_val = ioctl(fd, UNI_INPUT_SET_RESIZE_FLAG, (char *) NULL);
    if (ret_val < 0) {
        printf ("set resized flag failed.\n");
        exit(-1);
    }
}

void CUniKey::SetVtRestoreSize ()
{
    int ret_val; // = ioctl(fd, UNI_INPUT_SET_UNRESIZE_FLAG, (char *) NULL);
    ret_val = ioctl(fd, UNI_INPUT_SET_UNRESIZE_FLAG, (char *) NULL);
    if (ret_val < 0) {
        printf ("set resized flag failed.\n");
        exit(-1);
    }
}

int CUniKey::GetVideoPixelWidth ()
{
    return MyVideoInfo.pixel_width;
}

int CUniKey::GetVideoPixelHeight ()
{
    return MyVideoInfo.pixel_height;
}

int CUniKey::GetFontWidth ()
{
    return MyVideoInfo.font_height;
}

int CUniKey::GetFontHeight ()
{
    return MyVideoInfo.font_width;
}

void CUniKey::InputAreaOutput (int x, char *Str, VColor fg, VColor bg)
{
  u_char *string = (u_char *) Str;
  if(!string) return;
  while (*string)
  {
     if (*string >= 0x80)  // a valid Chinese Char
     {
         InputAreaWPut (x, ChineseInput_Y, *(string+1), *string, fg, bg);
         x += 2;
         string += 2;
     }
     else
         InputAreaSPut (x++, ChineseInput_Y, *string++, fg, bg);
  }
}

int CUniKey::TestFBExist ()
{
    char *fbdev = "/dev/fb0";
    int fd;
    fd = open(fbdev, O_RDWR);
    if(fd < 0) {
          printf ("\n");
          printf("\t\t        !!! Notice !!!\n");
          printf ("\t\tThis programe must run under Frame Buffer\n");
          printf ("\t\tplease reboot your system and enter framebuffer mode\n");
          printf ("\t\t,then try again.\n");
          exit (0);
    }
    return 0;
}

void CUniKey::ResizeTerm (int nRow)
{
    int tty, fd;
    struct vt_sizes vtsizes;
    struct winsize winsize;
    char dev[12];

    tty = 1;
    sprintf(dev, "/dev/tty%d", tty);
    fd = open(dev, O_RDWR);

    if(ioctl(fd, TIOCGWINSZ, &winsize)) {
        perror("TIOCGWINSZ");
        exit(1);
    }

    vtsizes.v_rows = winsize.ws_row + nRow;
    vtsizes.v_cols = winsize.ws_col;
    vtsizes.v_scrollsize = 0;

    if(ioctl(fd, VT_RESIZE, &vtsizes)) {
        perror("VT_RESIZE");
        exit(1);
    }
    close (fd);

    if (nRow == -1)
        SetVtChangeSize ();
    else
        SetVtRestoreSize ();
}

//only get the current tty no.
int CUniKey::GetCurrentTTY ()
{
   int fd;
   struct vt_stat vs;

   fd = open("/dev/tty0", O_RDONLY);
   if(ioctl(fd, VT_GETSTATE, &vs) == -1){
      printf("Error get status.\n");
      exit(1);
   }
   close(fd);
   return vs.v_active - 1;
}

void CUniKey::SetCurrentFont (int tty, int font_type)
{
    VtFont_T aa;
    aa.tty = tty;
    aa.font_type = font_type;
    aa.input_method_notify = 0;
    int ret_val = ioctl(fd, UNI_SET_CURRENT_FONT, &aa);
    if (ret_val < 0) {
        printf ("Set Current Font failed.\n");
        exit(-1);
    }
}

