// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: testprog.cpp 
// C++ Compiler Used: MSVC 4.2, HPUX CPP 1024
// Produced By: Doug Gaer 
// File Creation Date: 12/16/1997 
// Date Last Modified: 10/15/1998
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This is a a test program for the (V)ariable (B)lock (D)atabase 
file manger class used to read and write low level c strings to
a VBD file. This example is designed to work with the wxWindows 
GUI library.
*/
// ----------------------------------------------------------- //
#include <string.h>
#include "vbdfile.hpp"

#ifdef __GNUG__
#pragma implementation
#pragma interface
#endif

// For compilers that support precompilation, includes "wx.h".
#include "wx_prec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx.h"
#endif

// Define new frame types
class MyTextWindow;

// Database menu functions
void AddString(MyTextWindow &textWin);
void Build(MyTextWindow &textWin);
void Delete(MyTextWindow &textWin);
void Remove(MyTextWindow &textWin);

// Find menu functions
void FindString(MyTextWindow &textWin);

// Display menu functions
void ListInOrder(MyTextWindow &textWin);
void ListNextOrder(MyTextWindow &textWin);
void DumpInOrder(MyTextWindow &textWin);

// View menu functions
void Clear(MyTextWindow &textWin);

// Function prototypes for non-menu functions
void DBInit(MyTextWindow &textWin); 
FAU AddStringObject(const VBDFilePtr &f, const char *str);
char *ReadStringObject(const VBDFilePtr &f, FAU addr);
FAU Find(const VBDFilePtr &f, const char *str);

// Control key macro
#define CONTROL(c) ((c) & 037)

// General string utility functions
char *StringCat(const char *s1=" ", const char *s2= " ", const char *s3=" ");
char *StringCat(char *s1=" ", char *s2= " ", char *s3=" ");

// (V)ariable (B)lock (D)atabse components
VBDFilePtr f(new VBDFile);  
const char *FName = "strings.vbd";

// Define a new text subwindow that can respond to drag-and-drop
class MyTextWindow: public wxTextWindow
{
public:
  MyTextWindow(wxFrame *frame, int x=-1, int y=-1, int width=-1, int height=-1,
               long style=0):
    wxTextWindow(frame, x, y, width, height, style) { DragAcceptFiles(TRUE); }

public:
  void OnDropFiles(int n, char *files[], int x, int y) { LoadFile(files[0]); }
  void OnChar(wxKeyEvent& event);
};

// Define a new application type
class MyApp: public wxApp
{
public:
  wxFrame *OnInit();
};

// Define a new frame type
class MyFrame: public wxFrame
{
public:
  MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h);

public:
  void OnMenuCommand(int id);
  Bool OnClose();

public:
  MyTextWindow *textWin;  // Text window for main frame
};

// Declare frame and menu objects
MyFrame *frame = 0;
wxMenuBar *menu_bar = 0;

// Identification for the all main menu functions
enum {
  // File menu constants
  FILE_QUIT = 1,

  // Edit menu constants
  EDIT_CUT,   
  EDIT_COPY,  
  EDIT_PASTE, 

  // Database menu constants
  DB_ADD,
  DB_BUILD,
  DB_DELETE,
  DB_REMOVE,

  // Find menu constants
  FIND_BYNAME,
  
  // Display menu constants
  DISPLAY_INORDER,
  DISPLAY_DUMP,
  DISPLAY_INNEXTORDER,
  
  // View menu constants
  VIEW_CLEAR,
  
  // Help menu constants
  HELP_ABOUT
};

// Program name displayed in the frame 
const char *ProgName = "Variable Block Database strings program";

// Version number for this windows program 
const char *VerNumber = "1027.101";

// This statement initializes the whole application and calls OnInit
MyApp myApp;

// A macro needed for some compilers (AIX) that need 'main' to be defined
// in the application itself.
IMPLEMENT_WXWIN_MAIN

// `Main program' equivalent, creating windows and returning main app frame
wxFrame *MyApp::OnInit(void)
{
  // Create the main frame window
  frame = new MyFrame(NULL, (char *)ProgName, 50, 50, 400, 300);

  // Give this frame a status line
  frame->CreateStatusLine(1);
  
  // Give it an icon
#ifdef wx_msw
  frame->SetIcon(new wxIcon("graph03"));
#endif
#ifdef wx_x
  frame->SetIcon(new wxIcon("aiai.xbm"));
#endif

  // File menu 
  wxMenu *file_menu = new wxMenu;
  file_menu->Append(FILE_QUIT, "E&xit"," Exit this program");

  // Edit menu
  wxMenu *edit_menu = new wxMenu;
  edit_menu->Append(EDIT_CUT, "Cut\tCtrl+X", "Cut text and copy to clipboard");
  edit_menu->Append(EDIT_COPY, "Copy\tCtrl+C", "Copy text to clipboard");
  edit_menu->Append(EDIT_PASTE, "Paste\tCtrl+V", "Paste text from clipboard");
  
  // Database menu
  wxMenu *db_menu = new wxMenu;
  db_menu->Append(DB_ADD, "&Add", "Add an item to the database");
  db_menu->Append(DB_BUILD, "&Build", "Build a string database");
  db_menu->Append(DB_DELETE, "&Delete", "Delete an item from the database");
  db_menu->Append(DB_REMOVE, "&Remove", "Remove an item from the database");

  // Find menu
  wxMenu *find_menu = new wxMenu;
  find_menu->Append(FIND_BYNAME, "Find by Name", "Find item by name");

  // Display menu
  wxMenu *display_menu = new wxMenu;
  display_menu->Append(DISPLAY_INORDER, "Inorder",
		       "Display objects inorder");
  display_menu->Append(DISPLAY_DUMP, "Dump", "Dump the entire database");
  display_menu->Append(DISPLAY_INNEXTORDER, "Next Order",
		       "Display objects in next order");
  
  // View menu
  wxMenu *view_menu = new wxMenu;
  view_menu->Append(VIEW_CLEAR, "&Clear Window", "Clear this text window");
  
  // Help menu
  wxMenu *help_menu = new wxMenu;
  help_menu->Append(HELP_ABOUT, "&About", "Info about this program");
  
  // Make all the menu bars  
  menu_bar = new wxMenuBar;
  menu_bar->Append(file_menu, "&File");
  menu_bar->Append(edit_menu, "&Edit");
  menu_bar->Append(db_menu, "Database");
  menu_bar->Append(find_menu, "Find");
  menu_bar->Append(display_menu, "Display");
  menu_bar->Append(view_menu, "&View");
  menu_bar->Append(help_menu, "&Help");
  frame->SetMenuBar(menu_bar);

  // Associate the menu bar with the frame
  frame->SetMenuBar(menu_bar);

  // Create a text window in the main frame
  frame->textWin = new MyTextWindow(frame, 0, 250, 400, 250,
				    wxNATIVE_IMPL);
  frame->textWin->DragAcceptFiles(TRUE);

  // Show the frames
  frame->Show(TRUE);
  frame->SetStatusText((char *)ProgName);
  
  DBInit(*frame->textWin); // Open the VBD file
  
  // Return the main frame window
  return frame;
}

MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h):
  wxFrame(frame, title, x, y, w, h)
{
  textWin = 0;
}

// Define the behaviour for the frame closing
Bool MyFrame::OnClose(void)
{
  if(textWin) delete textWin;
  Show(FALSE);
  return TRUE;
}

void MyFrame::OnMenuCommand(int id)
// Intercept menu commands
{
  switch (id) {
    case FILE_QUIT:
      if (OnClose()) delete this;
      break;

    case EDIT_CUT:
      textWin->Cut();
      break;

    case EDIT_COPY:
      textWin->Copy();
      break;
      
    case EDIT_PASTE:
      textWin->Paste();
      break;

    case DB_ADD:
      AddString(*textWin);
      break;
      
    case DB_BUILD:
      Build(*textWin);
      break;
      
    case DB_DELETE:
      Delete(*textWin);
      break;

      
    case DB_REMOVE:
      Remove(*textWin);
      break;

    case FIND_BYNAME:
      FindString(*textWin);
      break;
      

    case DISPLAY_INORDER:
      ListInOrder(*textWin);
      break;
      
    case DISPLAY_DUMP:
      DumpInOrder(*textWin);
      break;
      
    case DISPLAY_INNEXTORDER:
      ListNextOrder(*textWin);
      break;

    case VIEW_CLEAR:
      Clear(*textWin);
      break;

    case HELP_ABOUT:
      char *mesg = StringCat(ProgName, "\nVersion Number ", VerNumber);
      wxMessageBox(mesg, "About testprog", wxOK|wxCENTRE);
      break;
  }
}

void MyTextWindow::OnChar(wxKeyEvent& event)
{
  if(event.keyCode == CONTROL('x')) {
    Cut(); 
    frame->SetStatusText("Cut text and copied to clipboard");
  }

  if(event.keyCode == CONTROL('c')) {
    Copy(); 
    frame->SetStatusText("Copied text from clipboard");
  }

  if(event.keyCode == CONTROL('v')) {
    Paste(); 
    frame->SetStatusText("Pasted text from clipboard");
  }

  wxTextWindow::OnChar(event);  // Process the default behavior
}

void DBInit(MyTextWindow& textWin)
{
  textWin.Clear();
  int exists;
  
  if(!VBDFile::Exists(FName)) {
    textWin << "Creating new file..." << "\n";
    f->Create(FName);
    exists = 0;
    textWin << "Reference count = " << (int)f->Count() << "\n";
  }
  else {
    textWin << "Opening existing file..." << "\n";
    f->Open(FName); 
    exists = 1;
    textWin << "Reference count = " << (int)f->Count() << "\n";
  }
  
  textWin << "\n";
}

void Clear(MyTextWindow& textWin)
{
  textWin.Clear();
}

FAU AddStringObject(const VBDFilePtr &f, const char *str)
{
  FAU addr = f->Alloc(strlen(str));
  if(!addr) return 0; // Could not allocate space for string
  f->Write(str, strlen(str));
  return addr;
}

char *ReadStringObject(const VBDFilePtr &f, FAU addr = CurrAddress)
{
  unsigned len = f->ObjectLength(addr);
  char *s = new char[len + 1];
  f->Read(s, len, addr);
  s[len] = 0;
  return s;
}

FAU Find(const VBDFilePtr &f, const char *str)
{
  char *buf;
  FAU addr = 0;
  FAU offset = sizeof(VBHeader);

  while(1)
    {
      addr = f->FindFirstObject(addr);
      if(!addr) break;
      buf = ReadStringObject(f);
      int result = strcmp(str, buf);
      if(result == 0) return addr;
    }
  return 0; // Could not find string
}

char *StringCat(const char *s1, const char *s2, const char *s3)
{
  int len = strlen(s1) + strlen(s2) + strlen(s3);
  char *comp = new char[len];
  comp[len] = '\0';
  strcpy(comp, s1);
  strcat(comp, s2);
  strcat(comp, s3);
  return comp;
}

char *StringCat(char *s1, char *s2, char *s3)
{
  int len = strlen(s1) + strlen(s2) + strlen(s3);
  char *comp = new char[len];
  comp[len] = '\0';
  strcpy(comp, s1);
  strcat(comp, s2);
  strcat(comp, s3);
  return comp;
}

void AddString(MyTextWindow &textWin)
{
  char *s = wxGetTextFromUser("Enter a string", "String Input");
  if (!s) return; // Nothing was entered by the user

  FAU exists = Find(f, s);
  
  if(exists) {
    char *mesg = StringCat("String: ", s, " already exists");
    wxMessageBox(mesg, "Object exists", wxOK|wxCENTRE);
    delete mesg;
    return;
  }
  
  FAU addr = AddStringObject(f, s);
  if(!addr) {
    textWin << "Could not add string object to database" << "\n";
    return;
  }
  else
    textWin << "Added: " << s << " to the database" << "\n";
}

void Delete(MyTextWindow &textWin)
{
  char *s = wxGetTextFromUser("Enter a string to delete", "String Input");
  if (!s) return; // Nothing was entered by the user

  FAU addr = Find(f, s);
  
  if(!addr) {
    char *mesg = StringCat("Could not find string: ", s);
    wxMessageBox(mesg, "Object not found", wxOK|wxCENTRE);
    delete mesg;
    return;
  }

  f->Delete(addr); // Delete at object's address
  textWin << "Deleted string: " << s << "\n";
}

void Remove(MyTextWindow &textWin)
{
  char *s = wxGetTextFromUser("Enter a string to remove", "String Input");
  if (!s) return; // Nothing was entered by the user

  FAU addr = Find(f, s);
  
  if(!addr) {
    char *mesg = StringCat("Could not find string: ", s);
    wxMessageBox(mesg, "Object not found", wxOK|wxCENTRE);
    delete mesg;
    return;
  }

  f->Remove(addr); // Remove at object's address
  textWin << "Removed string: " << s << "\n";
}

void Build(MyTextWindow &textWin)
{
  textWin << "Adding 9 three byte string objects to database..." << "\n";
  
  char *aa = "DOG";
  char *bb = "CAT";
  char *cc = "COW";
  char *dd = "BAT";
  char *ee = "ANT";
  char *ff = "BUG";
  char *gg = "HOG";
  char *hh = "PIG";
  char *ii = "HEN";
  
  if(!Find(f, aa)) AddStringObject(f, aa);
  if(!Find(f, bb)) AddStringObject(f, bb);
  if(!Find(f, cc)) AddStringObject(f, cc);
  if(!Find(f, dd)) AddStringObject(f, dd);
  if(!Find(f, ee)) AddStringObject(f, ee);
  if(!Find(f, ff)) AddStringObject(f, ff);
  if(!Find(f, gg)) AddStringObject(f, gg);
  if(!Find(f, hh)) AddStringObject(f, hh);
  if(!Find(f, ii)) AddStringObject(f, ii);
}

void FindString(MyTextWindow &textWin)
{
  char *s = wxGetTextFromUser("Enter a string to find", "String Input");
  if (!s) return; // Nothing was entered by the user

  FAU addr = Find(f, s);
  
  if(!addr) {
    char *mesg = StringCat("Could not find string: ", s);
    wxMessageBox(mesg, "Object not found", wxOK|wxCENTRE);
    delete mesg;
    return;
  }

  textWin << "Found string: " << s << "\n";
}

void ListInOrder(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Listing of all string objects:" << "\n" << "\n";
  
  FAU addr = 0;
  char *str;
  int i = 0;
  
  while(1)
    {
      addr = f->FindFirstObject(addr);
      if(!addr) break;
      i++;
      str = ReadStringObject(f);
      textWin << "(" << i << ")\t" << str << "\n";
    }
}

void ListNextOrder(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Listing of every other string object:" << "\n" << "\n";
  
  char *str;
  FAU addr = 0;

  int i = 0;
  
  while(1)
    {
      addr = f->FindNextObject(addr);
      if(!addr) break;
      i++;
      str = ReadStringObject(f);
      textWin << "(" << i << ")\t" << str << "\n";
    }
}

void DumpInOrder(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Dumping all objects..." << "\n"; 
  textWin << "This listing includes delete/removed varible blocks."
	  << "\n" << "\n";

  char *str;
  FAU addr = 0;
  FAU offset = sizeof(VBHeader);

  int i = 0;
  
  while(1)
    {
      addr = f->FindFirstVB(addr+offset);
      if(!addr) break;
      i++;
      str = ReadStringObject(f, addr+sizeof(VBHeader));
      textWin << "(" << i << ")\t" << str << "\n";
    }
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //

