#include "filesystem.h"

Filesystem::Directory::Directory(std::string PathName, std::string BasePath){
  MyBase = BasePath;
  if (PathName[0] == '/'){
    PathName.erase(0, 1);
  }
  if (BasePath[BasePath.size() - 1] != '/'){
    BasePath += "/";
  }
  MyPath = PathName;
  FillEntries();
}

Filesystem::Directory::~Directory(){
}

void Filesystem::Directory::FillEntries(){
  fprintf(stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str());
  ValidDir = true;
  struct stat StatBuf;
  Entries.clear();
  DIR * Dirp = opendir((MyBase + MyPath).c_str());
  if ( !Dirp){
    ValidDir = false;
  }else{
    dirent * entry;
    while ((entry = readdir(Dirp))){
      if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1){
        fprintf(stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno));
        continue;
      }
      ///Convert stat to string
      Entries[std::string(entry->d_name)] = StatBuf;
    }
  }
  fprintf(stderr, "Valid dir: %d\n", ValidDir);
  fprintf(stderr, "#Entries: %d\n", Entries.size());
}

void Filesystem::Directory::Print(){
  if ( !ValidDir){
    printf("%s is not a valid directory\n", (MyBase + MyPath).c_str());
    return;
  }
  printf("%s:\n", (MyBase + MyPath).c_str());
  for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++){
    printf("\t%s\n", ( *it).first.c_str());
  }
  printf("\n");
}

bool Filesystem::Directory::IsDir(){
  return ValidDir;
}

std::string Filesystem::Directory::PWD(){
  return "/" + MyPath;
}

std::string Filesystem::Directory::LIST(std::vector<std::string> ActiveStreams){
  FillEntries();
  int MyPermissions;
  std::stringstream Converter;
  passwd* pwd; //For Username
  group* grp; //For Groupname
  tm* tm; //For time localisation
  char datestring[256]; //For time localisation

  std::string MyLoc = MyBase + MyPath;
  if (MyLoc[MyLoc.size() - 1] != '/'){
    MyLoc += "/";
  }

  for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++){

    bool Active = (std::find(ActiveStreams.begin(), ActiveStreams.end(), ( *it).first) != ActiveStreams.end());
    fprintf(stderr, "%s active?: %d\n", ( *it).first.c_str(), Active);
    fprintf(stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath]);
    fprintf(stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE);
    if ((Active && (MyVisible[MyPath] & S_ACTIVE)) || (( !Active) && (MyVisible[MyPath] & S_INACTIVE)) || ((( *it).second.st_mode / 010000) == 4)){
      if ((( *it).second.st_mode / 010000) == 4){
        Converter << 'd';
      }else{
        Converter << '-';
      }
      MyPermissions = ((( *it).second.st_mode % 010000) / 0100);
      if (MyPermissions & 4){
        Converter << 'r';
      }else{
        Converter << '-';
      }
      if (MyPermissions & 2){
        Converter << 'w';
      }else{
        Converter << '-';
      }
      if (MyPermissions & 1){
        Converter << 'x';
      }else{
        Converter << '-';
      }
      MyPermissions = ((( *it).second.st_mode % 0100) / 010);
      if (MyPermissions & 4){
        Converter << 'r';
      }else{
        Converter << '-';
      }
      if (MyPermissions & 2){
        Converter << 'w';
      }else{
        Converter << '-';
      }
      if (MyPermissions & 1){
        Converter << 'x';
      }else{
        Converter << '-';
      }
      MyPermissions = (( *it).second.st_mode % 010);
      if (MyPermissions & 4){
        Converter << 'r';
      }else{
        Converter << '-';
      }
      if (MyPermissions & 2){
        Converter << 'w';
      }else{
        Converter << '-';
      }
      if (MyPermissions & 1){
        Converter << 'x';
      }else{
        Converter << '-';
      }
      Converter << ' ';
      Converter << ( *it).second.st_nlink;
      Converter << ' ';
      if ((pwd = getpwuid(( *it).second.st_uid))){
        Converter << pwd->pw_name;
      }else{
        Converter << ( *it).second.st_uid;
      }
      Converter << ' ';
      if ((grp = getgrgid(( *it).second.st_gid))){
        Converter << grp->gr_name;
      }else{
        Converter << ( *it).second.st_gid;
      }
      Converter << ' ';
      Converter << ( *it).second.st_size;
      Converter << ' ';
      tm = localtime( &(( *it).second.st_mtime));
      strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm);
      Converter << datestring;
      Converter << ' ';
      Converter << ( *it).first;
      Converter << '\n';
    }
  }
  return Converter.str();
}

bool Filesystem::Directory::CWD(std::string Path){
  if (Path[0] == '/'){
    Path.erase(0, 1);
    MyPath = Path;
  }else{
    if (MyPath != ""){
      MyPath += "/";
    }
    MyPath += Path;
  }
  FillEntries();
  printf("New Path: %s\n", MyPath.c_str());
  if (MyPermissions.find(MyPath) != MyPermissions.end()){
    printf("\tPermissions: %d\n", MyPermissions[MyPath]);
  }
  return SimplifyPath();
}

bool Filesystem::Directory::CDUP(){
  return CWD("..");
}

std::string Filesystem::Directory::RETR(std::string Path){
  std::string Result;
  std::string FileName;
  if (Path[0] == '/'){
    Path.erase(0, 1);
    FileName = MyBase + Path;
  }else{
    FileName = MyBase + MyPath + "/" + Path;
  }
  std::ifstream File;
  File.open(FileName.c_str());
  while (File.good()){
    Result += File.get();
  }
  File.close();
  return Result;
}

void Filesystem::Directory::STOR(std::string Path, std::string Data){
  if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_STOR)){
    std::string FileName;
    if (Path[0] == '/'){
      Path.erase(0, 1);
      FileName = MyBase + Path;
    }else{
      FileName = MyBase + MyPath + "/" + Path;
    }
    std::ofstream File;
    File.open(FileName.c_str());
    File << Data;
    File.close();
  }
}

bool Filesystem::Directory::SimplifyPath(){
  MyPath += "/";
  fprintf(stderr, "MyPath: %s\n", MyPath.c_str());
  std::vector<std::string> TempPath;
  std::string TempString;
  for (std::string::iterator it = MyPath.begin(); it != MyPath.end(); it++){
    if (( *it) == '/'){
      if (TempString == ".."){
        if ( !TempPath.size()){
          return false;
        }
        TempPath.erase((TempPath.end() - 1));
      }else if (TempString != "." && TempString != ""){
        TempPath.push_back(TempString);
      }
      TempString = "";
    }else{
      TempString += ( *it);
    }
  }
  MyPath = "";
  for (std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++){
    MyPath += ( *it);
    if (it != (TempPath.end() - 1)){
      MyPath += "/";
    }
  }
  if (MyVisible.find(MyPath) == MyVisible.end()){
    MyVisible[MyPath] = S_ALL;
  }
  return true;
}

bool Filesystem::Directory::DELE(std::string Path){
  if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_DELE)){
    std::string FileName;
    if (Path[0] == '/'){
      Path.erase(0, 1);
      FileName = MyBase + Path;
    }else{
      FileName = MyBase + MyPath + "/" + Path;
    }
    if (std::remove(FileName.c_str())){
      fprintf(stderr, "Removing file %s unsuccesfull\n", FileName.c_str());
      return false;
    }
    return true;
  }
  return false;
}

bool Filesystem::Directory::MKD(std::string Path){
  std::string FileName;
  if (Path[0] == '/'){
    Path.erase(0, 1);
    FileName = MyBase + Path;
  }else{
    FileName = MyBase + MyPath + "/" + Path;
  }
  if (mkdir(FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)){
    fprintf(stderr, "Creating directory %s unsuccesfull\n", FileName.c_str());
    return false;
  }
  MyVisible[FileName] = S_ALL;
  return true;
}

bool Filesystem::Directory::Rename(std::string From, std::string To){
  if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_RNFT)){
    std::string FileFrom;
    if (From[0] == '/'){
      From.erase(0, 1);
      FileFrom = MyBase + From;
    }else{
      FileFrom = MyBase + MyPath + "/" + From;
    }
    std::string FileTo;
    if (To[0] == '/'){
      FileTo = MyBase + To;
    }else{
      FileTo = MyBase + MyPath + "/" + To;
    }
    if (std::rename(FileFrom.c_str(), FileTo.c_str())){
      fprintf(stderr, "Renaming file %s to %s unsuccesfull\n", FileFrom.c_str(), FileTo.c_str());
      return false;
    }
    return true;
  }
  return false;
}

void Filesystem::Directory::SetPermissions(std::string Path, char Permissions){
  MyPermissions[Path] = Permissions;
}

bool Filesystem::Directory::HasPermission(char Permission){
  if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & Permission)){
    return true;
  }
  return false;
}

void Filesystem::Directory::SetVisibility(std::string Pathname, char Visible){
  MyVisible[Pathname] = Visible;
}