define  coDesktop            0
define  coShadow             1
define  coBorder             2
define  coCorner             3
define  coWindow             4
define  coSelect             5


define  pmMessage            0
define  pmMouse_Y            1
define  pmMouse_X            2
define  pmButtons            3
define  pmKbdCode            4
define  pmKbdIndo            5
define  pmTime_Lo            6
define  pmTime_Hi            7


define  sfCenterAlignY1  $0001
define  sfBottomAlignY1  $0002
define  sfCenterAlignX1  $0004
define  sfRight_AlignX1  $0008
define  sfCenterAlignY2  $0010
define  sfBottomAlignY2  $0020
define  sfCenterAlignX2  $0040
define  sfRight_AlignX2  $0080
define  sfSelectable     $0100
define  sfMoveable       $0200
define  sfSizeable       $0400
define  sfShadowed       $0800


define  wmQuit           $0001
define  wmKeyboard       $0002
define  wmViewWnd        $0003

define  wmPuzzle         $0101
define  wmJump           $0102


struct Addr
  word  Ofs;
  word  Seg;
section
  void @Ptr;
end

void @Ptr(word Seg, Ofs)
  Addr  P;

  P.Seg=Seg;
  P.Ofs=Ofs;

  return @P.Ptr;
end


struct CPoint
  int         Y;
  int         X;
  void        Init    (int y, x);
end

void CPoint.Init(int y, x)
  Y=y;
  X=x;
end


struct CVideoMem
  char Ch;
  byte Attr;
end


struct CScreen
  CVideoMem  @VMem;
  CVideoMem   Buff [25][80];
  byte        Attr [32];

  CPoint      C, D;

  void        Init    ();
  void        Open    ();
  void        Clip    (int Y1, X1, Y2, X2);
  void        Fill    (int Y1, X1, Y2, X2;           int pAttr);
  void        PutC    (int Y1, X1, Y2, X2; char  Ch; int pAttr);
  void        PutS    (int Y1, X1;         char @St; int pAttr);
  void        Copy    ();
end

void CScreen.Init()
  C.Init( 0, 0);
  D.Init(24,79);

  Attr[coDesktop]=$70; // $07;
  Attr[coShadow] =$08;
  Attr[coBorder] =$3F;
  Attr[coCorner] =$30;
  Attr[coWindow] =$3F;
  Attr[coSelect] =$0F;

  @VMem=@Ptr($B800,$0000);
end

void CScreen.Open()
  C.Init( 0, 0);
  D.Init(24,79);
end

void CScreen.Clip(int Y1, X1, Y2, X2)
  if C.Y<Y1 then
    C.Y=Y1;
  end

  if C.X<X1 then
    C.X=X1;
  end

  if D.Y>Y2 then
    D.Y=Y2;
  end

  if D.X>X2 then
    D.X=X2;
  end
end

void CScreen.Fill(int Y1, X1, Y2, X2; int pAttr)
  if Y1<C.Y then
    Y1=C.Y;
  end

  if X1<C.X then
    X1=C.X;
  end

  if Y2>D.Y then
    Y2=D.Y;
  end

  if X2>D.X then
    X2=D.X;
  end

  int Y=Y1;
  while Y<=Y2 do
    int X=X1;
    while X<=X2 do
      Buff[Y][X].Attr=Attr[pAttr];
      inc  X;
    end

    inc  Y;
  end
end

void CScreen.PutC(int Y1, X1, Y2, X2; char  Ch; int pAttr)
  if Y1<C.Y then
    Y1=C.Y;
  end

  if X1<C.X then
    X1=C.X;
  end

  if Y2>D.Y then
    Y2=D.Y;
  end

  if X2>D.X then
    X2=D.X;
  end

  int Y=Y1;
  while Y<=Y2 do
    int X=X1;
    while X<=X2 do
      Buff[Y][X].Ch  =Ch;
      Buff[Y][X].Attr=Attr[pAttr];
      inc  X;
    end

    inc  Y;
  end
end

void CScreen.PutS(int Y1, X1; char @St; int pAttr)
  if C.Y<=Y1 & Y1<=D.Y then
    int I=0;
    while St[I]!=#0 do
      if C.X<=X1 & X1<=D.X then
        Buff[Y1][X1].Ch  =St  [I];
        Buff[Y1][X1].Attr=Attr[pAttr];
      end

      inc X1;
      inc I;
    end
  end
end

void CScreen.Copy()
  int Y=C.Y;
  while Y<=D.Y do
    int P=80*Y+C.X;
    int X=C.X;
    while X<=D.X do
      VMem[P]=Buff[Y][X];
      inc  P;
      inc  X;
    end

    inc Y;
  end
end


struct CWindow;

struct CMessage
  int         Parm [8];
  CWindow    @From;

  int         Message();
  int         KbdCode();
  int         MouseY ();
  int         MouseX ();
  int         LButton();
end

int CMessage.Message()
  return Parm[pmMessage];
end

int CMessage.KbdCode()
  return Parm[pmKbdCode];
end

int CMessage.MouseY()
  return Parm[pmMouse_Y];
end

int CMessage.MouseX()
  return Parm[pmMouse_X];
end

int CMessage.LButton()
  return Parm[pmButtons];
end

struct CMouse
  int         Present;
  int         Visible;
  word        Buttons;
  int         Y;
  int         X;

  void        Init    ();
  void        Show    ();
  void        Hide    ();
  void        Test    ();
  int         GetY    ();
  int         GetX    ();
end


void CMouse.Init()
  int Flag;

  asm mov     AX,0
  asm int     33H
  asm mov     SS:[BP-2],AX

  if Flag!=0 then
    Present=1;
  else
    Present=0;
  end

  Y        =0;
  X        =0;
  Buttons  =0;
  Visible  =0;
end

void CMouse.Show()
  if Present!=0 then
    if Visible=0 then
      asm mov     AX,1
      asm int     33H

      Visible=1;
    end
  end
end

void CMouse.Hide()
  if Present!=0 then
    if Visible!=0 then
      asm mov     AX,2
      asm int     33H

      Visible=0;
    end
  end
end

void CMouse.Test()
  if Present!=0 then
    int  Y;
    int  X;
    word B;

    asm mov     AX,3
    asm int     33H

    asm  mov    SS:[BP-2],DX
    asm  mov    SS:[BP-4],CX
    asm  mov    SS:[BP-6],BX

    self.Y =Y/8;
    self.X =X/8;
    Buttons=B;
  end
end

struct CWindow
  CPoint      A, B;
  CPoint      C, D;
  CWindow    @Next;
  CWindow    @Owner;
  word        Flags;

  void        Init    (int y1, x1, y2, x2);
  void        SetFlags(word F);
  void        ClrFlags(word F);
  word        Pick    (int  Y, X);
  word        Focus   ();
  word        Post    (CMessage @Msg) virtual;
  word        Send    (CMessage @Msg) virtual;
  word        Show    (CScreen  @Scr) virtual;
  void        View    ();
end

void CWindow.Init(int y1, x1, y2, x2)
  A.Init(y1,x1);
  B.Init(y2,x2);

  @Next =NULL;
  @Owner=NULL;
  Flags =0;
end

void CWindow.SetFlags(word F)
  Flags=Flags |  F;
end

void CWindow.ClrFlags(word F)
  Flags=Flags & !F;
end

word CWindow.Focus()
  if @Next!=NULL then
    return 0;
  end

  if @Owner!=NULL then
    return Owner.Focus();
  end

  return   1;
end

word CWindow.Pick(int Y, X)
  if !(C.Y<=Y & Y<=D.Y) then
    return 0;
  end

  if !(C.X<=X & X<=D.X) then
    return 0;
  end

  if @Owner!=NULL then
    return Owner.Pick(Y,X);
  end

  return 1;
end

word CWindow.Post(CMessage @Msg) virtual
  return 0;
end

word CWindow.Send(CMessage @Msg) virtual
  if @Owner!=NULL then
    return Owner.Send(@Msg);
  end

  return 0;
end

word CWindow.Show(CScreen @Scr) virtual
  if @Owner=NULL then
    C.Init( 0, 0);
    D.Init(24,79);
    return 1;
  end

  select
    case (Flags & sfCenterAlignY1)!=0:
      C.Y=(Owner.C.Y+Owner.D.Y)/2+A.Y;
    case (Flags & sfBottomAlignY1)!=0:
      C.Y=Owner.D.Y+A.Y;
    default:
      C.Y=Owner.C.Y+A.Y;
  end

  select
    case (Flags & sfCenterAlignX1)!=0:
      C.X=(Owner.C.X+Owner.D.X)/2+A.X;
    case (Flags & sfRight_AlignX1)!=0:
      C.X=Owner.D.X+A.X;
    default:
      C.X=Owner.C.X+A.X;
  end

  select
    case (Flags & sfCenterAlignY2)!=0:
      D.Y=(Owner.C.Y+Owner.D.Y)/2+B.Y;
    case (Flags & sfBottomAlignY2)!=0:
      D.Y=Owner.D.Y+B.Y;
    default:
      D.Y=Owner.C.Y+B.Y;
  end

  select
    case (Flags & sfCenterAlignX2)!=0:
      D.X=(Owner.C.X+Owner.D.X)/2+B.X;
    case (Flags & sfRight_AlignX2)!=0:
      D.X=Owner.D.X+B.X;
    default:
      D.X=Owner.C.X+B.X;
  end

  if (C.Y<=Scr.D.Y) & (Scr.C.Y<=D.Y) & (C.X<=Scr.D.X) & (Scr.C.X<=D.X) then
    return 1;
  end

  return 0;
end

void CWindow.View()
  CMessage  Msg;
  Msg .Parm[pmMessage]= wmViewWnd;
  @Msg.From           =@self;
  Send(@Msg);
end


struct CBox (CWindow)
  void        Init    ();
  word        Show    (CScreen  @Scr) virtual;
end

void CBox.Init()
  base.Init( 0, 0, 0, 0);
  SetFlags (sfBottomAlignY2 | sfRight_AlignX2);
end

word CBox.Show(CScreen @Scr) virtual
  if base.Show(@Scr)!=0 then
    if Owner.Focus()!=0 then
      Scr.PutC(C.Y,C.X+1,C.Y,D.X-1,'Í',coBorder);
      Scr.PutC(D.Y,C.X+1,D.Y,D.X-1,'Í',coBorder);
      Scr.PutC(C.Y+1,C.X,D.Y-1,C.X,'º',coBorder);
      Scr.PutC(C.Y+1,D.X,D.Y-1,D.X,'º',coBorder);

      Scr.PutC(C.Y,C.X,C.Y,C.X,    'É',coBorder);
      Scr.PutC(C.Y,D.X,C.Y,D.X,    '»',coBorder);
      Scr.PutC(D.Y,C.X,D.Y,C.X,    'È',coBorder);
      Scr.PutC(D.Y,D.X,D.Y,D.X,    '¼',coBorder);
    else
      Scr.PutC(C.Y,C.X+1,C.Y,D.X-1,'Ä',coBorder);
      Scr.PutC(D.Y,C.X+1,D.Y,D.X-1,'Ä',coBorder);
      Scr.PutC(C.Y+1,C.X,D.Y-1,C.X,'³',coBorder);
      Scr.PutC(C.Y+1,D.X,D.Y-1,D.X,'³',coBorder);

      Scr.PutC(C.Y,C.X,C.Y,C.X,    'Ú',coBorder);
      Scr.PutC(C.Y,D.X,C.Y,D.X,    '¿',coBorder);
      Scr.PutC(D.Y,C.X,D.Y,C.X,    'À',coBorder);
      Scr.PutC(D.Y,D.X,D.Y,D.X,    'Ù',coBorder);
    end

    Scr.PutC(C.Y+1,C.X+1,D.Y-1,D.X-1,' ',coWindow);

    return 1;
  end

  return 0;
end


struct CBackground (CWindow)
  char        Ch;
  word        Color;
  void        Init    ();
  word        Show    (CScreen  @Scr) virtual;
end

void CBackground.Init()
  base.Init( 0, 0, 0, 0);
  SetFlags (sfBottomAlignY2 | sfRight_AlignX2);

  Ch=' '; // °
  Color=coDesktop;
end

word CBackground.Show(CScreen @Scr) virtual
  if base.Show(@Scr)!=0 then
    Scr.PutC(C.Y,C.X,D.Y,D.X,Ch,Color);
    return  1;
  end

  return 0;
end


struct CGroup (CWindow)
  CWindow    @List;
  word        Mouse;

  word        Init    (int y1, x1, y2, x2);
  void        Push    (CWindow  @Wnd);
  word        Post    (CMessage @Msg) virtual;
  word        Show    (CScreen  @Scr) virtual;
end

word CGroup.Init(int y1, x1, y2, x2)
  base.Init(y1,x1,y2,x2);

  @List=NULL;
  Mouse=0;
end

void CGroup.Push(CWindow @Wnd)
  @Wnd.Owner =  NULL;

  CWindow @@P=@@List;
  while @P!=NULL do
    if @P.Owner=NULL then
      @P.Owner=@self;
      return
    end

    @@P=@@P.Next;
  end

  @P        =@Wnd;
  @Wnd.Owner=@self;
  @Wnd.Next = NULL;
end

word CGroup.Post(CMessage @Msg) virtual
  select
    case Msg.Message()=wmKeyboard:
      if Msg.Parm[pmKbdCode]=0 then
        if Msg.LButton()!=0 then
          if Mouse=0 then
            CWindow @@P1=@@List;
            CWindow @@P2=  NULL;

            while @P1!=NULL do
              if P1.Pick(Msg.MouseY(),Msg.MouseX())!=0 then
                @@P2=@@P1;
              end

              @@P1=@@P1.Next;
            end

            if @@P2!=NULL then
              if @P2.Next!=NULL & (P2.Flags&sfSelectable)!=0 then
                @P1     =@P2;
                @P2     =@P2.Next;
                @P1.Next= NULL;

                View();
              end
            end

            Mouse=1;
          end
        else
          Mouse=0;
        end
      end
  end

  CWindow @P=@List;
  while @P!=NULL do
    if P.Post(@Msg)!=0 then
      return 1;
    end

    @P=@P.Next;
  end

  return base.Post(@Msg);
end

word CGroup.Show(CScreen @Scr) virtual
  word Flag=base.Show(@Scr);

  CPoint C0=Scr.C;
  CPoint D0=Scr.D;

  Scr.Clip(C.Y,C.X,D.Y,D.X);

  CPoint C1=Scr.C;
  CPoint D1=Scr.D;

  CWindow @P=@List;
  while @P!=NULL do
    Scr.C=C1;
    Scr.D=D1;

    P.Show(@Scr);

    Scr.C=C1;
    Scr.D=D1;

    if (P.Flags&sfShadowed)!=0 then
      Scr.Fill(P.C.Y+1,P.D.X+1,P.D.Y,  P.D.X+2,coShadow);
      Scr.Fill(P.D.Y+1,P.C.X+2,P.D.Y+1,P.D.X+2,coShadow);
    end

    @P=@P.Next;
  end

  Scr.D=D0;
  Scr.C=C0;

  return Flag;
end


struct CDesktop (CGroup)
  CBackground Background;
  void        Init    ();
end

void CDesktop.Init()
  base      .Init( 0, 0,23,79);
  Background.Init();

  Push(@Background);
end


struct CDialog (CGroup)
  CWindow     Title;
  CWindow     Corner;
  CBox        Box;
  CBackground Background;
  CGroup      Group;
  word        Mouse;
  word        Click;
  int         dY, dX;
  void        Init    (int y1, x1, y2, x2);
  word        Post    (CMessage @Msg) virtual;
  word        Show    (CScreen  @Scr) virtual;
end

void CDialog.Init(int y1, x1, y2, x2)
  base      .Init(y1,x1,y2,x2);

  Title     .Init( 0, 0, 0, 0); Title .SetFlags(sfRight_AlignX2);
  Corner    .Init( 0,-1, 0, 0); Corner.SetFlags(sfBottomAlignY1 | sfRight_AlignX1 | sfBottomAlignY2 | sfRight_AlignX2);
  Box       .Init();

  Group     .Init( 1, 1,-1,-1);
  Group     .SetFlags(sfBottomAlignY2 | sfRight_AlignX2);

  Background.Init();
  Background.Ch   =' ';
  Background.Color= coWindow;

  Push(@Title);
  Push(@Corner);
  Push(@Box);
  Push(@Group);

  Group.Push(@Background);

  SetFlags(sfSelectable | sfMoveable | sfSizeable | sfShadowed);

  Mouse=0;
  Click=0;
end

word CDialog.Post(CMessage @Msg) virtual
  if Focus()!=0 then
    select
      case Msg.Message()=wmKeyboard:
        if Msg.KbdCode()=0 then
          if Msg.LButton()!=0 then
            if Mouse=0 then
              select
                case Title.Pick(Msg.MouseY(),Msg.MouseX())!=0:
                  if (Flags & sfMoveable)!=0 then
                    dY   =Msg.MouseY()-C.Y;
                    dX   =Msg.MouseX()-C.X;
                    Click=1;
                  end

                case Corner.Pick(Msg.MouseY(),Msg.MouseX())!=0:
                  if (Flags & sfSizeable)!=0 then
                    dY   =D.Y-Msg.MouseY();
                    dX   =D.X-Msg.MouseX();
                    Click=2;
                  end
              end

              Mouse=1;
            end

            // Only for top/left align

            select
              case Click=1:
                int Y=Msg.MouseY()-dY;
                int X=Msg.MouseX()-dX;
                if Owner.Pick(Msg.MouseY(),Msg.MouseX())!=0 then
                  if Y!=C.Y | X!=C.X then
                    A.Init(Y-Owner.C.Y,X-Owner.C.X);
                    B.Init(A.Y+(D.Y-C.Y),A.X+(D.X-C.X));

                    CMessage Msg;
                    @Msg.From          =@Owner;
                    Msg.Parm[pmMessage]= wmViewWnd;
                    Send    (@Msg);
                  end
                end

              case Click=2:
                int Y=Msg.MouseY()+dY;
                int X=Msg.MouseX()+dX;
                if Owner.Pick(Msg.MouseY(),Msg.MouseX())!=0 then
                  if Y!=D.Y | X!=D.X then
                    if Y-C.Y>0 & X-C.X>1 then
                      B.Init(Y-Owner.C.Y,X-Owner.C.X);
                      CMessage Msg;
                      @Msg.From          =@Owner;
                      Msg.Parm[pmMessage]= wmViewWnd;
                      Send    (@Msg);
                    end
                  end
                end
            end
          else
            Click=0;
            Mouse=0;
          end
        end
    end
  end

  return base.Post(@Msg);
end

word CDialog.Show(CScreen @Scr) virtual
  if base.Show(@Scr)!=0 then
    if (Flags & sfSizeable)!=0 then
      Scr.PutS(D.Y,D.X-1,"ÄÙ",coCorner);
    end

    return 1;
  end

  return 0;
end


struct CProgram (CGroup)
  CVideoMem   Buff [25][80];
  CScreen     Scr;
  CMouse      Mouse;
  void        Init    ();
  void        GetMsg  (CMessage    @Msg);
  word        Post    (CMessage    @Msg) virtual;
  word        Send    (CMessage    @Msg) virtual;
  void        Exec    ();
  void        Done    ();
end

void CProgram.Init()
  base .Init( 0, 0,24,79);
  Scr  .Init();
  Mouse.Init();

  int Y=0;
  while Y<=24 do
    int P=80*Y;
    int X=0;
    while X<=79 do
      Buff[Y][X]=Scr.VMem[P];
      inc  P;
      inc  X;
    end

    inc Y;
  end
end

void CProgram.GetMsg(CMessage  @Msg)
  word Temp[2];

  asm mov     AH,01H
  asm int     16H
  asm lahf
  asm mov     AL,AH
  asm xor     AH,AH
  asm mov     SS:[BP-4],AX

  if (Temp[0] & $0040)=0 then
    asm mov     AH,00H
    asm int     16H
    asm mov     SS:[BP-4],AX

    if (Temp[0] & $00FF)=0 then
      Msg.Parm[pmKbdCode]=$0100 | (Temp[0]/256);
    else
      Msg.Parm[pmKbdCode]=Temp[0]%256;
    end
  else
    Msg.Parm  [pmKbdCode]=0;
  end

  asm mov     AH,0
  asm int     1AH
  asm mov     SS:[BP-2],CX
  asm mov     SS:[BP-4],DX

  Msg.Parm[pmTime_Lo]=Temp[0];
  Msg.Parm[pmTime_Hi]=Temp[1];

  Mouse.Test();

  Msg.Parm[pmMouse_Y]=Mouse.Y;
  Msg.Parm[pmMouse_X]=Mouse.X;
  Msg.Parm[pmButtons]=Mouse.Buttons;

  Msg.Parm[pmMessage]=wmKeyboard;
end

word CProgram.Post(CMessage @Msg) virtual
  select
    case Msg.Message()=wmViewWnd:
      Scr.Open();

      CWindow @P1=@Msg.From;
      CWindow @P2=@P1.Owner;
      while @P2!=NULL do
        Scr.Clip(P2.C.Y,P2.C.X,P2.D.Y,P2.D.X);
        @P2=@P2.Owner;
      end

      if P1.Show(@Scr)!=0 then
        Scr.Clip(P1.C.Y,P1.C.X,P1.D.Y,P1.D.X);
        while @P1!=NULL do
          @P2=@P1.Owner;
          @P1=@P1.Next;
          while @P1!=NULL do
            P1.Show(@Scr);
            if (P1.Flags&sfShadowed)!=0 then
              Scr.Fill(P1.C.Y+1,P1.D.X+1,P1.D.Y,  P1.D.X+2,coShadow);
              Scr.Fill(P1.D.Y+1,P1.C.X+2,P1.D.Y+1,P1.D.X+2,coShadow);
            end

            @P1=@P1.Next;
          end

          @P1=@P2;
        end

        Mouse.Hide();
        Scr  .Copy();
        Mouse.Show();
      end

      return 1;
  end

  return base.Post(@Msg);
end

word CProgram.Send(CMessage @Msg) virtual
  return Post(@Msg);
end

void CProgram.Exec()
  Show  (@Scr);
  Scr  .Copy();

  Mouse.Show();

  repeat
    CMessage Msg;
    GetMsg (@Msg);
    Post   (@Msg);
  until      Msg.Message()=wmQuit;

  Mouse.Hide();
end

void CProgram.Done()
  int Y=0;
  while Y<=24 do
    int P=80*Y;
    int X=0;
    while X<=79 do
      Scr.VMem[P]=Buff[Y][X];
      inc  P;
      inc  X;
    end

    inc Y;
  end
end


struct CClock (CDialog)
  word        Hi;
  word        Lo;
  void        Init    ();
  word        Post    (CMessage @Msg) virtual;
  word        Show    (CScreen  @Scr) virtual;
end

void CClock.Init()
  base.Init( 1,65,3,76);
  Hi=10;
  Lo=10000;

  ClrFlags(sfSizeable);
end

word str(word N; word S; char @Buff)
  if S>0 then
    dec S;
  end

  word P=0;
  if N>=10 | S>0 then
    P=str(N/10,S,@Buff);
  end

  char  @DIGIT = "0123456789";
  Buff  [P]=DIGIT[N%10];

  return P+1;
end

word CClock.Post(CMessage @Msg) virtual
  select
    case Msg.Message()=wmKeyboard:
      word lo=Msg.Parm[pmTime_Lo];
      word hi=Msg.Parm[pmTime_Hi];

      if Lo!=lo | Hi!=hi then
        Lo=lo;
        Hi=hi;
        View();
      end
  end

  return base.Post(@Msg);
end

word CClock.Show(CScreen @Scr) virtual
  if base.Show(@Scr)!=0 then
    word lo=Lo;
    word hi=Hi;
    word H =hi, M=0, S=0;

    asm mov     AX,SS:[BP-2]
    asm mov     BX,60
    asm mul     BX
    asm mov     SS:[BP-8],DX

    asm mov     AX,SS:[BP-2]
    asm mov     BX,3600
    asm mul     BX
    asm mov     AX,DX
    asm mov     DX,0
    asm mov     BX,60
    asm div     BX
    asm mov     SS:[BP-10],DX

    char Buff[32];
    str(H,2,@Buff[0]);
    str(M,2,@Buff[3]);
    str(S,2,@Buff[6]);
    Buff[2]=':';
    Buff[5]=':';
    Buff[8]=#0;

    Scr.PutS(C.Y+1,C.X+2,@Buff,coWindow);

    return 1;
  end

  return 0;
end


struct CChip (CWindow)
  char        Ch;
  word        Code;
  word        Color;
  word        Mouse;
  word        Click;
  void        Init    (int Y; int X; char ch; word code; word color);
  void        Move    (int Y; int X);
  word        Post    (CMessage @Msg) virtual;
  word        Show    (CScreen  @Scr) virtual;
end

void CChip.Init(int Y; int X; char ch; word code; word color)
  base.Init( Y, X, Y, X+2);
  Ch   =ch;
  Code =code;
  Color=color;
  Mouse=0;
  Click=0;
end

void CChip.Move(int Y; int X)
  A.Init(Y,X);
  B.Init(Y,X+2);
end

word CChip.Post(CMessage @Msg) virtual
  if Owner.Focus()!=0 then
    word Flag=0;
    select
      case Msg.Message()=wmKeyboard:
        if Msg.LButton()!=0 then
          if Mouse=0 then
            if Pick(Msg.MouseY(),Msg.MouseX())!=0 then
              Click=1;
            end
          end

          Mouse=1;
        else
          if Click!=0 & Pick(Msg.MouseY(),Msg.MouseX())!=0 then
            Msg .Parm[0]= wmPuzzle;
            Msg .Parm[1]= A.Y;
            Msg .Parm[2]= A.X;
            @Msg.From   =@self;

            Send(@Msg);

            Flag=1;
          else
            if Msg.KbdCode()=Code then
              Msg .Parm[0]= wmPuzzle;
              Msg .Parm[1]= A.Y;
              Msg .Parm[2]= A.X;
              @Msg.From   =@self;

              Send(@Msg);

              Flag=1;
            end
          end

          Click=0;
          Mouse=0;
        end

      case Msg.Message()=wmJump:
        char ch   =Ch;
        word color=Color;

        Ch   =' ';
        Color=coWindow;
        View();

        Move(Msg.Parm[1],Msg.Parm[2]);

        Ch   =ch;
        Color=color;
        View();
    end

    if Flag!=0 then
      return 1;
    end
  end

  return base.Post(@Msg);
end

word CChip.Show(CScreen @Scr) virtual
  if base.Show(@Scr)!=0 then
    Scr.PutC(C.Y,C.X,  D.Y,D.X,   ' ',Color);
    Scr.PutC(C.Y,C.X+1,C.Y,C.X+1,  Ch,Color);

    return 1;
  end

  return 0;
end


struct CPuzzle (CDialog)
  CChip       Chip[15];

  void        Init    ();
  word        Post    (CMessage @Msg) virtual;
  word        Show    (CScreen  @Scr) virtual;
end

void CPuzzle.Init()
  char @LETTERS="ABCDEFGHIJKLMNO";

  base.Init( 1, 1, 6,19);

  int I=0;
  while I<4 do
    int J=0;
    while J<4 & 4*I+J<15 do
      char Ch   =LETTERS[4*I+J];
      word Color=coWindow;
      word N=I+J;
      if N%2=1 then
        Color=coSelect;
      end

      Chip[4*I+J].Init(I,3*J,Ch,$0041+(4*I+J),Color);

      Group.Push(@Chip[4*I+J]);

      inc J;
    end

    inc I;
  end

  ClrFlags(sfSizeable);
end

word CPuzzle.Post(CMessage @Msg) virtual
  if Focus()!=0 then
    select
      case Msg.Message()=wmKeyboard:
        if Focus()!=0 & Msg.KbdCode()=$0144 then
          word N=Msg.Parm[pmTime_Lo];
          if N%2=0 then
            inc N;
          end

          int   I=0;
          while I<4096 do
            int J=N/4370;
            Msg .Parm[0]= wmPuzzle;
            Msg .Parm[1]= Chip[J].A.Y;
            Msg .Parm[2]= Chip[J].A.X;
            @Msg.From   =@Chip[J];

            N=1371*N;

            Post(@Msg);
            inc  I;
          end

          return 1;
        end

      case Msg.Message()=wmPuzzle:
        int Move [4][3];

        Move[0][0]=-1;
        Move[0][1]= 0;
        Move[0][2]= 1;
        Move[1][0]= 0;
        Move[1][1]= 3;
        Move[1][2]= 1;
        Move[2][0]= 1;
        Move[2][1]= 0;
        Move[2][2]= 1;
        Move[3][0]= 0;
        Move[3][1]=-3;
        Move[3][2]= 1;

        int Y=Msg.Parm[1];
        int X=Msg.Parm[2];

        int I=0;
        while I<15 do
          int J=0;
          while J<4 do
            if (Chip[I].A.Y=Y+Move[J][0] & Chip[I].A.X=X+Move[J][1]) then
              Move[J][2]=0;
            end

            inc J;
          end

          inc I;
        end

        int J=0;
        while J<4 do
          if !(0<=Y+Move[J][0] & Y+Move[J][0]<4) then
            Move[J][2]=0;
          end

          if !(0<=X+Move[J][1] & X+Move[J][1]<4*3) then
            Move[J][2]=0;
          end

          if Move[J][2]!=0 then
            Msg.Parm[0]=wmJump;
            Msg.Parm[1]=Y+Move[J][0];
            Msg.Parm[2]=X+Move[J][1];

            Msg.From.Post(@Msg);

            exit
          end

          inc J;
        end

        return 1;
    end
  end

  return base.Post(@Msg);
end

word CPuzzle.Show(CScreen  @Scr) virtual
  if base.Show(@Scr)!=0 then
    Scr.PutS(D.Y,D.X-5," F10 ",coBorder);
    return 1;
  end

  return 0;
end


struct CApp (CProgram)
  CDesktop    Desktop;
  CDialog     Dialog1, Dialog2;
  CPuzzle     Puzzle1, Puzzle2;
  CClock      Clock;

  void        Init    ();
  word        Post    (CMessage @Msg) virtual;
  word        Show    (CScreen  @Scr) virtual;
end

void CApp.Init()
  base   .Init();
  Desktop.Init();
  Dialog1.Init( 6,10,16,50);
  Dialog2.Init(10,14,20,54);
  Puzzle1.Init();
  Puzzle2.Init();
  Clock  .Init();

  inc Puzzle2.A.Y;
  inc Puzzle2.A.X;
  inc Puzzle2.A.X;

  inc Puzzle2.B.Y;
  inc Puzzle2.B.X;
  inc Puzzle2.B.X;

  Dialog2.Group.Push(@Puzzle1);
  Dialog2.Group.Push(@Puzzle2);

  Desktop.Push(@Dialog1);
  Desktop.Push(@Dialog2);
  Desktop.Push(@Clock);

  Push  (@Desktop);
end

word CApp.Post(CMessage @Msg) virtual
  select
    case Msg.Message()=wmKeyboard:
      if Msg.Parm[pmKbdCode]=$001B then
        Msg.Parm [pmMessage]=wmQuit;
        return    1;
      end
  end

  return base.Post(@Msg);
end

word CApp.Show(CScreen @Scr) virtual
  if base.Show(@Scr)!=0 then
    Scr.PutC(24, 0,24,79,' ',  coWindow);
    Scr.PutS(24, 1,"Esc-¢ëå®¤",coWindow);
    return 1;
  end

  return 0;
end


begin
  CApp App;

  App .Init();
  App .Exec();
  App .Done();
end
