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

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

#ifdef __BCPLUSPLUS__
#pragma hdrstop
#endif

#include <alloc.h>
#include <string.h>
#include <bios.h>

int Event::EventTimer=16;

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// Event()
//
// Constructor for Event class
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

static void (**GlobalActionQueue)();
static int GlobalActionCount;

Event::Event()
{
  ActionQueue=NULL;
  ActionCount=0;
  X=0;
  Y=0;
  Width=0;
  Height=0;
  CloseIcon=1;
  ActionTimer=0;
  ScreenSave=NULL;
  FlushMouseQueue();
  XShadow=0;
  YShadow=0;
  Shadowing=0;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// ~Event()
//
// Destructor for Event class
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Event::~Event()
{
  RestoreWindow();

  if (ActionCount)
    free(ActionQueue);

  FlushMouseQueue();
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// SpecifyWindow() / RestoreWindow()
//
// These functions specify a window region that can be moved or altered,
// anything below these windows is saved upon a call to SpecifyWindow and
// restored upon a call to RestoreWindow.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void Event::SpecifyWindow(int X,int Y,int Width,int Height)
{
  if (ScreenSave)
    delete ScreenSave;

  ScreenSave=NULL;

  if (FusionShadowing)
  {
    Shadowing=1;
    XShadow=0;
    YShadow=0;

    if (X+Width<Blaze.WhatWidth())
      XShadow++;

    if (Y+Height<Blaze.WhatHeight()-1)
      YShadow++;

    if (Width==Blaze.WhatWidth() || Height==Blaze.WhatHeight()-2
      || !XShadow || !YShadow)
    {
      XShadow=0;
      YShadow=0;
      Shadowing=0;
    }

    ScreenSave=new char[Blaze.ComputeNeededBytes(Width+XShadow,Height+YShadow)];

    MouseHide();
    Blaze.GetArea(X,Y,Width+XShadow,Height+YShadow,ScreenSave);
    if (Shadowing)
      Blaze.Shadow(X+XShadow,Y+YShadow,Width,Height);
    MouseShow();
  }
  else
  {
    Shadowing=0;
    ScreenSave=new char[Blaze.ComputeNeededBytes(Width,Height)];
    MouseHide();
    Blaze.GetArea(X,Y,Width,Height,ScreenSave);
    MouseShow();
  }

  Event::X=X;
  Event::Y=Y;
  Event::Width=Width;
  Event::Height=Height;
}

void Event::RestoreWindow()
{
  if (!ScreenSave)
    return;

  MouseHide();
  Blaze.PutArea(X,Y,ScreenSave);
  MouseShow();
  delete ScreenSave;
  ScreenSave=NULL;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// LocalLive()
//
// Adds a "LIVE" function onto the local "LIVE" function handler.
// "LIVE" functions are automatically executed once every second.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void Event::LocalLive(void (*Action)())
{
  ActionCount++;
  ActionQueue=(void(**)())realloc(ActionQueue,ActionCount*sizeof(void (*)()));
  ActionQueue[ActionCount-1]=Action;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// GlobalLive()
//
// Adds a "LIVE" function onto the global "LIVE" function handler.
// Global "LIVE" functions are alive for all occurances of the event class.
// "LIVE" functions are automatically executed once every second.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void Event::GlobalLive(void (*GlobalAction)())
{
  GlobalActionCount++;
  GlobalActionQueue=
    (void(**)())realloc(GlobalActionQueue,GlobalActionCount*sizeof(void (*)()));
  GlobalActionQueue[GlobalActionCount-1]=GlobalAction;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// NotClosable()
//
// Signals that there isn't a close button on this window
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void Event::NotClosable()
{
  CloseIcon=0;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// GetEvent()
//
// Checks for an event and returns one, if found.  All "LIVE" or action
// events are executed when this function is idle (no keyboard or mouse
// movements).  If the right position of a particular item (window) is
// grabbed, then the window is moved, or closed.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

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

int Event::GetEvent(int SeeEvent)
{
  static int RememberButtonDown=0;
  static long RememberDownDelay=0;

Top:

  if (!MouseButtons()&LeftButton)
    RememberButtonDown=0;

  int LastShift=bioskey(2);
  GetMouseQueue();

  while (!Blaze.SeeKey() && !MouseEvent && bioskey(2)==LastShift)
  {
    GetMouseQueue();

    if (MouseButtons()&LeftButton &&
      RememberButtonDown &&
      RememberDownDelay<biostime(0,0))
    {
      MouseEvent|=MouseHeldDown;
      MouseLocate();
      RememberDownDelay=biostime(0,0)+MouseRepeatSpeed;
      return MousedEvent;
    }

    if ((ActionCount || GlobalActionCount) && biostime(0,0)>(ActionTimer+EventTimer))
    {
      MouseHide();
      if (GlobalActionCount)
      {
        for (int i=0;i<GlobalActionCount;i++)
          (GlobalActionQueue[i])();
      }
      if (ActionCount)
      {
        for (int i=0;i<ActionCount;i++)
          (ActionQueue[i])();
      }
      MouseShow();
      ActionTimer=biostime(0,0);
    }

    if (SeeEvent)
    {
      if (MouseEvent)
        return MousedEvent;
      return 0;
    }
  };

  if (Blaze.SeeKey()==kbF1 || MouseEvent&MouseRightButtonRelease)
  {
    if (DisabledHelp || !HelpSystemAvailable)
      return (Blaze.SeeKey()==kbF1)?Blaze.GetKey():MousedEvent;
    if (Blaze.SeeKey()==kbF1)
      Blaze.GetKey();
    EngageHelp();
    return HelpEvent;
  }

  if (Blaze.SeeKey())
    return Blaze.GetKey();

  if (bioskey(2)!=LastShift)
    return ShiftEvent;

  if (MouseEvent)
  {
    if (Width)
    {
      if (MouseEvent&MouseLeftButtonPress && MouseVertical==Y &&
        ((CloseIcon && MouseHorizontal>=X+4 && MouseHorizontal<X+Width-1) ||
         (!CloseIcon && MouseHorizontal>X && MouseHorizontal<X+Width-1)))
      {
        if (!Y) // protects against attempted moves top line
          return MousedEvent;
        MoveWindow();
        return MoveEvent;
      }

      if (MouseEvent&MouseLeftButtonRelease && CloseIcon &&
        (MouseHorizontal==X+2 && MouseVertical==Y))
        return CloseEvent;

      if (MouseEvent&MouseLeftButtonRelease &&
        (MouseHorizontal<X || MouseHorizontal>=(X+Width) ||
         MouseVertical<Y || MouseVertical>=(Y+Height)))
        return OutsideEvent;
    }

    if (MouseEvent&MouseLeftButtonPress)
    {
      RememberButtonDown=1;
      RememberDownDelay=biostime(0,0)+MouseRepeatDelay;
    }
    else
    {
      if (!MouseButtonStatus&LeftButton)
        RememberButtonDown=0;
      else if (RememberButtonDown)
        MouseEvent|=MouseHeldDown;
    }

    return MousedEvent;
  }

  return 0;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// JustEvent()
//
// Just grab an event.  This function is used locally and is a private
// member function.  It does not perform some of the extended mouse features
// that are found in the GetEvent() member function.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

int Event::JustEvent()
{
  GetMouseQueue();

  while (!Blaze.SeeKey() && !MouseEvent)
  {
    GetMouseQueue();
    if ((ActionCount || GlobalActionCount) && biostime(0,0)>(ActionTimer+EventTimer))
    {
      if (ActionCount)
      {
        for (int i=0;i<ActionCount;i++)
          (ActionQueue[i])();
      }
      if (GlobalActionCount)
      {
        for (int i=0;i<GlobalActionCount;i++)
          (GlobalActionQueue[i])();
      }
      ActionTimer=biostime(0,0);
    }
  };

  if (Blaze.SeeKey())
    return Blaze.GetKey();
  else
    return MousedEvent;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// MoveWindow()
//
// Move the window.  This function is used locally and is a private member
// function that is called from GetEvent() when a mouse grabs the
// top of any window object.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void Event::MoveWindow()
{
  MouseHide();

  int ActionX=MouseHorizontal;
  int Locate=ActionX-X;
  int ActionY=MouseVertical;

  Blaze.Window(0,0,Blaze.WhatWidth(),Blaze.WhatHeight());
  Blaze.InvisibleCursor();

  Blaze.HelpLine(0,"Use the mouse to move the window");

  char *EntireSave=
    new char[Blaze.ComputeNeededBytes(Blaze.WhatWidth(),Blaze.WhatHeight())];
  char *MenuSave=new char[Blaze.ComputeNeededBytes(Width+XShadow,Height+YShadow)];

  Blaze.CopyVisualToVirtual(EntireSave);

  Blaze.UseMemory(EntireSave);

  Blaze.GetArea(X,Y,Width,Height,MenuSave);

  MouseShow();

  int ExactX=MouseHorizontal-X;
  int ExactY=MouseVertical-Y;

  for (;;)
  {
    JustEvent();

    if (MouseEvent&4)
    {
      delete EntireSave;
      delete MenuSave;
      Blaze.UseVideo();
      return;
    }

    if (MouseEvent&1)
    {
      MouseHide();

      Blaze.PutArea(X,Y,ScreenSave);

      if (ActionX>=(X+Locate))
        X+=(MouseHorizontal-ActionX);
      if (ActionY>=Y)
        Y+=(MouseVertical-ActionY);

      if (X<0)
        X=0;
      if ((X+Width+XShadow)>Blaze.WhatWidth())
        X=Blaze.WhatWidth()-Width-XShadow;

      if (Y<1)
        Y=1;
      if ((Y+Height+YShadow)>(Blaze.WhatHeight()-1))
        Y=Blaze.WhatHeight()-1-Height-YShadow;

      ActionX=MouseHorizontal;
      ActionY=MouseVertical;

      Blaze.GetArea(X,Y,Width+XShadow,Height+YShadow,ScreenSave);

      if (Shadowing)
        Blaze.Shadow(X+XShadow,Y+YShadow,Width,Height);

      Blaze.PutArea(X,Y,MenuSave);

      Blaze.BlockCopyVirtualToVisual(0,1,Blaze.WhatWidth(),Blaze.WhatHeight()-2,
        EntireSave);

      MouseShow();

     if (MouseVertical!=Y+ExactY ||
       MouseHorizontal!=X+ExactX)
     {
       MousePosition(X+ExactX,Y+ExactY);
       MouseLocate();
       ActionX=MouseHorizontal;
       ActionY=MouseVertical;
     }
    }
  }
}

