#include <iostream>
#include <fstream>
#include <cstring>
#include <string>

int str2num(const char* str) {
    int num = 0;
    for (int i = 0; str[i] != '\0'; ++i) {
        num = num * 10 + str[i] - '0';
    }
    return num;
}

std::string num2str(int num) {
    std::string str;
    if (num == 0) {
        str = "0";
    } else {
        while (num > 0) {
            str += num % 10 + '0';
            num /= 10;
        }
    }
    while (str.size() < 2) {
        str += '0';
    }
    for (int i = 0; i < (int)str.size()/2; ++i) {
        std::swap(str[i], str[(int)str.size()-1-i]);
    }
    return str;
}

void getFileName(const std::string& path, std::string& fileName) {
    int pos = (int)path.size() - 1;
    for (; pos >= 0; --pos) {
        if (path[pos] == '/') {
            break;
        }
    }
    fileName = path.substr(pos+1);
}

void formatDirPath(std::string& dirPath) {
    if (!dirPath.empty() || dirPath.back() != '/') {
        dirPath += '/';
    }
}

int getFileSize(std::ifstream& fin) {
    fin.seekg(0, fin.end);
    int fileSize = fin.tellg();
    fin.seekg(0);
    return fileSize;
}

void readBinFile(const std::string& path, int& bufferSize, char*& buffer) {
    std::ifstream fin(path, std::ios::binary);
    if (!fin.is_open()) {
        std::cout << "Cannot open " << path << '\n';
        return;
    }
    bufferSize = getFileSize(fin);
    buffer = new char[bufferSize];
    fin.read(buffer, bufferSize);
    fin.close();
}

void writeBinFile(const std::string& path, int bufferSize, int start, int maxchar, char* buffer) {
    std::ofstream fout(path, std::ios::binary);
    if (!fout.is_open()) {
        std::cout << "Cannot open " << path << '\n';
        return;
    }
    fout.write(buffer+start, std::min(maxchar, bufferSize - start));
    fout.close();
}

int main(int argc, char** argv) {
    std::string filePath;
    std::string outDir;
    int numPart = -1;
    int sizePart = -1; 

    const int numArg = 7;
    if (argc != numArg) {
        std::cout << "Invalid arguments\n";
        return 0;
    } else {
        for (int i = 1; i < numArg; i+=2) {
            if (strcmp(argv[i], "-s") == 0) {
                filePath = argv[i+1];
            } else if (strcmp(argv[i], "-d") == 0) {
                outDir = argv[i+1];
            } else if (strcmp(argv[i], "-numpart") == 0) {
                numPart = str2num(argv[i+1]);
            } else if (strcmp(argv[i], "-sizeapart") == 0) {
                sizePart = str2num(argv[i+1]);
            }
        }
        if (filePath.empty()) {
            std::cout << "Source file not found in arguments!\n";
            return 0;
        } 
        if (outDir.empty()) {
            std::cout << "Destination not found in arguments!\n";
            return 0;
        }
        if (numPart == -1 && sizePart == -1) {
            std::cout << "numpart and sizeapart not found in arguments!\n";
            return 0;
        }
    }

    std::ifstream fin(filePath, std::ios::binary);
    if (!fin.is_open()) {
        std::cout << "Cannot open " << filePath << '\n';
        return 0;
    }
    if (numPart == -1) {
        numPart = (getFileSize(fin) + sizePart - 1) / sizePart;
    } else {
        sizePart = (getFileSize(fin) + numPart - 1) / numPart;
    }
    fin.close();

    std::string fileName;
    getFileName(filePath, fileName);

    formatDirPath(outDir);

    int bufferSize;
    char* buffer = nullptr;
    readBinFile(filePath, bufferSize, buffer);

    for (int i = 1; i <= numPart; ++i) {
        std::string outPath = outDir + fileName + ".part" + num2str(i);
        writeBinFile(outPath, bufferSize, sizePart * (i-1), sizePart, buffer);
    }

    delete[] buffer;

    return 0;
}