/*****************************************************************************
 *			University of Toronto
 *			      ECE 385
 * (C) Copyright 2000           Taneem Ahmed <taneem@eyetap.org>         
 *****************************************************************************
 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *****************************************************************************/

#include <stdlib.h>
#include <curses.h>
#include <panel.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "ibuscurses.h"

static int mx,my;
static base_info bases[] = {
/*
     name      base cur_len  max_len    x      value   */
 { "Hex\0",	16,	0,	2,	0,	"0" },
 { "Dec\0",	10,	0,	3,	0,	"0" },
 { "Oct\0",	8,	0,	3,	0,	"0" },
 { "Bin\0",	2,	0,	8,	0,	"0" }
};
static char ibus_dev[64]="/dev/ibus1";
int fd;

int main(int argc, char **argv){

  int resp;
  int done=0,i;
  int cur_base=0;
  WINDOW *m;
  base_info * b_pt;

  fd = open(ibus_dev,O_RDWR);
  
  m = start_ncurses();
  getmaxyx(m,my,mx);
  my = my / 2;
  mx = mx / 2;
  draw_menu(m);

  do{
    resp = getch();
    switch (resp){
      case '\n':
        broombroom(m, cur_base);
        break;
      case KEY_BACKSPACE:
        b_pt = &bases[cur_base];
        if (b_pt->x == 0) break;
        for(i=b_pt->x;i<=b_pt->cur_len;i++)
          b_pt->value[i-1] = b_pt->value[i];
        b_pt->cur_len--;
        b_pt->x--;
        mvprintw(MENU_TOP_Y+cur_base,MENU_TOP_X,"%s : %s          ",b_pt->base_name,b_pt->value);
        move(MENU_TOP_Y+cur_base,MENU_TOP_X+MENU_X_OFFSET+b_pt->x);
        break;
      case KEY_LEFT:
        b_pt = &bases[cur_base];
        if (b_pt->x == 0) break;
        b_pt->x--;
        move(MENU_TOP_Y+cur_base,MENU_TOP_X+MENU_X_OFFSET+b_pt->x);
        break;
      case KEY_RIGHT:
        b_pt = &bases[cur_base];
        if (b_pt->x == b_pt->cur_len) break;
        b_pt->x++;
        move(MENU_TOP_Y+cur_base,MENU_TOP_X+MENU_X_OFFSET+b_pt->x);
        break;
      case KEY_DOWN:
        if (cur_base == LAST_BASE) break;
        cur_base++;
        move(MENU_TOP_Y+cur_base,MENU_TOP_X+MENU_X_OFFSET+bases[cur_base].cur_len);
        break;

      case KEY_UP:
        if (cur_base == FIRST_BASE) continue;
        cur_base--;
        move(MENU_TOP_Y+cur_base,MENU_TOP_X+MENU_X_OFFSET+bases[cur_base].cur_len);
        break;

      case '?':
      case KEY_F(1): 
        help_menu(m); 
        break;
      
      case 'q':
      case 'Q':
        done = 1;
        break;

      default:
        if (cur_base == BIN_BASE){
          if (resp != '0' && resp!= '1') break;
        }
        else if (cur_base == OCT_BASE){
          if (resp < '0' || resp > '7') break;
        }
        else if (cur_base == DEC_BASE){
          if (resp < '0' || resp > '9') break;
        }
        else if (cur_base == HEX_BASE){
          if (!((resp >= '0' && resp <= '9') ||
              (resp >= 'A' && resp <= 'F') ||
              (resp >= 'a' && resp <= 'f'))) break;
        }
        b_pt = &bases[cur_base];
        if (b_pt->x >= b_pt->max_len) break;
        b_pt->value[b_pt->x++] = resp;

        if ((b_pt->cur_len + 1) == b_pt->x)
          b_pt->cur_len++;
          b_pt->value[b_pt->cur_len] = '\0';
        echochar(resp);
    }
  }while(!done);
  stop_ncurses(m);
  close(fd);
  return 0;
}

WINDOW *start_ncurses(void){
  WINDOW *temp;
  temp = initscr();
  cbreak();
  noecho();
  keypad(temp,TRUE);
  return temp;
}

int draw_menu(WINDOW *w){
  int i;

  clear();
  border(0,0,0,0,0,0,0,0);
  mvprintw(3,mx-11,"University of Toronto");
  mvprintw(4,mx-5,"ECE 385");
  mvprintw(5,mx-8,"Lab 2: Pushbroom");
  mvprintw(2*my-3,10,"Press '?' or <F1> for help.");
  mvprintw(2*my-2,10,"Press 'q' to quit.");
  for(i=0;i<NUMBER_OF_BASE;i++)
    mvprintw(MENU_TOP_Y+i,MENU_TOP_X,"%s : %s",bases[i].base_name,bases[i].value);
  move(MENU_TOP_Y,MENU_TOP_X+MENU_X_OFFSET);

  return 0;
}

int broombroom(WINDOW *w, int cur_base){
  int v,i,x;
  base_info *b_pt;

  b_pt = &bases[cur_base];

  v = (int) strtol(b_pt->value,NULL,b_pt->base);
  if(v>255 || v <0) {
    switch(cur_base){
      case DEC_BASE:
        print_err(w,"Dec value range is 0 to 255");
        break; 
      case OCT_BASE:
        print_err(w,"Oct value range is 0 to 377");
        break; 
    }
    return -1;
  }
  sprintf(bases[0].value,"%x",v);
  bases[0].x = bases[0].cur_len = strlen(bases[0].value);
  sprintf(bases[1].value,"%d",v);
  bases[1].x = bases[1].cur_len = strlen(bases[1].value);
  sprintf(bases[2].value,"%o",v);
  bases[2].x = bases[2].cur_len = strlen(bases[2].value);
  x = v;
  for(i=7;i>=0;i--){
    bases[3].value[i] = (x & 1) ? '1' : '0';
    x = x >> 1;
  }
  bases[3].value[8] = '\0';
  bases[3].x = bases[3].cur_len = strlen(bases[3].value);
  draw_menu(w);
  move(MENU_TOP_Y+cur_base,MENU_TOP_X+MENU_X_OFFSET+b_pt->cur_len);
  if (fd < 0) {
    fd = open(ibus_dev,O_RDWR);
    if (fd < 0) {
      print_err(w,"ibus: module not loaded, see HELP.");
      return -1;
    }
  }
  write(fd,&v,1);
  return 0;
}

int print_err(WINDOW *w, const char *msg){
   int x,y;

   getyx(w,y,x);
   draw_menu(w);
   attrset(A_BOLD);
   mvprintw(19,10,"ERROR: %s",msg);
   attrset(A_NORMAL);
   move(y,x);
   return 0;
}

int help_menu(WINDOW *w){

  WINDOW *n;
  int ny,nx;

  n = newwin(2*LINES/3,2*COLS/3,LINES/4,COLS/6);
  getmaxyx(n,ny,nx);
  wborder(n,0,0,0,0,0,0,0,0);
  mvwprintw(n,1,1,"HELP MENU");
  mvwprintw(n,2,1,"---------");
  mvwprintw(n,3,1,"Before you can use this program, you have to insmod");
  mvwprintw(n,4,1,"the ibus module. It is better to use the");
  mvwprintw(n,5,1,"load_ibus.sh script to load the module.");
  mvwprintw(n,7,1,"If your pushbroom does not work then make sure you");
  mvwprintw(n,8,1,"are using the right base address when loading the");
  mvwprintw(n,9,1,"module. Also make sure the device node name created");
  mvwprintw(n,10,1,"by the script is the same used by this program.");
  mvwprintw(n,11,1,"Do a little hacking, it will work :)");
  mvwprintw(n,ny-2,1,"Press 'm' to return to main window.");
  wrefresh(n);

  while( getch() != 'm');

  delwin(n);
  draw_menu(w);
  return 0;
}

void stop_ncurses(WINDOW *w){

  clear();
  refresh();
  endwin();
}
