#pragma once

#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <time.h>
#include <chrono>
#include <thread>
#include "json.hpp"

#if __cplusplus >= 201703L
#include <filesystem>
#endif

#include "String.h"

class Utils {
public:
	static int max(int a, int b) {
		return (a > b) ? a : b;
	}
	static int min(int a, int b) {
		return (a > b) ? b : a;
	}
	static void Mkdir(const String& path) {
#if __cplusplus >= 201703L
		std::filesystem::create_directory(path.getCharArray());
#else
		mkdir(path.getCharArray(), 0777);
#endif
	}

	//https://stackoverflow.com/questions/4568681/using-chmod-in-a-c-program
	static void Chmod(const String& path) {
#if __cplusplus >= 201703L
        	std::filesystem::permissions(path.getCharArray(), std::filesystem::perms::all);
#else
        	String command = "chmod 777 " + String::DQuotes(path);
            int i=system(command.getCharArray());
            //printf("chmod 777 '%s' return code = %d\n", path.getCharArray(), i);
#endif
	}

	//https://stackoverflow.com/questions/230062/whats-the-best-way-to-check-if-a-file-exists-in-c
	static bool Exists(const String& path) {
#if __cplusplus >= 201703L
		return std::filesystem::exists(path.getCharArray());
#else
		struct stat buffer;
		return (stat(path.getCharArray(), &buffer) == 0);
#endif
	}

	//in seconds
	static void Sleep(int s) {
		std::chrono::seconds timespan(s);
		std::this_thread::sleep_for(timespan);
	}

	static void Copy(const String& src, const String& dst) {
#if __cplusplus >= 201703L
	        std::filesystem::copy(src.getCharArray(), dst.getCharArray());
#else
		    String command = "cp " + String::DQuotes(src) + " " + String::DQuotes(dst);
		    int i = system(command.getCharArray());
		    //printf("cp '%s' return code = %d\n",src.getCharArray(), i);
#endif
	}
		static void Ln(const String& src, const String& dst) {
    #if __cplusplus >= 201703L
    	        std::filesystem::create_symlink(src.getCharArray(), dst.getCharArray());
    #else
    		    String command = "ln -s " + String::DQuotes(src) + " " + String::DQuotes(dst);
    		    int i = system(command.getCharArray());
    		    //printf("cp '%s' return code = %d\n",src.getCharArray(), i);
    #endif
    	}
	static void CopyDirectory(const String& src, const String& dst) {
#if __cplusplus >= 201703L
        	std::filesystem::copy((src+ "/.").getCharArray(), dst.getCharArray(), std::filesystem::copy_options::recursive);
#else
        	String command = "cp -r " + String::DQuotes(src + "/.") + " " + String::DQuotes(dst);
            int i = system(command.getCharArray());
            //printf("cp -r '%s' return code = %d\n",src.getCharArray(),i);
#endif
    }
    	static void CopyDirectory_L(const String& src, const String& dst) {
#if __cplusplus >= 201703L
      	std::filesystem::copy((src+ "/.").getCharArray(), dst.getCharArray(), std::filesystem::copy_options::recursive|std::filesystem::copy_options::directories_only);
      	std::string src_home = string(src.getCharArray());
      	std::string dst_home = string(dst.getCharArray());
		for (const std::filesystem::directory_entry& dir_entry : std::filesystem::recursive_directory_iterator(src_home))
    		{
        		if (dir_entry.is_regular_file()){
        			std::string src_file = dir_entry.path().string();
        			std::string relative = src_file.substr(src_home.size(), src_file.size());
        			std::string dst_file = string(dst_home).append(relative);
        			std::filesystem::copy(src_file, dst_file, std::filesystem::copy_options::create_symlinks);
        		}
    		}
#else
      	String command = "cp -rs " + String::DQuotes(src + "/.") + " " + String::DQuotes(dst);
      	int i = system(command.getCharArray());
#endif
      }

	static time_t getAbsoluteTime() {
		return time(NULL);
	}
	static String getDate() {
		auto ttime = time(NULL);
		String res(ctime(&ttime));
		return res;
	}

	static String getFileName(const String& path){
	    int start_i = path.getBody().find_last_of('/');
	    if (start_i != string::npos){
	        return String(path.getBody().substr(start_i+1));
	    }
	    return String();
	}

	template <typename T>
	static T getValue(const nlohmann::json& data, const string& obj) {
		T retval;
		if (data.contains(obj))
			retval = data[obj];
		else {
			printf("can not fine object '%s'\n", obj.c_str());
			exit(-1);
		}

		return retval;
	}
};
