#include <textmenu.h>
#include <function.h>

#include <string.h>
#include <stdlib.h> /*ultoa*/
#include <dir.h>
#include <bios.h>

#define MEM_FILES 100
 //Величина порции, которыми выделяется память под список файлов
char Sector[512]; //!!! Сектор диска для biosdisk
#define PANEL_PATH 38
 //Длина пути, который помещается в панели
void itoa99 (MODE n, MODE *str) {
 //Число из интервала [0,99] в строку 2 байта
 str[0]=n/10+'0'; str[1]=n%10+'0';
}

void GetDateItem (MODE *Buffer,MODE Day,MODE Mon,MODE Year) {
 //Дату в строку вида DD/MM/YY
 itoa99 (Day,  &Buffer[0]); itoa99 (Mon,  &Buffer[3]);
 itoa99 (Year, &Buffer[6]); Buffer[2]=Buffer[5]='/';
}

void GetTimeItem (MODE *Buffer,MODE Hour,MODE Min) {
 //Время в строку вида HH:MM
 itoa99 (Hour, &Buffer[0]); itoa99 (Min,  &Buffer[3]);
 Buffer[2]=':';
}

void GetCatalogItem (char *Buffer,struct ffblk *ffblk_) {
 //Создать элемента каталога
 int i,Year;
 MODE Day,Mon,c,attr;
 attr=ffblk_->ff_attrib;
 strcpy (Buffer,ffblk_->ff_name);
 if (attr!=0x10) tolower_ (Buffer);
 LeftSide (Buffer,12);
 i=ffblk_->ff_fdate;
 Day=i&0x001f;
 Mon=(i&0x01e0)>>5;
 Year=1980+((i&0xfe00)>>9);
 c=Year%100;
 GetDateItem (&Buffer[13],Day,Mon,c);
 i=ffblk_->ff_ftime;
 Day=(i&0xf800)>>11; //Hour
 Mon=(i&0x07e0)>>5; //Min
 GetTimeItem (&Buffer[22],Day,Mon);
 Buffer[21]=Buffer[27]=' ';
 if (attr==0x10) strcpy (&Buffer[28],"Каталог");
 else ultoa (ffblk_->ff_fsize,&Buffer[28],10);
 RightSide (&Buffer[28],10);
 Buffer[38]='\0';
 Buffer[39]=attr;
}

void GetShortPath (MODE *ShortPath,MODE *FullPath) {
 //Получить из полного пути FullPath короткий ShortPath[PANEL_PATH]
 MODE i,*ptr;
 i=strlen (FullPath);
 if (i<=PANEL_PATH) strcpy (ShortPath,FullPath);
 else {
  ptr=strchr (FullPath,'\\');
  *ptr='\0';
  strcpy (ShortPath,FullPath);
  *ptr='\\';
  strcat (ShortPath,"\\...");
  ptr=strrchr (FullPath,'\\');
  strcat (ShortPath,ptr);
 }
}

void WritePanelItem (MODE *Buffer,MODE Row,MODE Col,MODE Attr) {
 //Вывести элемент панели
 MODE *Dummy="12345678.123\0";
 MODE  attr=Buffer[39],c,i,j,Is;
 for (i=0; i<12; i++) Dummy[i]=' ';
 if (attr&0x07) Dummy[8]='░';
 if (attr&0x10 && Buffer[0]=='.' && Buffer[1]=='.') {
  Dummy[0]=Dummy[1]='.';
 }
 else for (i=0,Is=NO; ; i++) {
  c=*(Buffer+i);
  if (c=='\0') break;
  else if (c=='.') { Is=YES; j=0; }
  else {
   if (Is) { Dummy [9+j]=c; j++; }
   else Dummy[i]=c;
  }
 }
 Dummy[12]='\0';
 write_string (Row,Col,Dummy,Attr);
}

void GetItemName (MODE *FullPath,int CurLen,MODE *Buffer,int Cur) {
 //Получить имя выбранного элемента в FullPath
 if (FullPath[CurLen-1]!='\\') {
  FullPath[CurLen]='\\'; FullPath[CurLen+1]='\0';
 }
 strcat (FullPath,&Buffer[40*Cur]);
 RightMargin (FullPath);
}

void SelectDrive (MODE AllDisk,MODE *Disks,MODE *CurDisk) {
 //Окно выбора диска с проверкой готовности a:, b:
 MODE *Bar=
  " $ESC$ Отказ  $F1$ Помощь  $LEFT$ Влево  $RIGHT$ Вправо  $ENTER$ Выбор";
 KEY  Bar_Keys[]={ESC,F1,LEFT,RIGHT,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,34,48,60},Num_Keys=5;
 MODE y1,y2,x1=10,x2=14,i,rev,attr,Curs,r,c;
 i=(AllDisk<<1)+3;
 y1=(80-i)>>1;
 y2=(80+i)>>1;
 if (y2-y1<21) y2=y1+21;
 save (x1,y1,x2,y2);
 rev=CYAN;
 ATTR=attr=set_attr (BLACK, LIGHTGRAY);
 Fun_Bar (Bar);
 FILLER=' ';
 clear (x1,y1,x2,y2);
 ATTR=attr;
 box (x1+1,y1+1,x2-1,y2-1,0x02);
 center_string (x1+1,y1+1,y2-1," Выберите диск: ");
 CURSOR_OFF();
 for (i=0; i<AllDisk; i++) {
  write_char (x1+2,y1+3+(i<<1),Disks[i]-32);
  if (*CurDisk+'a'==Disks[i]) Curs=i;
 }
 CURSOR_ON(); attr=LIGHTGRAY;
 while (1) {
  change_attr (x1+2,y1+3+(Curs<<1),x1+2,y1+3+(Curs<<1),rev);
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (Num_Keys,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (Mouse_In_Field (x1+2,y1+3+(Curs<<1), 1,1, AllDisk,1, 1, 0, &r, &c)) {
     change_attr (x1+2,y1+3+(Curs<<1),x1+2,y1+3+(Curs<<1),attr);
     Curs=c;
     change_attr (x1+2,y1+3+(Curs<<1),x1+2,y1+3+(Curs<<1),rev);
     Set_Key (1,ENTER);
     break;
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
  change_attr (x1+2,y1+3+(Curs<<1),x1+2,y1+3+(Curs<<1),attr);
  switch (sim) {
   case ESC: goto OUT;
   case F1: Help (ABOUT_SELECTDRIVE); Fun_Bar(Bar); break;
   case ENTER: *CurDisk=Curs; goto OUT;
   case LEFT:
    if (Curs==0) Curs=AllDisk-1;
    else Curs--;
   break;
   case RIGHT:
    if (Curs==AllDisk-1) Curs=0;
    else Curs++;
   break;
   case HOME: Curs=0; break;
   case END: Curs=AllDisk-1; break;
   default:
    if ((c=Find_Key ("aAфФbBиИcCсСdDвВeEуУfFаАgGпПhHрРiIшШjJоОkKлЛlLдДmMьЬ"
		     "nNтТoOщЩpPзЗqQйЙrRкКsSыЫtTеЕuUгГvVмМwWцЦxXчЧyYнНzZяЯ",
		     sim))!=NO_SIM) {
     for (i=0; i<AllDisk; i++) if (Disks[i]-'a'==c) {
      Curs=i;
      Set_Key (1,ENTER);
     }
    }
   break;
  }
 }
OUT:
 restore (x1,y1,x2,y2);
 return;
}

void SortFiles (MODE *Buffer,int All) {
 // Сортирует по расширению, каталоги вперед
 MODE *Ptr0="1234567890123456789012345678901234567890",
  *Ptr1,*Ptr2,*Ptr11,*Ptr22;
 MODE *Ext1="111",*Ext2="222";
 int i,j,k;
 if (Buffer[0]=='.' && Buffer[1]=='.') i=1; // .. всегда первый
 else i=0;
 for (; i<All; i++) {
  Ptr1=&Buffer[40*i];
  Ptr11=strchr (Ptr1,'.');
  if (Ptr11!=NULL) {
   Ptr11++;
   memcpy (Ext1, Ptr11, 3);
  }
  else Ext1[0]='\0';
  for (j=i+1; j<All; j++) {
   Ptr2=&Buffer[40*j];
   Ptr22=strchr (Ptr2,'.');
   if (Ptr22!=NULL) {
    Ptr22++;
    memcpy (Ext2, Ptr22, 3);
   }
   else Ext2[0]='\0';
   k=strnicmp(Ext2,Ext1,3);
   if ( (*(Ptr2+39)&0x10) && (!((*(Ptr1+39)&0x10))) || //КАТАЛОГ ПОСЛЕ НЕ КАТАЛОГА
	(k<0) || //ПО РАСШИРЕНИЮ
	(k==0 && strnicmp(Ptr2,Ptr1,3)<0)){//МЕНЬШАЯ СТРОКА ПОСЛЕ БОЛЬШЕЙ
    memcpy (Ptr0,Ptr1,40);
    memcpy (Ptr1,Ptr2,40);
    memcpy (Ptr2,Ptr0,40);  //ПЕРЕСТАВЛЯЕМ
    memcpy (Ext1,Ext2,3);
   }
  }
 }
}

MODE WildName (MODE *SubName, MODE *SubMask) {
 //Разбор масок
 MODE i,j,c,Next,AlMask=NO,AlName=NO;
 for (i=0,j=0; ; ) {
  c=*(SubMask+i);
  switch (c) {
   case '*': return YES;
   case '?': goto NEXT_CHAR;
   default:
    if (*(SubName+j)!=c) return NO;
NEXT_CHAR:
    if (!AlMask) {
     Next=*(SubMask+i+1);
     if (Next=='\0') AlMask=YES;
     i++;
    }
    if (!AlName) {
     Next=*(SubName+j+1);
     if (Next=='\0') AlName=YES;
     j++;
    }
//    if ((AlMask==NO)&&(AlName==YES))
    if ((AlMask==YES)&&(AlName==NO)) return NO;
    else if (AlMask && AlName) return YES;
   break;
  }
 }
}

MODE WildCard (MODE *Name,MODE *Mask) {
 //YES, если имя файла Name попадает под Mask
 MODE *Ptr1,*Ptr2,Ext1=YES,Ext2=YES,Ret1;
 Ptr1=strchr (Name,'.');
 if (Ptr1==NULL)  Ext1=NO;
 else *Ptr1='\0';
 Ptr2=strchr (Mask,'.');
 if (Ptr2==NULL)  Ext2=NO;
 else *Ptr2='\0';
 Ret1=WildName (Name,Mask);
 if (Ext1) { *Ptr1='.'; Ptr1++; }
 if (Ext2) { *Ptr2='.'; Ptr2++; }
 if (Ret1) {
  if (Ext1 && Ext2) Ret1=WildName (Ptr1,Ptr2);
  else if (Ext1 != Ext2) Ret1=NO;
 }
 return (Ret1);
}

MODE Filer (MODE *Mask, MODE *Name, MODE Mode) {
 // Mode=1 - вернуть полный путь
 // Возвращает 0, если файл выбран
 //            1, нет памяти
 //	       2, нет ни одного файла
 //	       3, ошибка доступа к каталогу
 //	       4, ошибка доступа к диску
 //          255, отказ
 MODE *Bar=
  " $ESC$ Отказ  $F1$ Помощь  $DOWN$ Вниз  $UP$ Вверх  $F2$ Диск  $ENTER$ Выбор";
 KEY  Bar_Keys[]={ESC,F1,DOWN,UP,F2,ENTER},sim;
 MODE Pos_Keys[]={0,11,22,33,43,52,64},beg;
 struct ffblk ffblk_;
 int AllFile,i,j,Next,Start,Cur,CurLen;
 MODE Disks[26],AllDisk,Portion,Ret,attr,rev,r,c,Disk,*Buffer;
 MODE x1=5,x2=20,y1=20,y2=59,Rows=12,Cols=3,Row=0,Col=0,LastRow,LastCol,OnPage;
 MODE FullPath[MAX_PATH], ShortPath[PANEL_PATH], GotoName[13], GotoFind=NO,
  Search=YES,row,col;
 MODE *Ptr;
 WINDOW NoDrive = {
  11,30,16,50," Внимание! ",
  " Диск X: $не готов$! ", YELLOW, LIGHTRED, CYAN,
  NO_HELP, YES, 0x00};

 // Look for available disks:
 Disk=getdisk(); AllDisk=0;
 for (i=0;i<26;++i) {
  setdisk (i); // Try to set this disk;
  if (i==getdisk()) { // it is available
   Disks[AllDisk]=i+'a';
   AllDisk++;
  }
 }
 setdisk (Disk);

 AllFile=100; Portion=0;
 Buffer=(MODE *)malloc(40*MEM_FILES);
 if (Buffer==NULL) { Ret=0x01; goto OUT; }

 save (x1,y1,x2,y2);
 rev=set_attr (BLACK, CYAN);
 ATTR=attr=set_attr (LIGHTCYAN, BLUE);
 Fun_Bar (Bar);
 toupper_(Mask);
AGAIN:
 FILLER=' ';ATTR=attr;
 clear (x1,y1,x2,y2);
 box (x1,y1,x2,y2,0x02);
 HorizLine (x1+13,y1,y2,LINE_212);
 VertLine (x1,x2-2,y1+13,LINE_211);
 VertLine (x1,x2-2,y1+26,LINE_211);
 beg=DrawVertBar (x1,x2,y2);

 if (Search==NO) goto DRAW_PATH;

 if (getcwd (FullPath, MAX_PATH)==NULL) {
  Ret=0x03; goto OUT;
 }
 CurLen=strlen(FullPath);
 GetShortPath (ShortPath,FullPath);
 OnPage=Rows*Cols;
DRAW_PATH:
 ATTR=set_attr (BLACK, LIGHTGRAY);
 center_string (x1,y1+1,y2-1,ShortPath);
 ATTR=attr;
 if (Search==NO) goto DRAW_PAGE;

 Search=NO; Buffer[0]='\0';

FIND_DIRS:
 if (CurLen<4) j=0; //Ссылки .. нет
 else j=1; //Место для ссылки ..
 i=findfirst ("*.*", &ffblk_, 0x37);
 for (; ; ) {
  if (j==AllFile) {
   Portion++; AllFile+=100;
   Buffer=(MODE *)realloc(Buffer, 40*(Portion+1)*MEM_FILES);
   if (Buffer==NULL) { Ret=0x01; goto OUT; }
  }
  Ptr=&ffblk_.ff_name[0];
  if (*Ptr=='.') {
   if (*(Ptr+1)=='.') { //Ссылка вверх
    if (CurLen>3) GetCatalogItem (Buffer,&ffblk_); //0 элемент это ..
   }
  }
  else if (ffblk_.ff_attrib==0x10 || WildCard(Ptr,Mask)) {
   GetCatalogItem (&Buffer[40*j],&ffblk_);
   j++;
  }
  i=findnext (&ffblk_);
  if (i) break;
 }
 Next=j; // Текущая позиция в буфере

 SortFiles (Buffer,Next); //Сортируем файлы по расширению
 Row=0; Col=0; Start=0;
 if (GotoFind) { //Ищем элемент, с которого поднялись
  for (j=0; j<Next; j++) {
   Ptr=&Buffer[40*j];
   i=strpos_ (Ptr,' ');
   Push (Ptr+i);
   if (!stricmp (Ptr,GotoName)) Start=j;
   Pop (Ptr+i);
   if (Start) break;
  }
  GotoFind=NO;
 }
DRAW_PAGE:
 row=0; col=0; //Строка и столбец на экране
 for (j=Start; j<Next; j++) {
  WritePanelItem (&Buffer[40*j],x1+1+row,y1+1+col*13,attr);
  if (row<Rows-1) row++;
  else {
   row=0;
   if (col<Cols-1) col++;
   else break;
  }
 }
// End=j;
 LastRow=(row==0?(Next==0?1:Rows):row);
 LastCol=(row==0?(Next==0?1:(Next<=OnPage?col:col+1)):col+1); //!!!

 while (1) {
  Cur=Start+Col*Rows+Row;
  WritePanelItem (&Buffer[40*Cur],x1+1+Row,y1+1+Col*13,rev);
  beg=ChangeVertBar (Cur,Next,beg,x1,x2,y2);
  Buffer[40*Cur+12]=' ';
  write_string (x2-1,y1+1,&Buffer[40*Cur],0x1f);
  Buffer[40*Cur+12]='\0';
  if (Mouse) while (!Hit_Key()) {
   ms_state ();
   if (buttons==0x01) { //Left
    if (Fun_Bar_Keys (6,Bar_Keys,Pos_Keys)) goto KEYBOARD;
    if (MouseVertBar (x1,x2,y2)) goto KEYBOARD;
    if (Mouse_In_Field (x1+1,y1+1, 1,12, 3,12, 1, 0, &r, &c)) {
     if (!(c==LastCol-1 && r>LastRow-1)) {
      WritePanelItem (&Buffer[40*Cur],x1+1+Row,y1+1+Col*13,rev);
      if (Row==r && Col==c) sim=ENTER;
      else sim=S_BAR;
      Row=r; Col=c;
      goto SWITCHER;
     }
    }
   }
  }
KEYBOARD:
  sim=Get_Key();
SWITCHER:
  WritePanelItem (&Buffer[40*Cur],x1+1+Row,y1+1+Col*13,attr);
  switch (sim) {
   case F1: //!!!
    Help (ABOUT_FILER);
    Fun_Bar(Bar);
   break;
   case ESC:
    strcpy (Name,FullPath);
    strcat (Name,"\\");
    Ret=NO_SIM; goto OUT;
   case ENTER:
    if (Cur==0 && CurLen>3) { //Ссылка вверх
     Ptr=strrchr (FullPath,'\\');
     strcpy (GotoName, Ptr+1); //Запоминаем, откуда поднялись
     if (Ptr) {
      if (*(Ptr-1)==':') Ptr++;
      *Ptr='\0';
      Search=YES;
      if (chdir(FullPath)) { Ret=0x03; goto OUT; }
      GotoFind=YES; goto AGAIN;
     }
    }
    else if (Buffer[40*Cur+39]==0x10) { //Имя каталога
     GetItemName (FullPath,CurLen,Buffer,Cur);
     if (chdir(FullPath)) { Ret=0x03; goto OUT; }
     Search=YES;
     goto AGAIN;
    }
    else {
     if (Mode) { // Вернуть полный путь
      GetItemName (FullPath,CurLen,Buffer,Cur);
      strcpy (Name,FullPath);
     }
     else {
      strcpy (Name,&Buffer[40*Cur]);
      RightMargin(Name);
     }
     Ret=0x00; //Файл выбран
     goto OUT;
    }
   case HOME: Start=0; Row=0; Col=0;
    if (Next>=OnPage) goto AGAIN;
   break;
   case END:
    Start=Next-OnPage;
    if (Start<0) Start=0;
    if (Next>=OnPage) { Row=Rows-1; Col=Cols-1; goto AGAIN; }
    else { Row=LastRow-1; Col=LastCol-1; break; }
   case PAGE_DO:
    if (Next<OnPage) { //Как END
     Row=LastRow-1; Col=LastCol-1;
     break;
    }
    else {
     Start+=(OnPage-1);
     if (Start>Next-OnPage) Start=Next-OnPage;
     Row=Rows-1; Col=Cols-1;
     goto AGAIN;
    }
   case PAGE_UP:
    if (Next<OnPage) { //Как HOME
     Row=0; Col=0;
     break;
    }
    else {
     Start-=(OnPage-1);
     if (Start<0) Start=0;
     Row=0; Col=0;
     goto AGAIN;
    }
   case CTRL_UP: //В родительский каталог
    if (CurLen>3) { //Не головной каталог
     Set_Key (2,HOME,ENTER);
    }
   break;
   case DOWN:
    if (Col==LastCol-1) {
     if (Row<LastRow-1) Row++;
     else if (Start<Next-OnPage) {
      Start++;
      //if (!(Start<Next-OnPage-1)) Row=0;
      goto AGAIN;
     }
    }
    else {
     if (Row==Rows-1) {
      Col++; Row=0;
     }
     else Row++;
    }
   break;
   case RIGHT:
    if (Col<LastCol-1) {
     Col++;
     if (Col==LastCol-1 && Row>LastRow-1) Row=LastRow-1;
    }
    else Set_Key (1,PAGE_DO);
   break;
   case UP:
    if (Col==0) {
     if (Row>0) Row--;
     else if (Start>0) {
      Start--; goto AGAIN;
     }
    }
    else {
     if (Row==0) {
      Col--; Row=Rows-1;
     }
     else Row--;
    }
   break;
   case LEFT:
    if (Col>0) Col--;
    else Set_Key (1,PAGE_UP);
   break;
   case F2:
SEL_DRIVE:
    row=Disk;
    SelectDrive (AllDisk,Disks,&Disk); //Вернули номер в списке дисков
    Fun_Bar(Bar);
    Disk=Disks[Disk]-'a'; //Получилм номер диска
    if (Disk!=row) {
     if (Disk<2) { // Проверить, вставлена ли дискета
      i = biosdisk(4,Disk,0,0,0,1,Sector);
      i &= 0x02;
      if (!i) { //Не готов
       NoDrive.str[6]=Disk+'A';
       r=MessageText (&NoDrive);
       if (r) goto SEL_DRIVE;
       else { Ret=0x04; goto OUT; }
      }
     }
     setdisk (Disk);
     FullPath[0]='a'+getdisk();
     FullPath[1]=':';    FullPath[2]='\\';    FullPath[3]='\x0';
     Search=YES;
     goto AGAIN;
    }
   break;
   default: break;
  }
 }
OUT:
 free (Buffer);
 restore (x1,y1,x2,y2);
 toupper_(Name);
 return Ret;
}
