/*
Crafter®.Asset
Copyright (C) 2025 Catcrafts®
Catcrafts.net

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
*/

#include <iostream>
#include <string>
#include <filesystem>
#include <unordered_map>
#include <vector>
#include <functional>

import Crafter.Asset;
using namespace Crafter;
namespace fs = std::filesystem;

int main(int argc, char* argv[]) {
    if(argc == 1) {
        std::cout << "No arguments provided, use help for help" << std::endl;
        return -1;
    }

    std::string command = std::string(argv[1]);

    if(command == "help") {
        std::cout << "help: Displays this help message.\nadd {file} {entry file} {entry type} {serializer} {entry name}: Adds an entry to an existing assset file or creates a new file if the file in {file} does not exist, {entry file} is the path to the file you want to add to the asset, {entry type} is the type of the resulting entry, {serializer} is the serializer to use to add the entry file to the asset, {entry name} is the name of the resulting entry\nremove {file} {entry}\nextract {file} {entry name} {extractor} {extracted path}: Extracts a entry with the name {entry name} from the asset file in {file}, using the extractor in {extractor} writes the result to the file {extracted path}\nlist serialize: Shows a list of the support serializers\nlist {file}: Lists all entries in the asset file\nlist extract: Shows a list of supported extractors" << std::endl;
    } else if(command == "list") {
        if(argc == 2) {
            std::cout << "Unkown command, use help for help" << std::endl;
            return -1;
        }
        std::string command2 = std::string(argv[2]);
        if(command2 == "serialize") {
            for (const auto& pair : serializers) {
                std::cout << pair.first << std::endl;
            }
        } else if(command2 == "extract") {
            for (const auto& pair : extractors) {
                std::cout << pair.first << std::endl;
            }
        }  else { 
            if(!fs::exists(argv[2])) {
                std::cout << "Asset file does not exist" << std::endl;
                return -1;
            }
            AssetLoad asset(argv[2]);
            std::cout << "<name>\t<type>\t<size>" << std::endl;
            for(const AssetEntry& entry : asset.entries) {
                std::cout << std::format("{}\t{}\t{}", entry.name, entry.type, entry.lenght) << std::endl;
            }
        }
    } else if(command == "remove") {
        if(argc == 2) {
            std::cout << "Missing argument {file}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 3) {
            std::cout << "Missing argument {enty name}. Run help for help" << std::endl;
            return -1;
        }
        if(!fs::exists(argv[2])) {
            std::cout << "Asset file does not exist" << std::endl;
            return -1;
        }
        AssetLoad asset(argv[2]);

        if(asset.entries.size() == 0) {
            std::cout << "Nothing to remove, asset file empty" << std::endl;
        }
        for (auto it = asset.entries.begin(); it != asset.entries.end(); std::advance(it, 1)) {
            if (it->name == argv[3]) {
                asset.entries.erase(it);
                fs::remove(argv[2]);
                AssetSave ass2(asset.entries, std::move(asset.LoadAll()));
                ass2.Save(argv[2]);
                return 0;
            }
            std::cout << std::format("Entry {} not found", argv[3]) << std::endl;
            return -1;
        }
    } else if(command == "add") {
        if(argc == 2) {
            std::cout << "Missing argument {file}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 3) {
            std::cout << "Missing argument {entry file}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 4) {
            std::cout << "Missing argument {entry type}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 5) {
            std::cout << "Missing argument {serializer}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 6) {
            std::cout << "Missing argument {entry name}. Run help for help" << std::endl; 
            return -1;
        }
        AssetEntry entry;
        entry.name = std::string(argv[6]);
        entry.type = std::string(argv[4]);
        std::unordered_map<std::string, std::function<std::vector<char>(fs::path)>>::const_iterator pos = serializers.find(argv[5]);
        if (pos == serializers.end()) {
            std::cout << std::format("Unkown serializer {}", argv[5]) << std::endl;
            return -1;
        } else {
            AssetSave asset;
            if(fs::exists(argv[2])) {
                AssetLoad assload(argv[2]);
                asset = AssetSave(assload.entries, std::move(assload.LoadAll()));
                fs::remove(argv[2]);
            }

            asset.AddEntry(entry.name, entry.type, pos->second(argv[3]));
            asset.Save(argv[2]);
        }
    } else if(command == "extract") {
        if(argc == 2) {
            std::cout << "Missing argument {file}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 3) {
            std::cout << "Missing argument {entry name}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 4) {
            std::cout << "Missing argument {extractor}. Run help for help" << std::endl;
            return -1;
        } else if(argc == 5) {
            std::cout << "Missing argument {extracted path}. Run help for help" << std::endl;
            return -1;
        }
        if(!fs::exists(argv[2])) {
            std::cout << "Asset file does not exist" << std::endl;
            return -1;
        }
        AssetLoad asset(argv[2]);
        auto pos = extractors.find(argv[4]);
        if (pos == extractors.end()) {
            std::cout << std::format("Unkown extractor {}", argv[4]) << std::endl;
            return -1;
        } else {
            for(const AssetEntry& entry : asset.entries) {
                if(entry.name == std::string(argv[3])) {
                    std::vector<char> loaded = asset.Load(entry);
                    pos->second(argv[5], loaded.data());
                }
            }
            std::cout << std::format("Entry {} not found", argv[3]) << std::endl;
            return -1;
        }
    } else {
       std::cout << "Unkown command, use help for help" << std::endl;
    }
}