/* DBF Editor for SEAL */
/* Copyright (C) by Bad Sector, 2001 */

#include <stdio.h>
#include "allegro.h"
#include "seal.h"
#include "app.h"
#include "dialogs.h"
#include "button.h"
#include "iodlg.h"

typedef struct t_dbf_header
{
  char version;
  char updated_date[3];
  unsigned long records;
  unsigned short headsize;
  unsigned short recordsize;
  short reserved;
  char DB4_flag;
  char encrypted;
  char DB4_use[12];
  char production_index;
  char DB4_language_driver;
  short reserved2;
  /* fields follows */
} t_dbf_header;

typedef struct t_dbf_field
{
  char name[11];
  char type; /* C=Character (string), L=Logical, N=Numeric */
  unsigned long unknown; /* really don't know what that field does! */
  unsigned char length;
  unsigned char decimal_places;
  short reserved;
  char DB4_work_area_ID;
  char reserved2[10];
  char part_of_production_index;
} t_dbf_field;

t_dbf_header head;
t_dbf_field field[1000];
int fields;

void initialize_engine()
{
  fields = 0;
}

int dbf_read_head(char *filename)
{
  FILE *f = fopen(filename, "rb");
  if (!f) return 0;
  fread(&head, sizeof(t_dbf_header), 1, f);
  fields = (head.headsize-32) / 32;
  fclose(f);
  return 1;
}

void dbf_read_fields(char *filename)
{
  FILE *f = fopen(filename, "rb");
  int i;

  if (!f) return;
  fseek(f, sizeof(t_dbf_header), SEEK_SET);

  for (i=0;i<fields;i++)
    fread(&field[i], sizeof(t_dbf_field), 1, f);

  fclose(f);
}

char *dbf_get_data(char *filename, int record, int fld)
{
  FILE *f = fopen(filename, "rb");
  char buffer[256];
  char *result = 0;
  int i, pos;

  if (!f) return result;

  for (i=0;i<256;i++) buffer[i]=0;

  pos = sizeof(t_dbf_header)+(fields*sizeof(t_dbf_field))+(head.recordsize*record)+2;
  for (i=0;i<fld;i++) pos += field[i].length;
  fseek(f, pos, SEEK_SET);
  fread(&buffer, field[fld].length, 1, f);

  result = (char *) malloc(field[fld].length);

  for (i=0;i<field[fld].length;i++)
    result[i] = buffer[i];
  result[field[fld].length] = 0;

  /* remove unneeded spaces from the end of the data */
  if (result[strlen(result)-1] == 32) do {
    result[strlen(result)-1] = 0;
  } while (result[strlen(result)-1] == 32 && strcmp(result, "") != 0);

  fclose(f);
  return result;
};

void dbf_set_data(char *filename, int record, int fld, char *data)
{
  FILE *f = fopen(filename, "rb+");
  char buffer[256];
  int i, pos;

  if (!f) return;

  for (i=0;i<256;i++) buffer[i]=32;
  for (i=0;i<strlen(data);i++)
    buffer[i] = data[i];

  pos = sizeof(t_dbf_header)+(fields*sizeof(t_dbf_field))+(head.recordsize*record)+2;
  for (i=0;i<fld;i++) pos += field[i].length;
  fseek(f, pos, SEEK_SET);

  fwrite(&buffer, field[fld].length, 1, f);

  fclose(f);
};

int open_dbf_file(char *filename)
{
  if (!dbf_read_head(filename)) return 0;
  dbf_read_fields(filename);
  return 1;
}

#define MSG_CREATE 10001
#define MSG_OPEN 10002
#define MSG_SAVE 10003
#define MSG_SAVEAS 10004
#define MSG_FIELDS 10005
#define MSG_EDIT 10006
#define MSG_ABOUT 10007

p_appwin mainwin;
p_appwin fieldbox;

char *filename;

l_int fieldbox_visible;

/* main controls */

p_button btCreate;
p_button btOpen;
p_button btSave;
p_button btSaveAs;
p_button btFields;
p_button btEdit;
p_button btAbout;
p_button btExit;

p_stattext status;

/* fieldbox controls */

p_listbox fb_fields;
p_textline fb_name;
p_textline fb_length;

static void fieldbox_handle_event(p_object o, p_event event)
{
  if (event->type == EV_MESSAGE) {
    switch (event->message) {
      case MSG_CLOSE: {
        fieldbox_visible = 0;
/*        OBJECT(fb_fields)->done(OBJECT(fb_fields));
        OBJECT(fb_name)->done(OBJECT(fb_name));*/
      } break;
    };
  };
}

p_list get_field_list()
{
  p_list p = list_init(malloc(sizeof(t_list)), &free_listbox_item, 0);
  l_int i;

  for (i=0;i<fields;i++)
    p->insert(p, new_listbox_item(strdup(field[i].name), NULL, TRUE, LIF_NONE));

  return p;
}

void open_fieldbox()
{
  t_rect r = rect_assign(200, 321, 500, 480);
  fieldbox = appwin_init(malloc(sizeof(t_appwin)), r, "Field editor", WF_MINIMIZE, 0, &fieldbox_handle_event);
  OBJECT(desktop)->insert(OBJECT(desktop), OBJECT(fieldbox));
  fieldbox_visible = 1;

  r = rect_assign(10, 30, 150, 140);
  fb_fields = listbox_init(malloc(sizeof(t_listbox)), r, get_field_list(), 1, LF_NONE);
  OBJECT(fieldbox)->insert(OBJECT(fieldbox), OBJECT(fb_fields));

  r = rect_assign(170, 30, 290, 50);
  fb_name = textline_init(malloc(sizeof(t_textline)), r, 11, 0);
  OBJECT(fieldbox)->insert(OBJECT(fieldbox), OBJECT(fb_name));
}

static void handle_event(p_object o, p_event event)
{
  if (event->type == EV_MESSAGE) {
    switch (event->message) {
      case MSG_CREATE: {
      } break;
      case MSG_OPEN: {
        l_text file;
        p_list filetypes = list_init(malloc(sizeof(t_list)), &free_filehistory_item, 0);
        filetypes->insert(filetypes, new_filehistory_item("dBase III+ files (*.dbf)", "*.dbf"));
        file = open_dialog("./", "*.dbf", filetypes);
        if (file) {
          if (open_dbf_file(file)) {
            if (filename) free(filename);
            filename = (char *) malloc(strlen(file)+1);
            strcpy(filename, file);
            strcpy(status->text, set_format_text(NULL, "Status: %s - %i records - %i fields", filename, head.records, fields));
            VIEW(status)->draw(VIEW(status));
            if (fieldbox_visible) {
              fb_fields->list->done(fb_fields->list);
              fb_fields->list = get_field_list();
              VIEW(fb_fields)->draw(VIEW(fb_fields));
            };
          } else {
            msgbox(MW_ERROR, MB_OK, "Cannot open `%s'", file);
          };
          free(file);
        };
        clear_event(event);
      } break;
      case MSG_FIELDS: {
        clear_event(event);
        if (!fieldbox_visible) open_fieldbox();
      } break;
    };
  };
}

void app_init()
{
  t_rect r = rect_assign(200, 200, 500, 320);

  filename = NULL;

  /* initialize edited window */
  mainwin = appwin_init(malloc(sizeof(t_appwin)), r, "DBF Editor 1.0", 0, ap_id, &handle_event);
  OBJECT(desktop)->insert(OBJECT(desktop), OBJECT(mainwin));

  r = rect_assign(5, 25, 79, 62);
  btCreate = button_init(malloc(sizeof(t_button)), r, "Create", MSG_CREATE, BF_NORMAL);
  OBJECT(mainwin)->insert(OBJECT(mainwin), OBJECT(btCreate));
  r = rect_assign(5, 63, 79, 100);
  btOpen = button_init(malloc(sizeof(t_button)), r, "Open", MSG_OPEN, BF_NORMAL);
  OBJECT(mainwin)->insert(OBJECT(mainwin), OBJECT(btOpen));
  r = rect_assign(80, 25, 154, 62);
  btSave = button_init(malloc(sizeof(t_button)), r, "Save", MSG_SAVE, BF_NORMAL);
  OBJECT(mainwin)->insert(OBJECT(mainwin), OBJECT(btSave));
  r = rect_assign(80, 63, 154, 100);
  btSaveAs = button_init(malloc(sizeof(t_button)), r, "SaveAs", MSG_SAVEAS, BF_NORMAL);
  OBJECT(mainwin)->insert(OBJECT(mainwin), OBJECT(btSaveAs));
  r = rect_assign(155, 25, 229, 62);
  btFields = button_init(malloc(sizeof(t_button)), r, "Fields", MSG_FIELDS, BF_NORMAL);
  OBJECT(mainwin)->insert(OBJECT(mainwin), OBJECT(btFields));
  r = rect_assign(155, 63, 229, 100);
  btEdit = button_init(malloc(sizeof(t_button)), r, "Edit", MSG_EDIT, BF_NORMAL);
  OBJECT(mainwin)->insert(OBJECT(mainwin), OBJECT(btEdit));

  r = rect_assign(5, 102, 305, 122);
  status = stattext_init(malloc(sizeof(t_stattext)), r, TX_ALIGN_LEFT, "Status: No file opened");
  OBJECT(mainwin)->insert(OBJECT(mainwin), OBJECT(status));

  fieldbox_visible = 0;
}

app_begin(void)
{
  if (ap_process == AP_INIT) {
    app_init();
  } else
  if (ap_process == AP_DONE) {
    if (fieldbox_visible) {
      dispose(OBJECT(fieldbox));
    };
  };
} app_end;

