#include <conio.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

//==== Работа с "собранным файлом"

 

//#include <sys\stat.h>

//====

 

//  цвет фона

#define colorfon BLACK

//  цвет текста

#define colortxt LIGHTGRAY

//  цвет выделенной строки в меню

#define color2 CYAN

//  цвет строки поддсказки

#define color1 RED

//  цвет "тела" меню

#define colormenu YELLOW

 

 

// стартовая позиция на экране

#define str_begin 1

#define row_begin 1

 

// максимальная длина строк - вопрос/вариант ответа

#define l_str_max 30

// максимальное количество строк вопроса

#define k_str_max 5

 

// структура констант текстового режима

   struct text_info ti;

 

// запрос текущего текстового режима заполнение структуры ti

int help_txt(void);

 

// программа "диалог"

int step_d( char *);

 

// очистить экран

void clear(void);

 

// вывод в "нормальном" цвете

void n_cputs(int, int, char *);

 

// вывод в "подсвеченном" цвете

void v_cputs(int, int, char *);

 

// вывод в "СПРАВОЧНОМ" цвете

void s_cputs(int, int, char *);

 

//  вывод в справочном цвете с "закраской" всей строки вывода

void h_cputs(int, int, char *);

 

// модель функциональных частей

int fun_fun(int);

 

// чтение данных из файла - читается Запись, но возращается С строка в buf

// длиной не более l_str_max  из файла h

// в конце файла возращается NULL

char * mfgets(char *, int, FILE * );

 

char * mfgets( char* buf, int l_str, FILE* h)

{

  char c;

  int l=0, buf_l=0;

   c=fgetc(h);

   if (c == EOF ) return NULL;

   while (  (c != '\n') && (c != '\r') && (c != EOF) )

      {

       if ( (l++) <  (l_str-1) ) buf[buf_l++]=c;

       c=fgetc(h);

      };

   buf[buf_l]='\0';

   return buf;

};

 

 

 

//=========================================================

// модель функциональных частей -----начало программы

int fun_fun(int n)

{  char *fun_menu[] = {

"функциональная часть № 1  Страуступа",

"функциональная часть № 2  Бадда",

"функциональная часть № 3  Буча",

"функциональная часть № 4  вводный курс",

"функциональная часть № 5  язык JAVA",

"функциональная часть № 6  JAVA скрипт",

"функциональная часть № 7  Кернигана",

"функциональная часть № 8  Справочник С++",

"функциональная часть № 9  Функции С++",

"функциональная часть № 10 Нортона",

"функциональная часть № 11 Фигурнова",

"функциональная часть № 12 Таненбаума",

"функциональная часть № 13 Столлингса",

"функциональная часть № 14 Олифера",

"функциональная часть № 15 реализациях",

"функциональная часть № 16 алгоритмах",

"функциональная часть № 17 книгу Бека",

"функциональная часть № 18 конспект лек.",

"функциональная часть № 19 справочник",

"функциональная часть № 20 Бредли",

"функциональная часть № 21 Джурден"  };

   textbackground(color1);

   gotoxy(row_begin,ti.winbottom-1);

   clreol();

   cprintf("*** Работа %d функциональной части ...", n);

   gotoxy(row_begin,ti.winbottom);

   clreol();

   cprintf("*** %s ***", fun_menu[n-1]);

   textbackground(colorfon);

   getchar();

   return 0;

};

// модель функциональных частей -----конец программы

 

// главная программа ------ начало

int main(void)

{

   int kod;           // код возврата

   help_txt();       // определить текущий текстовый режим

   while ( 1 )

    {

      clear();

      //@@@@@

      kod=step_d("menu_link.txt");    // выбор из меню

      //@@@@@@

      printf(" =====> kod= %d \n", kod); // для тестирования

      // можно использовать оператор swith

      if ( kod == 0 ) break;

      if ( kod >= 1 && kod <=21 )

           { fun_fun(kod); continue;}

       else

           { printf("Ошибка в структурах файлов меню - не допустимый код возврата\n");

              getch();

              break;  };

    };

   return 0;

};

// главная программа ------ конец

 

void clear()

   { textcolor(colortxt);

     textbackground(colorfon);

     clrscr(); return;};

 

void n_cputs(int nx, int ny, char * str)

 {textbackground(colormenu); //colorfon);

  gotoxy(nx,ny); cputs(str); return;};

 

void v_cputs(int nx, int ny, char * str)

 {textbackground(color2); gotoxy(nx,ny); cputs(str);

  textbackground(colorfon); return;};

 

void s_cputs(int nx, int ny, char * str)

 {textbackground(color1);

  gotoxy(nx,ny);  cputs(str);

  textbackground(colorfon);

  return;};

 

void h_cputs(int nx, int ny, char * str)

 {textbackground(color1);

  gotoxy(1,ny);

  clreol();

  gotoxy(nx,ny);

  cputs(str);

  textbackground(colorfon);

  return;};

 

 

// программы организации СТЕКА

// память для СТЕКА

int s_str[10], s_row[10];

//char  s_file[10][80];  //@@@@@@@@@@@@@@

int s_file[10];

// указатель СТЕКА

int s_ptr=-1;

// поместить в СТЕК

int stek_push(int, int, int );  //@@@@@@ char * );

// взять из СТЕКА

int stek_pop(int &str, int &row, int & ); //@@@@@@@@@@@@char * file);

 

 

// программы работы с стеком

int stek_push(int str, int row, int file)

{ //printf("s_ptr=%d str=%d row=%d file=%d\n", s_ptr, str, row, file); getchar();

  s_ptr++;

  if(s_ptr > 9) {printf("переполнение стека str=%d, row=%d,\n file=%s", str, row, file);

                  exit(16); };

  s_str[s_ptr] = str;

  s_row[s_ptr] = row;

  // @@@@ strcpy(s_file[s_ptr], file);

  s_file[s_ptr]=file;

  return 1;

};

 

int stek_pop(int &str, int &row, int &file )   //@@@@&char * file)

{

  if(s_ptr <= 0 ) return 0;

 

  str = s_str[s_ptr];

  row = s_row[s_ptr];

  //@@@@@  strcpy(file,s_file[s_ptr]);

  file=s_file[s_ptr];

  s_ptr--;

  return 1;

};

//=======================================================

// работа с файлом в ОП

char  TFILE[1000][l_str_max]; //таблица

int   nTFILE;                // кол-во элементов

// указатель следующей строки в TFILE

int tTFILE;

 

// открыть файл и сформировать  таблицу в памяти....(упрощенный вариант)

int t_open(char * nfile)

{

//  struct stat statbuf;

    char buf[l_str_max];

    int n=0;

    FILE *h;

    h=fopen(nfile, "r");             

    if (h == NULL ){printf( "Cannot open input file <%s> \n", nfile); getch(); return -1;};

    while (mfgets(buf, l_str_max, h) != NULL )

      {

        strcpy(TFILE[n],buf);

     //   printf("<%s>\n", TFILE[n]);

        n++;

      };

    nTFILE=n;

  //  getchar();

    fclose(h);

    return 0;

};

// "Тфайл" - установить указатель на заданную запись файла.....

int t_seek(int n)

  {

    if ( n <= 0 && n > nTFILE)

      { printf( "Ошибка в указателе № строки %d \n", n); getch(); return 0;};

    tTFILE=n; return 1;

};

 

 

// чтение данных из Тфайла - читается Запись, но возращается С строка в buf

// длиной не более l_str_max  из файла h

// в конце файла возращается NULL

char * t_mfgets(char * );

 

char * t_mfgets( char* buf)

{

  if ( tTFILE >= nTFILE ) return NULL;

  strcpy(buf, TFILE[tTFILE]);

  tTFILE++;

     return buf;

};

 

//

//========================================================

// программа - шаг диалога

int step_d( char * mfile)

{

// < > строки вопроса

//  #

// < > строки ответа

//  #

//  строки функций "ответа"  - 0 или 1 затем пробел

//  затем код_возврата или имя_файла_шага_меню  

// если в строке код_возврата = -1, то подьем на предыдущий шаг диалога

 

  int num_str,         // номер текущей строки

      rc=-1,           // "код возврата"

      num,             // номер служебной строки ответов

      k;               // рабочая переменная

 

 

  int finish;          // "флаг завершения" выбора в меню

 

//@@@  char nfile[80];      // текущее имя файла  шага диалога

  int nfile;   //@@@@@@@@

  char buf[l_str_max];        // буфер ввода

  char cnum;           //  ответ

  char vvv[5][l_str_max];     //  массив для ВОПРОСА

  char menu[10][l_str_max];   //  массив для организации "меню"

  unsigned int max_len; // максимальная длина строки варианта ответа

  int x,y,             //  рабочие переменные - индексы menu

      ymax,            //  мах количество ответов + 1

      amax,            //  max строк вопроса

      ytek,            // относительная текущая (подсвеченная) позиция в меню

      old_ytek;        // относительная "старая"(подсвеченная) позиция в меню

 

 //@@@@ FILE *h;             // указатель на файл шага диалога

 

 

  int tek_str_begin=str_begin;   // позиция начала диалога

  int tek_row_begin=row_begin;

 

//  НАЧАЛЬНЫЕ УСТАНОВКИ

  help_txt();   // определить текущий текстовый режим

//@@@  strcpy(nfile, mfile);

//@@@@@@@@

  t_open(mfile);

  nfile=0;

 

//@@@@@@

  s_ptr=-1;     // "инициализировать СТЕК"

  stek_push(tek_str_begin, tek_row_begin, nfile);

  clear();

 

 

beg:

 

//@@  h=fopen(nfile, "r");

//@@  if (h == NULL ){cprintf( "Cannot open input file <%s> \n", nfile); getch(); return -1;};

   t_seek(nfile);

 

// начало шага диалога.....

  

   gotoxy(tek_row_begin,tek_str_begin);

   num_str=tek_str_begin;

 

// читает строчки вопроса задаем вопрос

 

   for(y=0;y<5;y++) for(x=0; x <l_str_max; x++) vvv[y][x]=' '; // "чистим" массив

   for(y=0; y<5; y++) vvv[y][l_str_max-1]='\0';

   max_len=0;   // максимальная длина строки варианта ответа

   amax=0;      // счетчик строк ответа

 

 

 

next:

 

   if ( t_mfgets(buf) == NULL )   //@@@@@@, l_str_max, h) == NULL )

      { cprintf( "Ошибка в области вопроса строка N %d в файле <%s> \n", num_str-tek_str_begin+1, nfile);

        return -1; };

   if (buf[0] == '#' ) goto nask;   // вопрос задали

   strcpy(vvv[amax],buf);   // запишим вопрос в массив

// для красоты

   max_len=(strlen(buf) > max_len ) ? strlen(buf) : max_len;

   amax++;

   goto next;

 

nask:

// варианты ответа сохраняем в массив....

   for(y=0;y<10;y++) for(x=0;x<l_str_max; x++) menu[y][x]=' '; // "чистим" массив

   for(y=0; y<10; y++) menu[y][l_str_max-1]='\0';

   ymax=0; // счетчик вариантов ответа

nextv:

   if ( t_mfgets(buf) == NULL )   //@@@ , l_str_max, h) == NULL )

      {cprintf( "Ошибка в области ответа строка N %d в файле <%s> \n", num_str-str_begin+1, nfile);

          return -1; };

   if (buf[0] == '#' ) goto ask;   // закончили вводить вариатны ответа

   strcpy(menu[ymax],buf);   // запишим выриант ответа в массив меню

 

   // для красоты

   max_len=(strlen(buf) > max_len ) ? strlen(buf) : max_len;

   ymax++;

   goto nextv;

 

ask:           

// выравняем текст вопроса и ответа по  максимальной длине строки варианта ответа

   for(y=0; y< ymax; y++)

     { menu[y][strlen(menu[y])]=' '; menu[y][max_len]='\0';};

 

//  вывод вопроса

   for(y=0; y< amax; y++)

     {  vvv[y][strlen(vvv[y])]=' '; vvv[y][max_len]='\0'; 

            s_cputs(tek_row_begin, num_str, vvv[y]);   // вывод вопроса

            num_str++;

     };

 

 

 // "общий" - справочный текст для пользователя

 

   h_cputs(1,ti.winbottom-1,"  Выбирите при помощи срелок вариант ответа");

   h_cputs(1,ti.winbottom,  "  и подтвердите его клавишей Enter");

                         

// вывод возможных ответов

   ytek=0;

   for ( y=0; y < ymax; y++)

      n_cputs(tek_row_begin,num_str+y, menu[y]);  //вывод на экран выриантов ответа меню

    

// подсветка ytek

   v_cputs(tek_row_begin,num_str+ytek, menu[ytek]);

   old_ytek=ytek;   // запомнит "подсвеченную" позицию

// начальные установки выполнили

 

// цикл приема номера выбранного пункта меню

ask_menu:    

   n_cputs(tek_row_begin,num_str+old_ytek, menu[old_ytek]);  // убрать подсветку "старой" строки

   v_cputs(tek_row_begin,num_str+ytek, menu[ytek]);          // установить подсветку "новой" стоки

   old_ytek=ytek;                                        // запомнит "подсвеченную" позицию

   finish=0;   // флаг завершения выбора - нет завершения

//  принять символ с клавиатуры и изменить ytek

zzz:           

   if ( (cnum=getch()) == '\0' ) cnum=getch(); /* 0x50 - стрелка вниз

                                                  0x48 - стрелка вверх

                                                  0x4b - стрелка на лево

                                                  0x4d - стрелка на право

                                                  0x1b - ESC

                                                  0X0d - Enter 

                                                   */

//  cprintf("<%x>\n",cnum);

   switch (cnum)

   {

    case 0x50:   // стрелка вниз

       if ((ytek+1) >= ymax ) {ytek=0; break;};

       ytek++; break;

    case 0x48:   // стрелка вверх

       if ( (ytek-1) < 0 )    {ytek=ymax-1; break;};

       ytek--; break;

    case 0x0d: // ENTER

        finish=1;

        break;

    case 0x1b:  // ESC

        ytek=-1; finish=1; break;

 

    default:

        break;

   };

 

  if ( finish == 1  && ytek != -1 )

 

        {

          rc=ytek+1;

          goto kon;

         };

 

     if (finish==1 && ytek == -1 )

        {

          rc=0; 

          goto kon;

         };

   goto ask_menu;   // повторить выбор

 

kon:

 

      if ( rc==0 )    // Нажали сброс - ESC

    {

        //@@@ fclose(h);

        // обработаем предыдущий файл - узел диалога

        //  надо "вытереть" текущий диалог

            for ( y=0; y < (tek_str_begin+ymax)+1; y++)

              {  gotoxy(tek_row_begin,tek_str_begin+y);  //начало строк вывода

                 clreol(); };

           // можно ли подняться на предыдущий шаг????

           if (stek_pop(tek_str_begin, tek_row_begin, nfile) == 0 )

               { return 0; } ;

           goto beg;

     };

 

   num=rc;

//  чтение служебных строк до служебной строки (включительно) выбранного ответа

   for (k=1; k <= num; k++) if ( t_mfgets(buf) == NULL ) /*, l_str_max, h) == NULL )*/ {goto err; };

//  анализ типа строки ответа

   // printf("buf=<%s> k=%d\n", buf, num); getchar();

   if (buf[0] == '0')  // ВСЕ - код возврата....

            { sscanf( buf+2, "%d", &rc);

              // моделируем нажатие ESC

              if (rc==-1) { rc=0; goto kon; };

              //@@@fclose(h);

              return rc; };

   if (buf[0] == '1')  // новый узел диалога....

            { //@@@@fclose(h);

              //printf("до стека nfile=%d\n", nfile); getchar();

              stek_push(tek_str_begin, tek_row_begin, nfile);

              //@@@@@@@@@@@@@@@@@@@@@@

              //@@@@sscanf( buf+2, "%s", nfile);

              sscanf( buf+2, "%d", &nfile);

              // printf("после чтения nfile=%d\n", nfile); getchar();

              tek_str_begin+=(ytek+amax);  

              tek_row_begin+=max_len;

              goto beg; };

 

 err:

   printf(" Ошибка структуры файла  в области служебных строк\n");

   getchar();

   exit(16);

   return -1;

};

// справочные данные по текущему текстовому режиму

int help_txt(void)

{

   gettextinfo(&ti);

/*

   cprintf("Левая граница:       %2d\r\n",ti.winleft);

   cprintf("Верхняя граница:     %2d\r\n",ti.wintop);

   cprintf("Правая граница:      %2d\r\n",ti.winright);

   cprintf("Нижняя граница:      %2d\r\n",ti.winbottom);

   cprintf("Атрибуты:            %2d\r\n",ti.attribute);

   cprintf("Нормальные атрибуты: %2d\r\n",ti.normattr);

   cprintf("Текущий режим:       %2d\r\n",ti.currmode);

   cprintf("Высота экрана:       %2d\r\n",ti.screenheight);

   cprintf("Ширина экрана:       %2d\r\n",ti.screenwidth);

   cprintf("Координата х:        %2d\r\n",ti.curx);

   cprintf("Координата y:        %2d\r\n",ti.cury);

   getch();

*/

   return 0;

}