#include <alloc.h>
#include <conio.h>
#include <dos.h>
#include <graphics.h>
#include <stdlib.h>
#include <mem.h>

class CursorType {
   unsigned int OldCursor;
   void Switch (void);
public:
   // constructor
   CursorType (void);
   // destructor
   ~CursorType (void);
};

class ScreenType {
   unsigned int VirtualSeg, RealSeg;
public:
   unsigned int ScreenSeg;
   // constructor
   ScreenType (void);
   // destructor
   ~ScreenType (void);
   void Switch (void);
};

const NumBlocks	   = 4;
const TopLine      = 18-NumBlocks;
const Delay	   = 0;

typedef struct BlockType1 {
   int Width;
   struct BlockType1 *Next;
} BlockType;

class TowerType {
   BlockType *Head;
public:
   unsigned char xPosition, Height;
   // constructor
   TowerType ( unsigned char Position );
   void Push ( unsigned char aWidth );
   unsigned char Pop (void);
   void Draw (void);
};

TowerType *Tower[3];
ScreenType *Screen;

// ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл //

void CursorType::Switch (void)
{
   unsigned int Alternate;
   struct REGPACK Regs;

   Regs.r_ax=0x0300;
   Regs.r_bx=0;
   intr (0x10, &Regs);
   Alternate=Regs.r_cx;
   Regs.r_ax=0x0100;
   Regs.r_cx=OldCursor;
   intr (0x10, &Regs);
   OldCursor=Alternate;
}

CursorType::CursorType (void)
{
   OldCursor=0x2000;
   Switch();
}

CursorType::~CursorType (void)
{
   Switch();
}

ScreenType::ScreenType (void)
{
   int gd, gm;

   if (allocmem (250, &VirtualSeg)!=-1) exit(1);

   detectgraph(&gd, &gm);
   if (gd == HERCMONO)
      RealSeg = 0xB000;
   else
      RealSeg = 0xB800;
   ScreenSeg=RealSeg;
}

ScreenType::~ScreenType (void)
{
   freemem (VirtualSeg);
}

void ScreenType::Switch (void)
{
   if (ScreenSeg==RealSeg)
   {
      movedata (RealSeg, 0, VirtualSeg, 0, 4000);
      ScreenSeg=VirtualSeg;
   }
   else
   {
      movedata (VirtualSeg, 0, RealSeg, 0, 4000);
      ScreenSeg=RealSeg;
   }
}

// ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл //

void PutChar ( unsigned char xPos, unsigned char yPos,
	       unsigned char Ch, unsigned char Col )
{
   unsigned int Offset=((yPos-1)*160)+((xPos-1)*2);
   *(unsigned char far *)MK_FP (Screen->ScreenSeg, Offset)=Ch;
   *(unsigned char far *)MK_FP (Screen->ScreenSeg, Offset+1)=Col;
} 

void DrawBlock ( unsigned char xPos, unsigned char yPos,
		 unsigned char Width, unsigned char State )
{
   unsigned char Blok=(State==1) ? 178 : 32;
   for (unsigned char i=1; i<=Width; i++)
   {
	PutChar (xPos+1-i, yPos, Blok, 7);
	PutChar (xPos+i, yPos, Blok, 7);
   }
}

// ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл //

TowerType::TowerType ( unsigned char Position )
{
   Head=NULL;
   Height=0;
   xPosition=Position;
}

void TowerType::Push ( unsigned char aWidth )
{
   BlockType *p;
   p = new BlockType;
   p->Width=aWidth;
   p->Next=Head;
   Head=p;
   Height++;
}

unsigned char TowerType::Pop (void)
{
   BlockType *p;
   unsigned char LastWidth;
   p=Head;
   if (Head==NULL)
      return 0;
   Head=Head->Next;
   LastWidth=p->Width;
   delete p;
   Height--;
   return LastWidth;
}

void TowerType::Draw (void)
{
   unsigned char yPos=21-Height;
   BlockType *p;
   p=Head;
   while (p!=NULL)
   {
      DrawBlock (xPosition, yPos++, p->Width, 1);
      p=p->Next;
   }
}

// ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл //

void RefreshDisplay (void)
{
   clrscr();
   for (unsigned char i=0; i<3; i++)
      Tower[i]->Draw();
}

void StartTower ( unsigned char TowerNo, unsigned char Size )
{
   clrscr();
   for (unsigned char i=Size; i>0; i--)
      Tower[TowerNo]->Push(i);
   Tower[TowerNo]->Draw();
}

void AnimateBlock ( unsigned char Source, unsigned char Dest,
		    unsigned char Width )
{
   unsigned char yPos=20-Tower[Source]->Height;
   unsigned char xPos=Tower[Source]->xPosition;
   while (yPos>TopLine)
   {
      Screen->Switch();
      DrawBlock (xPos, yPos, Width, 0);
      DrawBlock (xPos, --yPos, Width, 1);
      Screen->Switch();
      delay (Delay);
   }
   while (xPos<Tower[Dest]->xPosition)
   {
      Screen->Switch();
      DrawBlock (xPos, yPos, Width, 0);
      DrawBlock (++xPos, yPos, Width, 1);
      Screen->Switch();
      delay (Delay);
   }
   while (xPos>Tower[Dest]->xPosition)
   {
      Screen->Switch();
      DrawBlock (xPos, yPos, Width, 0);
      DrawBlock (--xPos, yPos, Width, 1);
      Screen->Switch();
      delay (Delay);
   }
   while (yPos<(20-Tower[Dest]->Height))
   {
      Screen->Switch();
      DrawBlock (xPos, yPos, Width, 0);
      DrawBlock (xPos, ++yPos, Width, 1);
      Screen->Switch();
      delay (Delay);
   }
}

// ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл //

void ShiftBlock ( unsigned char Source, unsigned char Dest )
{
   unsigned char BlockWidth=Tower[Source]->Pop();
   AnimateBlock (Source, Dest, BlockWidth);
   Tower[Dest]->Push (BlockWidth);
//   RefreshDisplay();
}

void MoveBlocks ( unsigned char Source, unsigned char Dest,
		  unsigned char Size )
{
   if (Size==1)
      ShiftBlock (Source, Dest);
   else
   {
      MoveBlocks (Source, 3-Source-Dest, Size-1);
      ShiftBlock (Source, Dest);
      MoveBlocks (3-Source-Dest, Dest, Size-1);
   }
}

// ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл //
// лллллллллллллллллллллллллл  MAIN PROGRAM  ллллллллллллллллллллллллллллл //
// ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл //

void main (void)
{
   CursorType Cursor;
   ScreenType ScreenX;

   Screen=&ScreenX;

   TowerType TowerLeft(15), TowerMiddle(40), TowerRight(65);
   Tower[0]=&TowerLeft;
   Tower[1]=&TowerMiddle;
   Tower[2]=&TowerRight;

   StartTower (0, NumBlocks);
   MoveBlocks (0, 2, NumBlocks);
}
