// Program written by Maarten Meuris AKA Nyerguds
program dune_edit;
uses crt, dos;

TYPE
   chset  = array [1..8] of byte;
   coords = array [1..4] of integer;
   keymapper = Record
      up, down, left, right, space, enter, esc :char;
   end;
   option = Record
      typeid: Integer;
      {
      0 = unsigned byte (HEX)
      1 = signed byte (DEC)
      3 = 2-byte (DEC)
      4 = 4-byte (HEX)
      5 = owner
      6 = command
      7 = Movement type
      8 = weapon type
      9 = structure
      10 = unit
      }
      text: String[20];
   end;
    lev2Lst = array[0..27] of String;
    lev3Lst = array[0..49] of option;

var level: integer;
    keyread:char;
    select,scroll,max,len:array [1..4] of integer;
    inputstr:String[20];
    lev2List:lev2Lst;
    lev3List:lev3Lst;
const
    framechar:array[1..2] of chset = (
                         ($DA, $BF, $C0, $D9, $C4, $B3, $C2, $C1),
                         ($C9, $BB, $C8, $BC, $CD, $BA, $CB, $CA));
    selectBoundaries:array[1..4] of coords = (
                         (3,4,25,5), (3,9,25,23), (32,4,54,23), (17,12,38,18));

    mainlistlen:integer=1;
    mainlist:array[0..1] of String[20] = ('Units       ','Structures  ');

    unitlistlen:integer=26;
    unitlist:lev2Lst = (
             'Carryall    ','''Thopter    ','Infantry    ','Troopers    ',
             'Soldier     ','Trooper     ','Saboteur    ','Launcher    ',
             'Deviator    ','Tank        ','Siege Tank  ','Devastator  ',
             'Sonic Tank  ','Trike       ','Raider Trike','Quad        ',
             'Harvester   ','MCV         ','Death Hand  ','Rocket      ',
             'ARocket     ','GRocket     ','MiniRocket  ','Bullet      ',
             'Sonic Blast ','Sandworm    ','Frigate     ','');
    structlistlen:integer=18;
    structlist:lev2Lst = (
             'Concrete    ','Concrete4   ','Palace      ','Light Fctry ',
             'Heavy Fctry ','Hi Tech     ','IX          ','WOR         ',
             'Const Yard  ','Windtrap    ','Barracks    ','Starport    ',
             'Refinery    ','Repair      ','Wall        ','Turret      ',
             'R-Turret    ','Spice Silo  ','Outpost     ','','','','','','','','','');

    unitoptsListlen:integer=46;
    unitOptsList:lev3Lst = (
              (typeid: 3; text: 'Short name ID       '),
              (typeid: 4; text: 'Name code ref.      '),
              (typeid: 2; text: 'Long name ID        '),
              (typeid: 4; text: 'WSA file code ref.  '),
              (typeid: 3; text: 'Unknown 005         '),
              (typeid: 3; text: 'Unknown 006         '),
              (typeid: 3; text: 'Hit points          '),
              (typeid: 3; text: 'Unknown 008         '),
              (typeid: 3; text: 'Sidebar icon gfxID  '),
              (typeid: 3; text: 'Cost                '),
              (typeid: 3; text: 'Build Time          '),
              (typeid: 3; text: 'Tech level          '),
              (typeid: 9; text: 'Prerequisites       '),
              (typeid: 3; text: 'Unknown 014         '),
              (typeid: 1; text: 'Unknown 015         '),
              (typeid: 1; text: 'Upgrades needed     '),
              (typeid: 6; text: 'Sidebar command #1  '),
              (typeid: 6; text: 'Sidebar command #2  '),
              (typeid: 6; text: 'Sidebar command #3  '),
              (typeid: 6; text: 'Sidebar command #4  '),
              (typeid: 3; text: 'Unknown 021         '),
              (typeid: 1; text: 'Unknown 022         '),
              (typeid: 3; text: 'Unknown 023         '),
              (typeid: 3; text: 'Unknown 024         '),
              (typeid: 5; text: 'Owner               '),
              (typeid: 3; text: 'Unknown 026         '),
              (typeid: 3; text: 'Unknown 027         '),
              (typeid: 1; text: 'Unknown 028         '),
              (typeid: 1; text: 'Unknown 029         '),
              (typeid: 1; text: 'Unknown 030         '),
              (typeid: 1; text: 'Unknown 031         '),
              (typeid: 3; text: 'Sight               '),
              (typeid: 7; text: 'Movement type       '),
              (typeid: 3; text: 'Unknown 034         '),
              (typeid: 3; text: 'Speed               '),
              (typeid: 3; text: 'Unknown 036         '),
              (typeid: 3; text: 'Unit gfxID          '),
              (typeid: 3; text: 'Turret gfxID        '),
              (typeid: 3; text: 'Unknown 039         '),
              (typeid: 3; text: 'Unknown 040         '),
              (typeid: 3; text: 'Unknown 041         '),
              (typeid: 3; text: 'Weapon rate of fire '),
              (typeid: 3; text: 'Weapon range        '),
              (typeid: 3; text: 'Weapon damage       '),
              (typeid: 3; text: 'Unknown 045         '),
              (typeid: 8; text: 'Weapon type         '),
              (typeid: 3; text: 'Unknown 047         '),
              (typeid: 0; text: ''),
              (typeid: 0; text: ''),
              (typeid: 0; text: ''));

      keymap:keymapper = (up:    char(72);
                          down:  char(80);
                          left:  char(75);
                          right: char(77);
                          space: char(32);
                          enter: char(13);
                          esc:   char(27));

procedure cntin;
var pause: char;
begin
   write('Press any key to continue.');
   repeat until keypressed;
    pause:=ReadKey();
end; {cntin}

procedure drawLine(x,y,len:integer; c:char);
var count:integer;
begin
   for count:=0 to (len-1) do
    begin
      gotoxy(x,y+count);
      write(c);
    end;
end;

procedure drawFrame(x,y,w,h,m:integer; active,fill:boolean );
var charset,count,count2:integer;
begin
   if active then charset:=2
   else charset:=1;
   gotoxy(x,y);
   write(char(framechar[charset,1]));
   for count:=1 to (w-2) do
    write(char(framechar[charset,5]));
   write(char(framechar[charset,2]));
   for count:=1 to (h-2) do
    begin
      gotoxy(x,y+count);
      write(char(framechar[charset,6]));
      if fill then for count2:=1 to (w-2) do write(' ');
      gotoxy(x+w-1,y+count);
      write(char(framechar[charset,6]));
    end;
   gotoxy(x,y+h-1);
   write(char(framechar[charset,3]));
   for count:=1 to (w-2) do
    write(char(framechar[charset,5]));
   write(char(framechar[charset,4]));

   if (m>1) then
    begin
      gotoxy(x+m-1,y);
      write(char(framechar[charset,7]));
      gotoxy(x+m-1,y+h-1);
      write(char(framechar[charset,8]));
      drawLine(x+m-1,y+1,h-2,char(framechar[charset,6]));
    end;
   gotoxy(0,0);
end; {drawFrame}

procedure drawSelect(l:integer);
begin
  if (l<>4) or (l=level) then
   begin
     drawLine(selectBoundaries[l][1],selectBoundaries[l][2],max[l]+1,' ');
     gotoxy(selectBoundaries[l][1],selectBoundaries[l][2]+select[l]);
     write('[');
     drawLine(selectBoundaries[l][3],selectBoundaries[l][2],max[l]+1,' ');
     gotoxy(selectBoundaries[l][3],selectBoundaries[l][2]+select[l]);
     write(']');
   end;
end;

procedure drawSelect();
begin
  drawSelect(level);
end;

procedure drawSelects();
var i:integer;
begin
  for i:=1 to 4 do drawSelect(i);
end;


function openFile(filename:String ) : file;
var
  status:integer;
begin
  assign(openFile,filename);
  {$I-}
  reset(openFile,1);
  status:=IOResult;
  if status<>0 then
   begin
    writeln();
    writeln('Error opening "',filename,'"');
    {$I+}
   end;
end; {openFile}

procedure closeFile(f:file);
begin
  close(f);
  {$I+}
end; {closeFile}

procedure drawLev3();
var i,last: integer;
    showlist:array [0..19] of String[20];
begin
  if len[3]<max[3] then last:=len[3]
  else last:=max[3];
  for i:=0 to last do
  begin
    showlist[i]:=lev3List[(i+scroll[3])].text;
  end;
  for i:=0 to last do
  begin
    gotoxy(selectBoundaries[3][1]+2,selectBoundaries[3][2]+i);
    write(showlist[i]);
  end;
end;

procedure drawLev2();
var i,last: integer;
    showlist:array [0..19] of String[20];
begin
  if len[2]<max[2] then last:=len[2]
  else last:=max[2];
  for i:=0 to last do
  begin
    showlist[i]:=lev2List[(i+scroll[2])];
  end;
  for i:=0 to last do
  begin
    gotoxy(selectBoundaries[2][1]+2,selectBoundaries[2][2]+i);
    write(showlist[i]);
  end;
end;

procedure drawLev1();
var i,last: integer;
    showlist:array [0..2] of String[20];
begin
  if len[1]<max[1] then last:=len[1]
  else last:=max[1];
  for i:=0 to last do
  begin
    showlist[i]:=mainlist[(i+scroll[1])];
  end;
  for i:=0 to last do
  begin
    gotoxy(selectBoundaries[1][1]+2,selectBoundaries[1][2]+i);
    write(showlist[i]);
  end;
end;


procedure drawList(l:integer);
begin
    if l=1 then drawLev1()
    else if l=2 then
     begin
       if select[1]=0 then
        begin
           len[2]:=UnitListLen;
           lev2List:=UnitList;
         end
        else if select[1]=1 then
         begin
           len[2]:=StructListLen;
           Lev2List:=StructList;
         end;
       drawLev2();
     end
    else if l=3 then
     begin
       lev3List:=unitOptsList;
       len[3]:=unitOptsListLen;
       drawLev3();
     end;
end;

procedure drawList();
begin
   drawList(level);
end;

procedure drawLists();
var i:integer;
begin
   for i:=1 to 3 do drawList(i);
end;


procedure updateLists();
var i: integer;
begin
  for i:=2 to 3 do
    begin
      scroll[i]:=0;
      select[i]:=0;
      drawSelect(i);
      drawList(i);
    end;
end;


procedure MoveDown(level:integer);
begin
  if (select[level]=max[level]) and ((len[level]-max[level])>0) and (scroll[level]<(len[level]-max[level])) then
     begin
      scroll[level]:=scroll[level]+1;
      drawList();
     end
  else if (select[level]<>max[level]) and (select[level] < len[level])then
    select[level]:=select[level]+1;
  if level=1 then updateLists();
end;

procedure MoveUp(level:integer);
begin
  if (select[level]=0) and (scroll[level]>0) then
     begin
      scroll[level]:=scroll[level]-1;
      drawList();
     end
  else if select[level]<>0 then
    select[level]:=select[level]-1;
  if level=1 then updateLists();
end;


procedure init();
var i: integer;
begin
  for i:=1 to 25 do
  begin
    gotoxy(1,i);
    clreol;
  end;
  gotoxy(1,1);
  for i:=1 to 80 do
    write(char(framechar[2,5]));
  gotoxy(3,1);
  write(' Dune II Editor ');
  gotoxy(65,1);
  write(' by Nyerguds ');
  level:=1;
  len[1]:=mainlistlen;
  len[2]:=unitListLen;
  len[3]:=unitOptsListLen;
  len[4]:=0;
  for i:=1 to 4 do
  begin
   select[i]:=0;
   scroll[i]:=0;
   max[i]:=selectBoundaries[i][4]-selectBoundaries[i][2];
   drawList(i);
   drawSelect(i);
  end;
end;

procedure drawLayout();
begin
   drawFrame(1,3,27,4,-1,level=1,false);
   drawFrame(1,8,27,17,-1,level=2,false);
   drawFrame(30,3,50,22,27,level=3,false);
   if level=4 then
   begin
    drawFrame(15,5,52,15,-1,true,true);
    gotoxy(17,6);
    write(lev2list[select[2]+scroll[2]]);
    gotoxy(17,7);
    write(lev3list[select[3]+scroll[3]].text);
    gotoxy(selectBoundaries[4][1]+2,selectBoundaries[4][2]);
    write('value');
    drawSelect();
    gotoxy(selectBoundaries[4][1]+2,selectBoundaries[4][2]);
    readln(inputstr);
   end;
   drawSelect();
   gotoxy(1,25);
end;


procedure clearWorkArea();
var i: integer;
begin
    for i:=3 to 25 do
      begin
        gotoxy(1,i);
        clreol;
      end;
      drawLists();
      drawLayout();
      drawSelects();
end;

procedure input();
begin
 if level<>4 then
  begin
   if (keyread=keymap.ESC) then keyread:=char(255)
   else if (level=3) and (keyread=keymap.ENTER) or (keyread=keymap.SPACE) then level:=4
   else if (level<>3) and (keyread=keymap.RIGHT) then level:=level+1
   else if (level=3) and (keyread=keymap.RIGHT) then level:=1
   else if (level<>1) and (keyread=keymap.LEFT) then level:=level-1
   else if (level=1) and (keyread=keymap.LEFT) then level:=3
   else if (keyread=keymap.DOWN) then MoveDown(level)
   else if (keyread=keymap.UP) then   MoveUp(level);
  end
 else
  begin
   if (keyread=keymap.DOWN) then MoveDown(level)
   else if (keyread=keymap.UP) then MoveUp(level);
   if (keyread=keymap.ESC) and (level=4) then begin level:=level-1; clearWorkArea(); end;
  end;

end;

begin
  init();
  drawLayout();
  keyread:=char(0);
    while keyread <> char(255) do
    begin
      keyread:=ReadKey();
      gotoxy(1,25);
      input();
      drawLayout();
    end;
end.
