//
// The Fusion Library Interface for DOS
// Version 1.06c
// Copyright (C) 1990, 1991, 1992
// Software Dimensions
//
// FusionHelp
// Context Sensitive Help
//

#include "fli.h"
#include "elements.h"
#include "colors.h"

#ifdef __BCPLUSPLUS__
#pragma hdrstop
#endif

#pragma warn -inl

#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <string.h>
#include <alloc.h>
#include <mem.h>

#pragma warn -par
#pragma warn -use

static char EndCopyRight[] = "FusionHelp 1.00 (c) Software Dimensions";

static char HelpFile[80] = "FUSION.HLP";

int HelpSystemAvailable=0; // Is help available (i.e. the help file)
int DisabledHelp=0; // Was the help system shut off by the programmer?

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// class HelpLister -- another illustration of how to create a quick element
//
// This class handles the display of help through a dialog
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

class HelpLister : public DialogElement
{
protected:

  int &ItemCount;
  char **Items;
  int ItemAtTopOfList;
  int Widest;
  int OffsetFromLeft;

public:

  HelpLister(int _X,int _Y,int _Width,int _Height,int &_ItemCount,char **_Items) :
      ItemCount(_ItemCount),
      Items(_Items)
  {
    X=_X;
    Y=_Y;
    Width=_Width+3;
    Height=_Height+1;
    ItemAtTopOfList=0;
    OffsetFromLeft=0;
    Widest=0;
    for (int i=0;i<_ItemCount;i++)
      if (strlen(_Items[i])>Widest)
        Widest=strlen(Items[i]);
  }

  void Show()
  {
    MouseHide();

    Blaze->EraseArea(X,Y,Width-1,Height,Colors.PickText);
    Blaze->CharacterRepeaterDown(X+Width-1,Y,Height-1,Colors.PickButton,0xb1);
    Blaze->CharacterRepeater(X,Y+Height-1,Width-1,Colors.PickButton,0xb1);

    (*Blaze) (X+Width-1,Y)
      << Colors.PickButton
      << '\x1e';

    (*Blaze) (X+Width-1,Y+Height-2)
      << '\x1f';

    (*Blaze) (X,Y+Height-1)
      << '\x11';

    (*Blaze) (X+Width-2,Y+Height-1)
      << '\x10';

    if (ItemCount && ItemCount>Height-3)
    {
      int Calc=Height-3;

      if (ItemCount)
      {
        ItemCount-=(Height-1);

        int Determine=ItemCount/Calc;
        int AnotherDetermine=0;
        if (ItemAtTopOfList && Determine)
          AnotherDetermine=ItemAtTopOfList/Determine;

        ItemCount+=(Height-1);

        if (AnotherDetermine>=Height-3)
          AnotherDetermine=Height-4;

        (*Blaze) (X+Width-1,Y+1+AnotherDetermine)
          << '\xfe';
      }
    }

    if (Widest && Widest>Width-3)
    {
      int Calc=Width-3;

      int Determine=Widest/Calc;
      int AnotherDetermine=0;
      if (OffsetFromLeft && Determine)
        AnotherDetermine=OffsetFromLeft/Determine;

      if (AnotherDetermine>=Width-3)
        AnotherDetermine=Width-4;

      (*Blaze) (X+1+AnotherDetermine,Y+Height-1)
        << '\xfe';
    }

    int LocX=0, LocY=0, LocWide=0, LocHigh=0;

    Blaze->WindowInformation(LocX,LocY,LocWide,LocHigh);

    BlazeClass Blaze;
    Blaze.Window(LocX+X,LocY+Y,Width-2,Height);
    +Blaze;
    !Blaze;

    if (ItemCount)
      for (int i=0;i<Height-1;i++)
      {
        if (OffsetFromLeft<strlen(Items[ItemAtTopOfList+i]))
          Blaze (1,i)
            << Colors.PickText
            << (Items[ItemAtTopOfList+i]+OffsetFromLeft);

        if (ItemAtTopOfList+i==ItemCount-1)
          break;
      }

    MouseShow();
  }

  void HighLight()
  {
    Blaze->WindowGotoXY(X+1,Y);
  }

  int EventHandler(int Event)
  {
    if (Event==kbDown ||
      (Event==ValidatedMousedEvent
        && (MouseEvent&MouseLeftButtonRelease || MouseEvent&MouseHeldDown)
        && MouseHorizontal==X+Width-1
        && MouseVertical==Y+Height-2))
    {
      if (ItemCount && ItemAtTopOfList+Height-1<ItemCount)
      {
        ItemAtTopOfList++;
        if (ItemAtTopOfList>=ItemCount)
        {
          ItemAtTopOfList--;
          return CompleteEvent;
        }
        Show();
        HighLight();
      }
      return CompleteEvent;
    }

    if (Event==kbUp ||
      (Event==ValidatedMousedEvent
        && (MouseEvent&MouseLeftButtonRelease || MouseEvent&MouseHeldDown)
        && MouseHorizontal==X+Width-1
        && MouseVertical==Y))
    {
      if (ItemCount)
      {
        ItemAtTopOfList--;
        if (ItemAtTopOfList<0)
        {
          ItemAtTopOfList++;
          return CompleteEvent;
        }
        Show();
        HighLight();
      }
      return CompleteEvent;
    }

    if (Widest>Width-3 && (Event==kbRight ||
      (Event==ValidatedMousedEvent
        && (MouseEvent&MouseLeftButtonRelease || MouseEvent&MouseHeldDown)
        && MouseHorizontal==X+Width-2
        && MouseVertical==Y+Height-1)))
    {
      if (OffsetFromLeft>=Widest)
        return CompleteEvent;
      else
        OffsetFromLeft++;
      Show();
      HighLight();
      return CompleteEvent;
    }

    if (OffsetFromLeft && (Event==kbLeft ||
      (Event==ValidatedMousedEvent
        && (MouseEvent&MouseLeftButtonRelease || MouseEvent&MouseHeldDown)
        && MouseHorizontal==X
        && MouseVertical==Y+Height-1)))
    {
      OffsetFromLeft--;
      Show();
      HighLight();
      return CompleteEvent;
    }

    if (MouseButtonStatus&LeftButton && ItemCount && ItemCount>Height-3 && Height>3 &&
      MouseHorizontal==X+Width-1 && MouseVertical>Y && MouseVertical<Y+Height-2)
    {
      int Block=MouseVertical-Y-1;

      int Calc=Height-3;

      ItemCount-=(Height-1);

      if (!ItemCount)
        return CompleteEvent;

      int Determine=ItemCount/Calc;
      int AnotherDetermine=Block*Determine;

      ItemCount+=(Height-1);

      ItemAtTopOfList=AnotherDetermine;

      if (ItemAtTopOfList+Height-1>ItemCount || Block==Height-4)
        ItemAtTopOfList=(ItemCount-Height)+1;

      if (ItemAtTopOfList<0)
        ItemAtTopOfList=0;

      Show();
      HighLight();

      return CompleteEvent;
    }

    if (MouseButtonStatus&LeftButton && Widest>Width-3 &&
      MouseVertical==Y+Height-1 && MouseHorizontal>X && MouseHorizontal<X+Width-2)
    {
      int Block=MouseHorizontal-X-1;

      int Calc=Width-3;

      int Determine=Widest/Calc;
      int AnotherDetermine=Block*Determine;
      if (AnotherDetermine>Widest)
        AnotherDetermine=Widest-1;

      OffsetFromLeft=AnotherDetermine;

      Show();
      HighLight();

      return CompleteEvent;
    }

    if (Event==ValidatedMousedEvent && MouseEvent&MouseDoubleClick)
      return StopEvent;

    return Event;
  }
};

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// class HelpConnections
//
// This class connects the help control file before the program starts
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

class HelpConnections
{
  public:

  HelpConnections()
  {
    if (!access(HelpFile,0))
      HelpSystemAvailable=1;
  }
};

static HelpConnections MakeTheConnection;

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// FusionHelp()
//
// Constructor
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

FusionHelp::FusionHelp()
{
  HelpId=0;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// DefineHelp()
//
// Define an alternate help file
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

int FusionHelp::DefineHelp(char *HelpFileName)
{
  if (!access(HelpFileName,0))
  {
    strcpy(HelpFile,HelpFileName);
    HelpSystemAvailable=1;
    return 1;
  }
  else
  {
    HelpSystemAvailable=0;
    return 0;
  }
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// DisableHelp()
//
// Disables the help system
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void FusionHelp::DisableHelp()
{
  DisabledHelp=1;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// HelpDialog class
//
// Defines the event handler for a help system
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

const winAcceptButton=995;

class HelpDialog : public DialogClass
{
public:

  HelpDialog(int Width,int Height,char *Title) :
    DialogClass(Width,Height,Title) { }

  int EventHandler(int Event)
  {
    if (Event==kbEsc ||
        Event==kbCr ||
        Event==CloseEvent ||
        Event==OutsideEvent ||
        Event==winAcceptButton)
      return StopEvent;
    return CompleteEvent;
  }
};

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// RequestHelp()
//
// Request help by calling the help system directly
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void FusionHelp::RequestHelp(int Help)
{
  int SaveHelp=HelpId;

  HelpId=Help;
  EngageHelp();
  HelpId=SaveHelp;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// EngageHelp()
//
// Define an alternate help file
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void FusionHelp::EngageHelp()
{
  if (DisabledHelp)
    return;

  if (HelpSystemAvailable && HelpId)
  {
    if (HelpId>0 && HelpId<1000)
      return;

    if (access(HelpFile,0))
      goto HelpMissing;

    int Help=_open(HelpFile,O_RDONLY|O_BINARY|O_DENYWRITE);

    lseek(Help,0,SEEK_SET);

    char CopyRightCheck[sizeof(EndCopyRight)+1];

    read(Help,&CopyRightCheck,sizeof(EndCopyRight));

    if (memcmp(EndCopyRight,CopyRightCheck,sizeof(EndCopyRight)))
    {
      close(Help);
      InfoBox &NotAvailable = *new InfoBox;
      NotAvailable
            + "The help file is damaged and contains"
            + "an incorrect signature.  As a result"
            + "the help system is not available.";
      NotAvailable.Title("Help Failure");
      NotAvailable.UseInfoBox();
      delete &NotAvailable;
      return;
    }

    lseek(Help,sizeof(EndCopyRight),SEEK_SET);

    int AvailableHelps=0;

    read(Help,&AvailableHelps,sizeof(int));

    struct _HelpLocator
    {
      int Identifier;
      long Locator;
    } Location;

    for (int i=0;i<AvailableHelps;i++)
    {
      read(Help,&Location,sizeof(_HelpLocator));

      if (Location.Identifier==HelpId)
        goto Accepted;
    }

    close(Help);

    {
    InfoBox &NotAvailable = *new InfoBox;
    NotAvailable
          + "The help for this option appears"
          + "to be missing.";
    NotAvailable.Title("Help Failure");
    NotAvailable.UseInfoBox();
    delete &NotAvailable;
    }

    return;

Accepted:

    lseek(Help,Location.Locator,SEEK_SET);

    long TotalSize;

    read(Help,&TotalSize,sizeof(long));
    
    #pragma warn -sig
    char *Storage=new char[TotalSize];
    read(Help,Storage,TotalSize);
    #pragma warn .sig

    close(Help);

    //------------------------------------------------------------------------
    //
    // Interpret the help file
    //
    //------------------------------------------------------------------------

    char **LineStorage=0;

    int Count=0;

    char Stuffer[300];

    int CurrentWidth=0;

    Stuffer[0]=0;

    char BlankLine[2]="\0";

    int TabActive=0;

    for (int j=0;j<TotalSize;j++)
    {
      if (*(Storage+j)=='\t')
      {
        TabActive++;
        if (CurrentWidth)
        {
          Count++;
          LineStorage=(char**)realloc(LineStorage,Count*sizeof(char*));
          LineStorage[Count-1]=new char[CurrentWidth+1];
          strcpy(LineStorage[Count-1],Stuffer);
        }
        Stuffer[0]=0;
        CurrentWidth=0;
        continue;
      }

      if (*(Storage+j)=='\r')
      {
        if (!CurrentWidth)
        {
          TabActive=0;
          Count++;
          LineStorage=(char**)realloc(LineStorage,Count*sizeof(char*));
          LineStorage[Count-1]=BlankLine;
        }
        else
        {
          if (TabActive)
          {
            Count++;
            LineStorage=(char**)realloc(LineStorage,Count*sizeof(char*));
            LineStorage[Count-1]=new char[CurrentWidth+1];
            strcpy(LineStorage[Count-1],Stuffer);
          }
          else
          {
            Count+=2;
            LineStorage=(char**)realloc(LineStorage,Count*sizeof(char*));
            LineStorage[Count-2]=new char[CurrentWidth+1];
            strcpy(LineStorage[Count-2],Stuffer);
            LineStorage[Count-1]=BlankLine;
          }
          Stuffer[0]=0;
          CurrentWidth=0;
          TabActive=0;
        }
      }
      else
      {
        Stuffer[CurrentWidth]=*(Storage+j);
        Stuffer[++CurrentWidth]=0;

        if (CurrentWidth==53 && !TabActive)
        {
          if (Stuffer[CurrentWidth-1]!=' ')
          {
            do Stuffer[CurrentWidth]=0;
              while(Stuffer[--CurrentWidth]!=' ');
            Stuffer[CurrentWidth]=0;
            do j--;
              while(*(Storage+j)!=' ');
            Count++;
            LineStorage=(char**)realloc(LineStorage,Count*sizeof(char*));
            LineStorage[Count-1]=new char[CurrentWidth+1];
            strcpy(LineStorage[Count-1],Stuffer);
          }
          else
          {
            Count++;
            LineStorage=(char**)realloc(LineStorage,Count*sizeof(char*));
            LineStorage[Count-1]=new char[CurrentWidth+1];
            strcpy(LineStorage[Count-1],Stuffer);
          }
          Stuffer[0]=0;
          CurrentWidth=0;
        }
      }
    }

    if (CurrentWidth)
    {
      Count++;
      LineStorage=(char**)realloc(LineStorage,Count*sizeof(char*));
      LineStorage[Count-1]=new char[CurrentWidth+1];
      strcpy(LineStorage[Count-1],Stuffer);
    }

    delete Storage;

    HelpDialog &Dialog=*new HelpDialog(60,16,"Help");

    Dialog.Element(new HelpLister(1,1,53,9,Count,LineStorage));
    Dialog.Help("This is the help you requested");

    Dialog.Element(new DiaPushButton(26,12,"~Okay",winAcceptButton,kbAltO));
    Dialog.Help("Close the help system and return to the program");

    Dialog.UseDialog();

    delete &Dialog;

    for (i=0;i<Count;i++)
    {
      if (LineStorage[i]!=BlankLine)
        delete LineStorage[i];
    }

    free(LineStorage);

    return;
  }

  if (!HelpSystemAvailable)
  {

HelpMissing:

    InfoBox &NotAvailable = *new InfoBox;
    NotAvailable
          + "The help file is missing.  As a result"
          + "the help system is not available.";
    NotAvailable.Title("Help Failure");
    NotAvailable.UseInfoBox();
    delete &NotAvailable;
  }
}

#pragma warn .inl
