text: // LICENSE NOTICE ==================================================================================
  
  /**
   * netzhaut - Web Browser Engine
   * Copyright (C) 2020 The netzhaut Authors
   * Published under MIT
   */
  
  // INCLUDE =========================================================================================
  
  #include "TreeListing.h"
  #include "Editor.h"
  
  #include "../TTY/TTY.h"
  
  #include "../Common/Macros/Macros.h"
  #include NH_TTY_FLOW
  #include NH_TTY_DEFAULT_CHECK
  
  #include "../../nhcore/System/Memory.h"
  #include "../../nhcore/Common/Macros/Macros.h"
  #include NH_FLOW
  #include NH_CUSTOM_CHECK
  
  #include "../../nhencoding/Encodings/UTF32.h"
  #include "../../nhencoding/Encodings/UTF8.h"
  #include "../../nhencoding/Common/Macros/Macros.h"
  #include NH_ENCODING_FLOW
  #include NH_ENCODING_CUSTOM_CHECK
  
  #include <stddef.h>
  #include <unistd.h>
  #include <stdio.h>
  #include <string.h>
  #include <dirent.h>
  #include <stdlib.h>
  
  #include <sys/types.h>
  #include <sys/stat.h>
  
  // HELPER ==========================================================================================
  
  static int nh_tty_isRegularFile(
      nh_tty_TreeListingNode *Node_p)
  {
  NH_TTY_BEGIN()
  
      if (Node_p->unsaved) {NH_TTY_END(NH_TRUE)}
  
      nh_encoding_UTF8String Path = nh_encoding_encodeUTF8(Node_p->Path.p, Node_p->Path.length);
      struct stat path_stat;
      stat(Path.p, &path_stat);
      nh_encoding_freeUTF8String(&Path);
  
  NH_TTY_END(S_ISREG(path_stat.st_mode))
  }
  
  static void nh_tty_getNodeList(
      nh_List *List_p, nh_tty_TreeListingNode *Node_p)
  {
  NH_TTY_BEGIN()
  
      nh_appendToList(List_p, Node_p);
  
      if (!Node_p->open) {NH_TTY_SILENT_END()}
  
      for (int i = 0; i < Node_p->Children.size; ++i) {
          nh_tty_getNodeList(List_p, Node_p->Children.pp[i]);
      }
  
  NH_TTY_SILENT_END()
  }
  
  static nh_tty_TreeListingNode *nh_tty_getCurrentNode(
      nh_tty_TreeListing *Listing_p)
  {
  NH_TTY_BEGIN()
  
      int current = 0; 
      nh_List Nodes = nh_initList(128);
      nh_tty_getNodeList(&Nodes, Listing_p->Root_p);
  
      nh_tty_TreeListingNode *Current_p = Nodes.pp[Listing_p->treeCurrent];
  
      nh_freeList(&Nodes, NH_FALSE);
  
  NH_TTY_END(Current_p)
  }
  
  static nh_tty_TreeListingNode *nh_tty_getTreeListingNode(
      nh_tty_TreeListing *Listing_p, nh_encoding_UTF32String *Path_p)
  {
  NH_TTY_BEGIN()
  
      int current = 0; 
      nh_List Nodes = nh_initList(128);
      nh_tty_getNodeList(&Nodes, Listing_p->Root_p);
  
      nh_tty_TreeListingNode *Result_p = NULL;
      for (int i = 0; i < Nodes.size; ++i) {
          if (nh_encoding_compareUTF32(((nh_tty_TreeListingNode*)Nodes.pp[i])->Path.p, Path_p->p)) {
              Result_p = Nodes.pp[i];
              break;
          }
      }
  
      nh_freeList(&Nodes, NH_FALSE);
  
  NH_TTY_END(Result_p)
  }
  
  // CREATE ==========================================================================================
  
  static nh_tty_TreeListingNode *nh_tty_createTreeListingNode(
      nh_tty_TreeListingNode *Parent_p, nh_encoding_UTF32String Path, nh_tty_File *File_p)
  {
  NH_TTY_BEGIN()
  
  #include NH_TTY_CUSTOM_CHECK
  
      nh_tty_TreeListingNode *Node_p = nh_allocate(sizeof(nh_tty_TreeListingNode));
      NH_TTY_CHECK_MEM(NULL, Node_p)
  
  #include NH_TTY_DEFAULT_CHECK
  
      Node_p->open     = NH_FALSE;
      Node_p->unsaved  = NH_FALSE;
      Node_p->Path     = Path;
      Node_p->Children = nh_initList(16);
      Node_p->Parent_p = Parent_p;
      Node_p->File_p   = File_p;
  
  NH_TTY_END(Node_p)
  }
  
  static NH_TTY_RESULT nh_tty_openNode(
      nh_tty_TreeListingNode *Node_p)
  {
  NH_TTY_BEGIN()
  
      NH_TTY_CHECK_NULL(Node_p)
  
      if (Node_p->Path.length <= 0 || Node_p->Children.size > 0 || nh_tty_isRegularFile(Node_p)) {
          NH_TTY_DIAGNOSTIC_END(NH_TTY_ERROR_BAD_STATE)
      }
  
  #ifdef __unix__ 
  
      struct dirent **namelist_pp;
      nh_encoding_UTF8String CurrentPath = nh_encoding_encodeUTF8(Node_p->Path.p, Node_p->Path.length);
  
      int n = scandir(CurrentPath.p, &namelist_pp, 0, alphasort);
      if (n < 0) {NH_TTY_DIAGNOSTIC_END(NH_TTY_ERROR_BAD_STATE)}
  
      nh_encoding_freeUTF8String(&CurrentPath);
  
      for (int i = 0; i < n; ++i) 
      {
          if (strcmp(namelist_pp[i]->d_name, ".") && strcmp(namelist_pp[i]->d_name, "..")) 
          {
              nh_encoding_UTF32String NewPath = nh_encoding_initUTF32(128);
              nh_encoding_UTF32String FileName = nh_encoding_decodeUTF8(namelist_pp[i]->d_name, strlen(namelist_pp[i]->d_name), NULL);
  
              if (Node_p->Path.p[Node_p->Path.length - 1] != '/') {
                  nh_encoding_appendUTF32(&NewPath, Node_p->Path.p, Node_p->Path.length);
                  nh_encoding_appendUTF32Codepoint(&NewPath, '/');
                  nh_encoding_appendUTF32(&NewPath, FileName.p, FileName.length);
              } else {
                  nh_encoding_appendUTF32(&NewPath, Node_p->Path.p, Node_p->Path.length);
                  nh_encoding_appendUTF32(&NewPath, FileName.p, FileName.length);
              }
  
              nh_tty_TreeListingNode *New_p = nh_tty_createTreeListingNode(Node_p, NewPath, NULL);
              NH_TTY_CHECK_MEM(New_p)
  
              NH_CHECK(NH_TTY_ERROR_BAD_STATE, nh_appendToList(&Node_p->Children, New_p))
          }
  
          free(namelist_pp[i]);
      }
  
      if (Node_p->Children.size == 0) {
          NH_TTY_CHECK(nh_tty_setCustomMessage(
              &nh_tty_getTTY()->Tab_p->Tile_p->Status, NH_TTY_MESSAGE_EDITOR_EMPTY_DIRECTORY, 
              Node_p->Path.p, Node_p->Path.length
          ))
      }
  
      free(namelist_pp);
  
  #elif defined(_WIN32) || defined(WIN32)
  
      printf("microsoft windows not supported\n");
      exit(0);
  
  #endif
  
      Node_p->open = NH_TRUE;
  
  NH_TTY_END(NH_TTY_SUCCESS)
  }
  
  nh_tty_TreeListingNode *nh_tty_insertTreeListingNode(
      nh_tty_TreeListing *Listing_p, NH_ENCODING_UTF32 *name_p, int length)
  {
  NH_TTY_BEGIN()
  
  #include NH_TTY_CUSTOM_CHECK
  
      NH_TTY_CHECK_NULL(NULL, name_p)
      if (length <= 0) {NH_TTY_END(NULL)}
  
      nh_tty_TreeListingNode *Current_p = nh_tty_getCurrentNode(Listing_p);
  
      int offset = Current_p->Path.length;
      if (nh_tty_isRegularFile(Current_p)) {
          for (offset = Current_p->Path.length - 1; Current_p->Path.p[offset] != '/'; --offset);
      }
  
      offset = Current_p->Path.length - offset;
  
      nh_encoding_UTF32String Path = nh_encoding_initUTF32(128);
      nh_encoding_appendUTF32(&Path, Current_p->Path.p, Current_p->Path.length - offset);
      nh_encoding_appendUTF32Codepoint(&Path, '/');
      nh_encoding_appendUTF32(&Path, name_p, length);
  
      nh_tty_TreeListingNode *New_p = NULL;
  
      if (!nh_tty_getTreeListingNode(Listing_p, &Path)) {
          if (Current_p->Children.size > 0) {
              New_p = nh_tty_createTreeListingNode(Current_p, Path, NULL);
              NH_CHECK(NULL, nh_appendToList(&Current_p->Children, New_p))
          }
          else if (Current_p->Parent_p != NULL) {
              New_p = nh_tty_createTreeListingNode(Current_p->Parent_p, Path, NULL);
              NH_CHECK(NULL, nh_appendToList(&Current_p->Parent_p->Children, New_p))
          }
          else {NH_TTY_END(NULL)}
          NH_TTY_CHECK_MEM(NULL, New_p)
          New_p->unsaved = NH_TRUE;
          nh_tty_renderTreeListing(Listing_p);
      }
      else {
          NH_TTY_CHECK(NULL, nh_tty_setDefaultMessage(
              &nh_tty_getTTY()->Tab_p->Tile_p->Status, NH_TTY_MESSAGE_EDITOR_FILE_ALREADY_EXISTS
          ))
          nh_encoding_freeUTF32(&Path);
      }
  
  #include NH_TTY_DEFAULT_CHECK
  
  NH_TTY_END(New_p)
  }
  
  // INPUT ===========================================================================================
  
  static NH_TTY_RESULT nh_tty_removeFile(
      nh_tty_Status *Status_p, nh_tty_Editor *Editor_p)
  {
  NH_TTY_BEGIN()
  
      nh_tty_TreeListingNode *Node_p = nh_tty_getCurrentNode(&Editor_p->TreeListing);
      nh_encoding_UTF8String Path = nh_encoding_encodeUTF8(Node_p->Path.p, Node_p->Path.length);
  
      remove(Path.p);
      nh_encoding_freeUTF8String(&Path);
  
      NH_TTY_CHECK(nh_tty_setCustomMessage(
          Status_p, NH_TTY_MESSAGE_EDITOR_FILE_REMOVED, Node_p->Path.p, Node_p->Path.length
      ))
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  static NH_TTY_RESULT nh_tty_delete(
      nh_tty_Status *Status_p, nh_tty_Event Event, NH_BOOL *continue_p)
  {
  NH_TTY_BEGIN()
  
      if (Event.trigger != NH_WSI_TRIGGER_PRESS) {NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)}
  
      NH_ENCODING_UTF32 c = Event.codepoint;
  
      if (c == 'y' || c == 'n') 
      {
          Status_p->block  = NH_FALSE;
          Status_p->args_p = NULL;
          Status_p->callback_f = NULL;
      
          if (c == 'n') {
              NH_TTY_CHECK(nh_tty_setDefaultMessage(
                  Status_p, NH_TTY_MESSAGE_BINARY_QUERY_DELETE_INTERRUPTED
              ))
          }
          if (c == 'y') {
  //            nh_tty_Program *Program_p = nh_tty_getCurrentProgram(&nh_tty_getTTY()->Tab_p->Tile_p->Console);
  //            nh_tty_removeFile(Status_p, Program_p->handle_p);
  //            NH_TTY_CHECK(nh_tty_handleTreeListingInput(
  //                Program_p, ((nh_tty_Editor*)Program_p->handle_p)->height, 'w'
  //            ))
          }
      }
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  static NH_TTY_RESULT nh_tty_setCurrentToRoot(
      nh_tty_TreeListing *Listing_p, nh_Array *Programs_p)
  {
  NH_TTY_BEGIN()
  
      nh_tty_TreeListingNode *Current_p = nh_tty_getCurrentNode(Listing_p);
  
      if (Current_p->Children.size > 0) {
          for (nh_tty_TreeListingNode *Parent_p = Current_p; Parent_p = Parent_p->Parent_p;) {
              Parent_p->open = NH_FALSE;
          }
          Listing_p->Root_p = Current_p;
      }
  
      NH_TTY_CHECK(nh_tty_setCustomMessage(
          &nh_tty_getTTY()->Tab_p->Tile_p->Status, NH_TTY_MESSAGE_EDITOR_NEW_ROOT, 
          Listing_p->Root_p->Path.p, Listing_p->Root_p->Path.length
      ))
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  static NH_TTY_RESULT nh_tty_setParentToRoot(
      nh_tty_TreeListing *Listing_p, nh_Array *Programs_p)
  {
  NH_TTY_BEGIN()
  
      if (Listing_p->Root_p->Parent_p != NULL) {
          Listing_p->Root_p = Listing_p->Root_p->Parent_p;
          Listing_p->Root_p->open = NH_TRUE;
      }
      else {
          nh_encoding_UTF32String Path = nh_encoding_initUTF32(128);
          nh_encoding_appendUTF32(&Path, Listing_p->Root_p->Path.p, Listing_p->Root_p->Path.length);
  
          while (Path.p[Path.length - 1] != '/') {nh_encoding_removeUTF32Tail(&Path, 1);}
          if (Path.length > 1) {nh_encoding_removeUTF32Tail(&Path, 1);}
  
          nh_tty_TreeListingNode *OldRoot_p = Listing_p->Root_p;
  
          Listing_p->Root_p = nh_tty_createTreeListingNode(NULL, Path, NULL);
          NH_TTY_CHECK_MEM(Listing_p->Root_p)
  
          OldRoot_p->Parent_p = Listing_p->Root_p;
          NH_TTY_CHECK(nh_tty_openNode(Listing_p->Root_p))
  
          NH_BOOL isChild = NH_FALSE;
          for (int i = 0; i < Listing_p->Root_p->Children.size; ++i) {
              nh_tty_TreeListingNode *Child_p = Listing_p->Root_p->Children.pp[i];
              if (nh_encoding_compareUTF32(Child_p->Path.p, OldRoot_p->Path.p)) {
                  nh_encoding_freeUTF32(&Child_p->Path);
                  nh_free(Listing_p->Root_p->Children.pp[i]);
                  Listing_p->Root_p->Children.pp[i] = OldRoot_p;
                  isChild = NH_TRUE;
                  break;
              }
          }
  
          if (!isChild) {NH_TTY_DIAGNOSTIC_END(NH_TTY_ERROR_BAD_STATE)}
      }
  
      NH_TTY_CHECK(nh_tty_setCustomMessage(
          &nh_tty_getTTY()->Tab_p->Tile_p->Status, NH_TTY_MESSAGE_EDITOR_NEW_ROOT, 
          Listing_p->Root_p->Path.p, Listing_p->Root_p->Path.length
      ))
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  static void nh_tty_updateTreeListingView(
      nh_tty_Program *Program_p, int key)
  {
  NH_TTY_BEGIN()
  
      switch (key) 
      {
          case 'w' :
              for (int i = 0; i < Program_p->Views.size; ++i) {
                  nh_tty_EditorView *View_p = Program_p->Views.pp[i];
                  if (View_p->treeListingCurrent > 0) {View_p->treeListingCurrent--;}
                  else if (View_p->treeListingOffset > 0) {View_p->treeListingOffset--;} 
              }
              break;
  
          case 's' :
              for (int i = 0; i < Program_p->Views.size; ++i) {
                  nh_tty_EditorView *View_p = Program_p->Views.pp[i];
                  if (View_p->treeListingCurrent < View_p->height - 1) {View_p->treeListingCurrent++;}
                  else {View_p->treeListingOffset++;}
              }
              break;
      }
  
  NH_TTY_SILENT_END()
  }
  
  static void nh_tty_moveCursorVertically(
      nh_tty_Program *Program_p, int key) 
  {
  NH_TTY_BEGIN()
  
      nh_tty_FileEditor *FileEditor_p = &((nh_tty_Editor*)Program_p->handle_p)->FileEditor;
      nh_tty_TreeListing *Listing_p = &((nh_tty_Editor*)Program_p->handle_p)->TreeListing;
  
      nh_List Nodes = nh_initList(32);
      nh_tty_getNodeList(&Nodes, Listing_p->Root_p);
  
      switch (key) 
      {
          case 'w' :
          case CTRL_KEY('w') :
  
              if (Listing_p->treeCurrent > 0) {
                  Listing_p->treeCurrent--;
                  nh_tty_updateTreeListingView(Program_p, 'w');
              }
              break;
  
          case 's' :
          case CTRL_KEY('s') :
  
              if (Listing_p->treeCurrent < Nodes.size - 1) { 
                  Listing_p->treeCurrent++;
                  nh_tty_updateTreeListingView(Program_p, 's');
              }
              break;
      }
  
      nh_freeList(&Nodes, NH_FALSE);
  
      if (Listing_p->preview && FileEditor_p != NULL)
      {
          if (Listing_p->Preview_p != NULL) {
              nh_tty_closeFile(Program_p, Listing_p->Preview_p->File_p);   
              Listing_p->Preview_p->File_p = NULL;
              Listing_p->Preview_p = NULL;
          }
  
          nh_tty_TreeListingNode *Current_p = nh_tty_getCurrentNode(Listing_p);
  
          if (Current_p->File_p == NULL && Current_p->Path.length > 0 && nh_tty_isRegularFile(Current_p)) {
              Current_p->File_p = nh_tty_openFile(Program_p, Current_p, NH_TRUE);
              Listing_p->Preview_p = Current_p; 
          }
      }
  
  NH_TTY_SILENT_END()
  }
  
  NH_TTY_RESULT nh_tty_handleTreeListingInput(
      nh_tty_Program *Program_p, NH_ENCODING_UTF32 c)
  {
  NH_TTY_BEGIN()
  
      nh_tty_Editor *Editor_p = Program_p->handle_p;
      nh_List *Views_p = &Program_p->Views;
  
      nh_tty_FileEditor *FileEditor_p = &Editor_p->FileEditor;
      nh_tty_TreeListing *Listing_p = &Editor_p->TreeListing;
      nh_tty_TreeListingNode *Current_p = nh_tty_getCurrentNode(Listing_p);
  
      switch (c)
      {
          case 'w' :
          case 's' :
          case CTRL_KEY('w') :
          case CTRL_KEY('s') :
  
              nh_tty_moveCursorVertically(Program_p, c);
              break;
  
          case 'a' :
          case CTRL_KEY('a') :
  
              if (Listing_p->treeCurrent == 0) {
                  nh_tty_setParentToRoot(Listing_p, Program_p->Programs_p);
              } 
              else if (Current_p->Children.size > 0 && Current_p->open) {
                  Current_p->open = NH_FALSE;
              }
              else if (Current_p->Children.size == 0 && Current_p->File_p != NULL && Current_p->Path.length > 0) {
                  NH_TTY_CHECK(nh_tty_closeFile(Program_p, Current_p->File_p))   
                  Current_p->File_p = NULL;
              }
              else if (Current_p->Children.size == 0 && Current_p->File_p != NULL) {
                  NH_TTY_CHECK(nh_tty_closeFile(Program_p, Current_p->File_p))
                  nh_removeFromList2(&Current_p->Parent_p->Children, NH_TRUE, Current_p);
                  NH_TTY_CHECK(nh_tty_handleTreeListingInput(Program_p, 'w'))
              }
              else { // delete ?
                  nh_encoding_UTF32String Question = nh_encoding_initUTF32(128);
                  int deleteLength;
                  NH_ENCODING_UTF32 *delete_p = nh_tty_getMessage(NH_TTY_MESSAGE_BINARY_QUERY_DELETE, &deleteLength);
                  NH_ENCODING_CHECK(NH_TTY_ERROR_BAD_STATE, nh_encoding_appendUTF32(&Question, delete_p, deleteLength))
                  NH_ENCODING_CHECK(NH_TTY_ERROR_BAD_STATE, nh_encoding_appendUTF32(&Question, Current_p->Path.p, Current_p->Path.length))
                  NH_TTY_CHECK(nh_tty_setBinaryQueryMessage(Question.p, Question.length, NULL, nh_tty_delete))
                  nh_encoding_freeUTF32(&Question);
              }
              break;
  
          case 'd' :
          case CTRL_KEY('d') :
  
              if (Listing_p->Preview_p != NULL) {
                  Listing_p->Preview_p->File_p->readOnly = NH_FALSE;
                  Listing_p->Preview_p = NULL;
              }
              else if (Current_p->Children.size > 0 && !Current_p->open) {
                  Current_p->open = NH_TRUE;
              }
              else if (Current_p->Children.size == 0 && !nh_tty_isRegularFile(Current_p)) {
                  NH_TTY_CHECK(nh_tty_openNode(Current_p))
              }
              else if (Current_p->Children.size == 0 && nh_tty_isRegularFile(Current_p)) {
                  if (Current_p->File_p == NULL) {
                      Current_p->File_p = nh_tty_openFile(Program_p, Current_p, NH_FALSE);
                  }
                  else {
                      NH_TTY_CHECK(nh_tty_writeFile(Current_p->File_p))
                      Current_p->unsaved = NH_FALSE;
                  }
              } else {
                  nh_tty_setCurrentToRoot(Listing_p, Program_p->Programs_p);
                  Listing_p->treeCurrent = 0;
                  for (int i = 0; i < Views_p->size; ++i) {
                      nh_tty_EditorView *View_p = Views_p->pp[i];
                      View_p->treeListingCurrent = 0;
                      View_p->treeListingOffset = 0;
                  }
              }
  
              break;
      }
   
      NH_TTY_CHECK(nh_tty_renderTreeListing(Listing_p))
  
  NH_TTY_END(NH_TTY_SUCCESS)
  }
  
  // RENDER ==========================================================================================
  
  static NH_TTY_RESULT nh_tty_renderTreeListingNode(
      nh_tty_TreeListingNode *Node_p, NH_BYTE *row_p)
  {
  NH_TTY_BEGIN()
  
      int offset = 0;
      nh_tty_TreeListingNode *Parent_p = Node_p->Parent_p;
  
      while (Parent_p != NULL && Parent_p->open) {
          offset += 2; 
          Parent_p = Parent_p->Parent_p;
      }
  
      for (int i = 0; i < offset; i++) {row_p[i] = ' ';}
  
      if (Node_p->Path.length > 0)
      {
          NH_BYTE *p = NULL;
          int tmp = 0;
  
          for (int i = 0; i < Node_p->Path.length; ++i) {
              if (Node_p->Path.p[i] == '/') {tmp = i;}
          }
  
          tmp++;
  
          nh_encoding_UTF8String Name = 
              nh_encoding_encodeUTF8(Node_p->Path.p + tmp, Node_p->Path.length - tmp);
  
          sprintf(row_p + offset, Name.p);
  
          nh_encoding_freeUTF8String(&Name);
  
          if (!nh_tty_isRegularFile(Node_p)) {
              row_p[strlen(row_p)] = '/'; 
          }
          else if (Node_p->unsaved) {
              row_p[strlen(row_p)] = '*'; 
          }
      }
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  static NH_TTY_RESULT nh_tty_renderTreeListingRow(
      nh_List *Nodes_p, nh_tty_TreeListingNode *Current_p, nh_String *Row_p, int row, int cols)
  {
  NH_TTY_BEGIN()
  
      nh_tty_TreeListingNode *Node_p = nh_getFromList(Nodes_p, row);
  
      if (Node_p != NULL) 
      {
          NH_BYTE row_p[1024] = {'\0'};
          NH_TTY_CHECK(nh_tty_renderTreeListingNode(Node_p, row_p))
  
          if (Current_p == Node_p && row > 0) {
              NH_BYTE *p = row_p;
              while (*p == ' ') {p++;}
              p[-2] = '-'; 
              p[-1] = '>';
          }
  
          if (Node_p->File_p != NULL) {
              nh_appendToString(Row_p, "\e[1;1m", 6);
          }
  
          nh_appendFormatToString(Row_p, row_p);
  
          for (int i = 0; i < cols - strlen(row_p); ++i) {
              nh_appendToString(Row_p, " ", 1);
          }
      }
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  static int nh_tty_getTreeListingWidth(
      nh_tty_TreeListing *Listing_p)
  {
  NH_TTY_BEGIN()
  
      nh_List Nodes = nh_initList(64);
      nh_tty_getNodeList(&Nodes, Listing_p->Root_p);
  
      int width = 0;
      NH_BYTE row_p[1024] = {'\0'};
  
      for (int i = 0; i < Nodes.size; ++i) {
          NH_TTY_CHECK(nh_tty_renderTreeListingNode(Nodes.pp[i], row_p))
          if (strlen(row_p) > width) {width = strlen(row_p);}
          memset(row_p, 0, 1024);
      }
  
      nh_freeList(&Nodes, NH_FALSE);
  
  NH_TTY_END(width)
  }
  
  NH_TTY_RESULT nh_tty_renderTreeListing(
      nh_tty_TreeListing *Listing_p)
  {
  NH_TTY_BEGIN()
  
      nh_List Nodes = nh_initList(32);
      nh_tty_getNodeList(&Nodes, Listing_p->Root_p);
      nh_tty_TreeListingNode *Current_p = nh_tty_getCurrentNode(Listing_p);
  
      for (int i = 0; i < Listing_p->RenderLines.length; ++i) {
          nh_freeString(&((nh_String*)Listing_p->RenderLines.p)[i]);
      }
      nh_freeArray(&Listing_p->RenderLines);
      Listing_p->RenderLines = nh_initArray(sizeof(nh_String), Nodes.size);
  
      int width = nh_tty_getTreeListingWidth(Listing_p);
  
      for (int row = 0; row < Nodes.size; ++row) {
          nh_String *Line_p = nh_incrementArray(&Listing_p->RenderLines);
          *Line_p = nh_initString(128);
          NH_TTY_CHECK(nh_tty_renderTreeListingRow(
              &Nodes, Current_p, Line_p, row, width 
          ))
      }
  
      nh_freeList(&Nodes, NH_FALSE);
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  // DRAW ============================================================================================
  
  NH_TTY_RESULT nh_tty_drawTreeListingRow(
      nh_tty_Program *Program_p, nh_String *Row_p, int row)
  {
  NH_TTY_BEGIN()
  
      nh_tty_TreeListing *Listing_p = &((nh_tty_Editor*)Program_p->handle_p)->TreeListing;
  
      nh_tty_EditorView *View_p = Program_p->view_p;
      View_p->treeListingWidth = nh_tty_getTreeListingWidth(Listing_p);
  
      row += View_p->treeListingOffset; 
  
      if (row < Listing_p->RenderLines.length) {
          nh_String *RenderLine_p = &((nh_String*)Listing_p->RenderLines.p)[row];
          nh_appendToString(Row_p, RenderLine_p->p, RenderLine_p->length);
      }
      else {
          NH_BYTE whitespace_p[1024];
          memset(whitespace_p, 32, 1024);
          nh_appendToString(Row_p, whitespace_p, View_p->treeListingWidth);
      }
  
      nh_appendToString(Row_p, "\e[0m", 4);
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  // CURSOR ==========================================================================================
  
  NH_TTY_RESULT nh_tty_setTreeListingCursor(
      nh_tty_Program *Program_p, nh_tty_File *File_p)
  {
  NH_TTY_BEGIN()
  
      nh_List Nodes = nh_initList(16);
      nh_tty_TreeListing *Listing_p = &((nh_tty_Editor*)Program_p->handle_p)->TreeListing;
  
      nh_tty_getNodeList(&Nodes, Listing_p->Root_p);
  
      for (int i = 0; i < Nodes.size; ++i) {
          if (((nh_tty_TreeListingNode*)Nodes.pp[i])->File_p == File_p) {
              if (Listing_p->treeCurrent < i) {
                  int diff = i - Listing_p->treeCurrent;
                  while (diff-- > 0) {
                      nh_tty_moveCursorVertically(Program_p, CTRL_KEY('s'));
                  }
              }
              else if (Listing_p->treeCurrent > i) {
                  int diff = Listing_p->treeCurrent - i;
                  while (diff-- > 0) {
                      nh_tty_moveCursorVertically(Program_p, CTRL_KEY('w'));
                  }
              }
              break;
          }
      }
  
      nh_freeList(&Nodes, NH_FALSE);
      NH_TTY_CHECK(nh_tty_renderTreeListing(Listing_p))
  
  NH_TTY_DIAGNOSTIC_END(NH_TTY_SUCCESS)
  }
  
  // INIT ============================================================================================
  
  nh_tty_TreeListing nh_tty_initTreeListing()
  {
  NH_TTY_BEGIN()
  
      nh_tty_TreeListing Listing;
  
      Listing.Root_p        = nh_tty_createTreeListingNode(NULL, nh_encoding_initUTF32(0), NULL);
      Listing.treeCurrent   = 0;
      Listing.RenderLines   = nh_initArray(sizeof(nh_String), 255);
      Listing.preview       = NH_FALSE;
      Listing.Preview_p     = NULL;
  
      NH_BYTE wrkDir_p[2048];
      memset(wrkDir_p, 0, 2048);
      getcwd(wrkDir_p, 2048);
  
      if (wrkDir_p != NULL) {
          Listing.Root_p->Path = nh_encoding_decodeUTF8(wrkDir_p, strlen(wrkDir_p), NULL);
          nh_tty_openNode(Listing.Root_p); 
          nh_tty_renderTreeListing(&Listing);
      }
  
  NH_TTY_END(Listing)
  }
  
  