#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "event.h"
#include "baricon.h"
#include "res.h"
#include "resspr.h"
#include "menu.h"
#include "template.h"
#include "dbox.h"
#include "werr.h"
#include "coords.h"
#include "bbc.h"
#include "os.h"
#include "xferrecv.h"
#include "xfersend.h"
#include "saveas.h"
#include "dboxquery.h"

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

/******************************** CONSTANTS ********************************/

#define CharDes_menu_info 1
#define CharDes_menu_save 2
#define CharDes_menu_reset 3
#define CharDes_menu_quit 4

#define Bitmap_menu_reset 1
#define Bitmap_menu_blank 2

#define X_SPC 32
#define Y_SPC 40

#define SPC 20

#define FILETYPE_BBCFONT 0xff7

#define WINDOWS 10

/******************************* GLOBAL DATA *******************************/

typedef struct
{
  unsigned char bitmap[8];
  int window;
} character;

typedef struct
{
  wimp_w handle;
  BOOL used;
} bitmap_window;

menu chardes_menu, bitmap_menu;

wimp_w chardes_window;

char title[256], title_m[258], msg[300];

BOOL chardes_modified, chardes_titled, chardes_window_open = FALSE;

character chr[256];

bitmap_window window[WINDOWS];

/******************************* PROTOTYPES ********************************/

void chardes_event_handler(wimp_eventstr *e, void *handle);

void chardes_menuproc(void *handle, char *hit);

void bitmap_open(int c);

/******************************** FUNCTIONS ********************************/

void chardes_modify(void)
{
  if (chardes_modified)
    return;

  chardes_modified = TRUE;
  sprintf(title_m,"%s *",title);
  win_settitle(chardes_window, title_m);
}

void bitmap_close(int c)
{
wimp_w handle = window[chr[c].window].handle;

  win_register_event_handler(handle, 0, 0);
  event_attachmenu(handle, 0, 0, 0);

  if (wimpt_complain(wimp_close_wind(handle)))
    return;

  window[chr[c].window].used = FALSE;
  chr[c].window = -1;
}

BOOL chardes_save(char *filename, void *handle)
{
int c,j;
FILE *file;

  handle = handle;

  if ((file = fopen(filename,"wb")) == NULL)
    return FALSE;

  for (c = 32; c < 256;c++)
  {
    if (c == 127)
      continue;

    putc(23,file);
    putc(c,file);

    for (j = 0;j < 8;j++)
      putc(chr[c].bitmap[j],file);
  }

  fclose(file);

  if (xfersend_file_is_safe)
  {
    strcpy(title,filename);
    win_settitle(chardes_window,title);

    chardes_titled = TRUE;
    chardes_modified = FALSE;

    sprintf(msg,"SetType %s BBC Font",filename);
    wimpt_complain(os_cli(msg));
  }

  return TRUE;
}

BOOL chardes_save_yn(void)
{
  if (!chardes_modified)
    return TRUE;

  if (chardes_titled)
    sprintf(msg,"Do you wish to save edited file '%s'",title);
  else
    sprintf(msg,"Do you wish to save your edited file");

  switch (dboxquery(msg))
  {
  case dboxquery_YES:
    saveas(FILETYPE_BBCFONT, chardes_titled ? title : "AFont", 2230, chardes_save, 0, 0, 0);
    return TRUE;

  case dboxquery_NO:
    return TRUE;

  default:
    return FALSE;
  }
}

void chardes_reset_char(int c)
{
int i;
unsigned char block[9];

  block[0] = c;
  os_word(10,(void *) block);
  for (i = 0;i < 8;i++)
    chr[c].bitmap[i] = (((c < 32) || (c == 127)) ? 0 : block[i+1]);
}

BOOL chardes_open(void)
{
wimp_wstate state;

  if (chardes_window_open)
  {
  int c;
  wimp_redrawstr r;

    for (c = 0;c < 256;c++)
      if (chr[c].window != -1)
        bitmap_close(c);

    win_settitle(chardes_window,title);

    r.w = chardes_window;
    wimpt_noerr(wimp_getwindowoutline(&r));
    r.w = -1;
    wimp_force_redraw(&r);
  }
  else
  {
    if (wimpt_complain(wimp_get_wind_state(chardes_window, &state)))
      return FALSE;
    state.o.behind = -1;
    if (wimpt_complain(wimp_open_wind(&state.o)))
      return FALSE;

    win_settitle(chardes_window,title);

    win_register_event_handler(chardes_window, chardes_event_handler, 0);
    event_attachmenu(chardes_window, chardes_menu, chardes_menuproc, 0);

    chardes_window_open = TRUE;
  }

  return TRUE;
}

void chardes_reset_font(void)
{
int c;

  for(c = 0;c < 256;c++)
    chardes_reset_char(c);

  strcpy(title, "<untitled>");
  chardes_titled = FALSE;
  chardes_modified = FALSE;
  chardes_open();
}

void chardes_info(void)
{
dbox  d;

  if ((d = dbox_new("ProgInfo")) != NULL)
  {
    dbox_show(d);
    dbox_fillin(d);
    dbox_dispose(&d);
  }
}

void chardes_close(void)
{
int c;

  for (c = 0;c < 256;c++)
    if (chr[c].window != -1)
        bitmap_close(c);

  win_register_event_handler(chardes_window, 0, 0);
  if (!event_attachmenu(chardes_window, 0, 0, 0))
    return;

  if (wimpt_complain(wimp_close_wind(chardes_window)))
    return;

  chardes_window_open = FALSE;
}

void chardes_menuproc(void *handle, char *hit)
{
  handle = handle;

  switch (*hit)
  {
  case CharDes_menu_info:
    chardes_info();
    break;

  case CharDes_menu_save:
    saveas(FILETYPE_BBCFONT, chardes_titled ? title : "AFont", 2230, chardes_save, 0, 0, 0);
    break;

  case CharDes_menu_reset:
    if (chardes_save_yn())
      chardes_reset_font();
    break;

  case CharDes_menu_quit:
    if (chardes_window_open && chardes_modified)
    {
      if (chardes_save_yn())
        exit(0);
    }
    else
      exit(0);
  }
}

int chardes_load(void)
{
int c,j;
char *filename;
FILE *file;

  if (!chardes_save_yn)
    return 5;

  for(c = 0;c < 256;c++)
    for(j = 0;j < 8;j++)
      chr[c].bitmap[j] = 0;

  if (xferrecv_checkinsert(&filename) != FILETYPE_BBCFONT)
    return 4;

  if ((file = fopen(filename,"rb")) == NULL)
    return 1;

  while (TRUE)
  {
    switch (getc(file))
    {
    case '\x17':
      c = getc(file);
      if (c == EOF)
      {
        fclose (file);
        chardes_reset_font();
        return 2;
      }

      if ((c >= 32) && (c < 256) && (c != 127))
        for (j = 0;j < 8;j++)
          chr[c].bitmap[j] = getc(file);
      else
        fseek(file, 8, SEEK_CUR);

      break;

    case EOF:
      fclose(file);
      xferrecv_insertfileok();

      chardes_titled = TRUE;
      chardes_modified = FALSE;

      strcpy(title, filename);
      chardes_open();
      return 0;

    default:
      fclose(file);
      chardes_reset_font();
      return 3;
    }
  }
}

void chardes_redraw_window(wimp_w handle)
{
int more;
wimp_redrawstr r;

coords_cvtstr cvtstr;
coords_pointstr pointstr;

int x,y;
int x0,x1,y0,y1;
unsigned char c,*a;

  r.w = handle;
  wimpt_noerr(wimp_redraw_wind(&r, &more));

  while (more)
  {
    cvtstr.box = r.box;
    cvtstr.scx = r.scx;
    cvtstr.scy = r.scy;
    coords_box_toworkarea(&r.g,&cvtstr);

    y0 = (int) ceil(r.g.y0 / Y_SPC);
    y1 = (int) ceil(r.g.y1 / Y_SPC);

    x0 = (int) floor(r.g.x0 / X_SPC);
    x1 = (int) floor(r.g.x1 / X_SPC + 1);

    pointstr.x=0;
    pointstr.y=0;
    coords_point_toscreen(&pointstr,&cvtstr);

    for(y = y0;y <= y1;y++)
      for(x = x0;x < x1;x++)
      {
        bbc_move(pointstr.x+x*X_SPC+8,pointstr.y+y*Y_SPC-1);
        c=(2-y)*16 + x;
        if((c > 31) & (c < 256) & (c != 127))
        {
          a = chr[c].bitmap;
          bbc_vduq(23,32,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]);
          bbc_vdu(32);
        }
      }

    wimp_get_rectangle(&r, &more);
  }
  bbc_vduq(23,32,0,0,0,0,0,0,0,0);
}

void chardes_redraw_char(int c)
{
wimp_redrawstr r;
int x = (c % 16);
int y = (2 - ((int) (c / 16)));

  r.w = chardes_window;
  r.box.x0 = x * X_SPC;
  r.box.y1 = y * Y_SPC;
  r.box.x1 = r.box.x0 + X_SPC;
  r.box.y0 = r.box.y1 - Y_SPC;
  wimp_force_redraw(&r);
}

void bitmap_event_handler(wimp_eventstr *e, void *cp)
{
int c = ((character *) cp) - chr;

  switch (e->e)
  {
  case wimp_EOPEN:
    wimpt_noerr(wimp_open_wind(&e->data.o));
    break;

  case wimp_ECLOSE:
    bitmap_close(c);
    break;

  case wimp_EBUT:
    if (e->data.but.m.bbits & (wimp_BLEFT | wimp_BRIGHT))
    {
    wimp_i icon = e->data.but.m.i;

      wimp_set_icon_state(window[chr[c].window].handle,icon,wimp_ISELECTED,0);
      chr[c].bitmap[icon / 8] ^= (1 << (7 - (icon % 8)));
      chardes_modify();
      chardes_redraw_char(c);
    }
    break;

  default:
    break;
  }
}

void chardes_update_char(int c)
{
int x,y;
wimp_redrawstr r;
wimp_w handle = window[chr[c].window].handle;

  for (y = 0;y < 8;y++)
    for (x = 0;x < 8;x++)
      wimpt_noerr(wimp_set_icon_state(handle, y*8+7-x, chr[c].bitmap[y] & (1 << x) ? wimp_ISELECTED : 0, wimp_ISELECTED));

  r.w = handle;
  wimpt_noerr(wimp_getwindowoutline(&r));
  wimp_force_redraw(&r);

  chardes_redraw_char(c);
}

void bitmap_menuproc(void *cp, char *hit)
{
int j;
int c = ((character *) cp) - chr;

  switch (*hit)
  {
  case Bitmap_menu_reset:
    chardes_reset_char(c);
    chardes_modify();
    chardes_update_char(c);
    break;

  case Bitmap_menu_blank:
    for (j = 0;j < 8;j++)
      chr[c].bitmap[j] = 0;
    chardes_modify();
    chardes_update_char(c);
  }
}

void bitmap_open(int c)
{
wimp_w handle;
wimp_wstate state;
int x,y,i;

  if ((chr[c].window != -1) || (c == 127))
    return;

  for (i = 0;i < WINDOWS && window[i].used;i++);

  if (i == WINDOWS)
    return;
  else
  {
    chr[c].window = i;
    window[i].used = TRUE;
    handle = window[i].handle;
  }

  sprintf(msg,"Char %d - '%c'",c,c);
  win_settitle(handle, msg);

  if (wimpt_complain(wimp_get_wind_state(handle, &state)))
  {
    wimpt_noerr(wimp_delete_wind(handle));
    return;
  }
  state.o.behind = -1;
  if (wimpt_complain(wimp_open_wind(&state.o)))
    return;

  for (y = 0;y < 8;y++)
    for (x = 0;x < 8;x++)
      wimpt_noerr(wimp_set_icon_state(handle,y*8+7-x,((chr[c].bitmap[y] >> x) & 1) * wimp_ISELECTED,wimp_ISELECTED));

  win_register_event_handler(handle, bitmap_event_handler, (void *) &chr[c]);
  event_attachmenu(handle, bitmap_menu, bitmap_menuproc, (void *) &chr[c]);
}
                                          
void chardes_select(wimp_mousestr *m)
{
coords_cvtstr cvtstr;
coords_pointstr pointstr;
wimp_wstate w;

  if (!(m->bbits & (wimp_BLEFT | wimp_BRIGHT)))
    return;

  wimp_get_wind_state(m->w,&w);

  cvtstr.box = w.o.box;
  cvtstr.scx = w.o.x;
  cvtstr.scy = w.o.y;

  pointstr.x = m->x;
  pointstr.y = m->y;

  coords_point_toworkarea(&pointstr,&cvtstr);

  bitmap_open((2 - pointstr.y / Y_SPC)*16 + pointstr.x / X_SPC);
}

void chardes_event_handler(wimp_eventstr *e, void *handle)
{
  handle = handle;

  switch (e->e)
  {
  case wimp_EREDRAW:
    chardes_redraw_window(chardes_window);
    break;

  case wimp_EOPEN:
    wimpt_noerr(wimp_open_wind(&e->data.o));
    break;

  case wimp_ECLOSE:
    if (chardes_save_yn())
      chardes_close();
    break;

  case wimp_EBUT:
    chardes_select(&e->data.but.m);
    break;

  case wimp_ESEND:
  case wimp_ESENDWANTACK:
    switch (e->data.msg.hdr.action)
    {
    case wimp_MDATALOAD:
      chardes_load();
      break;

    default:
      break;
    }

  default:
    break;
  }
}

void chardes_iconclick(wimp_i icon)
{
  icon = icon;

  if (chardes_window_open)
    return;

  chardes_reset_font();
}

void chardes_icon_load(wimp_eventstr *e, void *handle)
{
  handle = handle;

  switch (e->e)
  {
  case wimp_ESEND:
  case wimp_ESENDWANTACK:
    switch (e->data.msg.hdr.action)
    {
    case wimp_MDATALOAD:
      chardes_load();

    default:
      break;
    }

  default:
    break;
  }
}

BOOL chardes_initialise(void)
{
wimp_wind *wind;
wimp_w handle;
int i;

  wimpt_init("CharDes");
  res_init("CharDes");
  resspr_init();
  template_init();
  dbox_init();

  wind = &(template_copy(template_find("MainWindow"))->window);
  if (wind == 0)
    return FALSE;
  if (wimpt_complain(wimp_create_wind(wind, &chardes_window)))
    return FALSE;

  for (i = 0;i < 256;i++)
    chr[i].window = -1;

  for (i = 0;i < WINDOWS;i++)
  {
    wind = &(template_copy(template_find("Bitmap"))->window);
    if (wind == 0)
      return FALSE;
    if (wimpt_complain(wimp_create_wind(wind, &handle)))
      return FALSE;

    window[i].handle = handle;
    window[i].used = FALSE;
  }

  if ((chardes_menu = menu_new("CharDes", ">Info,>Save,Reset,Quit")) == NULL)
    return FALSE;

  if ((bitmap_menu = menu_new("CharDes", "Reset,Blank")) == NULL)
    return FALSE;

  baricon("!chardes", (int)resspr_area(), chardes_iconclick);
  if (!event_attachmenu(win_ICONBAR, chardes_menu, chardes_menuproc, 0))
    return FALSE;
  win_register_event_handler(win_ICONBARLOAD, chardes_icon_load, 0);

  return TRUE;
}

/****************************** MAIN PROGRAM *******************************/

int main()
{
  if (!chardes_initialise())
    return 1;

  while (TRUE)
    event_process();

  return 0;
}
