#pragma once

#include "Dtypes.h"

#ifdef PLATFORM_ESP32
#include "SPIFFS.h"

namespace portablefs
{
    using File = ::File;
    using string = String;

    template<typename T> 
    String to_string(const T & value) {
        return String(value);
    }

    class Dir
    {
    public:
        Dir() {}
        Dir(const String &path)
            : root_(SPIFFS.open(path))
        {
            //next();
        }

        bool next()
        {
            file_ = root_.openNextFile();
            return file_;
        }

        bool isFile()
        {
            return !file_.isDirectory();
        }

        bool isDirectory()
        {
            return file_.isDirectory();
        }

        String fileName() const
        {
            return file_.name();
        }

        size_t fileSize() const
        {
            return file_.size();
        }

    private:
        File root_;
        File file_;
    };

    inline Dir openDir(const String &path)
    {
        return Dir(path);
    }

    inline File open(const char *name, const char *mode)
    {
        return SPIFFS.open(name, mode);
    }

    inline bool exists(const char *name)
    {
        return SPIFFS.exists(name);
    }

    inline bool remove(const char *name)
    {
        return SPIFFS.remove(name);
    }

    inline bool mkdir(const char *name)
    {
        return SPIFFS.mkdir(name);
    }
    
    inline size_t usedBytes() 
    {
        return SPIFFS.usedBytes();
    }

    inline size_t totalBytes()
    {
        return SPIFFS.totalBytes();
    }


} // namespace portablefs

#endif


#ifdef PLATFORM_NATIVE

#include <string>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;

enum SeekMode {
    SeekSet = 0,
    SeekCur = 1,
    SeekEnd = 2
};


namespace portablefs
{
    const std::string basePath = "./base";
    using string = std::string;
    
    using std::to_string;

    class Dir
    {
    public:
        Dir() {}
        Dir(const String &path)
            : it_(fs::directory_iterator(path)),
              end_(fs::directory_iterator()),
              firstIncremented_(false)
        {
        }

        bool next()
        {
            if (!firstIncremented_)
                firstIncremented_ = true;
            else
                ++it_;

            return it_ != end_;
        }

        bool isFile()
        {
            return it_->is_regular_file();
        }

        bool isDirectory()
        {
            return it_->is_directory();
        }

        String fileName() const
        {
            return it_->path().filename().string();
        }

        size_t fileSize() const
        {
            return it_->file_size();
        }

    private:
        fs::directory_iterator it_;
        fs::directory_iterator end_;
        bool firstIncremented_;
    };

    inline Dir openDir(const String &path)
    {
        return Dir(path);
    }

    class File 
    {
    public:
        File(std::FILE * file) : file_(file) {}

        bool seek(uint32_t pos, SeekMode mode) 
        {
            if(mode == SeekSet)
                return std::fseek(file_, pos, SEEK_SET) == 0;
            else if (mode == SeekCur)
                return std::fseek(file_, pos, SEEK_CUR) == 0;
            else 
                return std::fseek(file_, pos, SEEK_END) == 0;
        }

        bool seek(uint32_t pos) { return std::fseek(file_, pos, SEEK_SET); }

        size_t write(const uint8_t *buf, size_t size) {
            return std::fwrite(buf, size, 1UL, file_);
        }

        size_t size() {
            auto current_position = ftell(file_);
            seek(0, SeekEnd);
            auto end = ftell(file_);
            seek(0, SeekSet);
            auto begin = ftell(file_);
            seek(current_position, SeekSet);
            return end - begin;
        }

    private:
        std::FILE * file_;
    };

    inline File open(const char *name, const char *mode)
    {
        std::string modeStr;
        if(modeStr.find("b") == std::string::npos)
            modeStr += "b";
        return File(std::fopen(name, modeStr.c_str()));
    }

    inline bool exists(const char *name)
    {
        return fs::exists(fs::path(name));
    }

    inline bool remove(const char *name)
    {
        return fs::remove(fs::path(name));
    }

    inline bool mkdir(const char *name)
    {
        return fs::create_directory(fs::path(name));
    }    

    //inline size_t totalBytes() {
    //    return 1024 * 1024 * 4; // some dummy value
    //}
    //inline size_t usedBytes() {
    //    // TODO: makes more sense to report total file size of all files in folder
    //    return 1024; // some dummy value 
    //}


} // namespace portablefs

#endif