//  SuperTuxKart - a fun racing game with go-kart
//  Copyright (C) 2010-2015 Lucas Baudin
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 3
//  of the License, or (at your option) any later version.
//
//  This program 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#include <string.h>
#include <iostream>
#include <fstream>

#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"

#include <IWriteFile.h>
using namespace irr;
using namespace io;
s32 IFileSystem_copyFileToFile(IWriteFile* dst, IReadFile* src)
{
  char buf[1024];
  const s32 sz = sizeof(buf) / sizeof(*buf);

  s32 rx = src->getSize();
  for (s32 r = 0; r < rx; /**/)
  {
    s32 wx = src->read(buf, sz);
    for (s32 w = 0; w < wx; /**/)
    {
      s32 n = dst->write(buf + w, wx - w);
      if (n < 0)
        return -1;
      else
        w += n;
    }
    r += wx;
  }
  return rx;
}   // IFileSystem_copyFileToFile

// ----------------------------------------------------------------------------
/** Extracts all files from the zip archive 'from' to the directory 'to'.
 *  \param from A zip archive.
 *  \param to The destination directory.
 *  \return True if successful.
 */
bool extract_zip(const std::string &from, const std::string &to, bool recursive)
{
    //Add the zip to the file system
    IFileSystem *file_system = irr_driver->getDevice()->getFileSystem();
    if(!file_system->addFileArchive(from.c_str(),
                                    /*ignoreCase*/false,
                                   /*ignorePath*/!recursive, io::EFAT_ZIP))
    {
        return false;
    }

    // Get the recently added archive, which is necessary to get a
    // list of file in the zip archive.
    io::IFileArchive *zip_archive =
        file_system->getFileArchive(file_system->getFileArchiveCount()-1);
    const io::IFileList *zip_file_list = zip_archive->getFileList();
    // Copy all files from the zip archive to the destination
    bool error = false;
    for(unsigned int i=0; i<zip_file_list->getFileCount(); i++)
    {
        if(zip_file_list->isDirectory(i)) continue;
        if(zip_file_list->getFileName(i)[0]=='.') continue;
        std::string base = zip_file_list->getFullFileName(i).c_str();
        if (!recursive)
            base = StringUtils::getBasename(base);

        Log::debug("addons", "Unzipping file '%s'.", base.c_str());

        IReadFile* src_file =
            zip_archive->createAndOpenFile(base.c_str());
        if(!src_file)
        {
            Log::warn("addons", "Can't read file '%s'. This is ignored, but the addon might not work", base.c_str());
            error = true;
            continue;
        }

        std::string file_location = to + "/" + base;
        if (recursive)
        {
            const std::string& dir = StringUtils::getPath(file_location);
            file_manager->checkAndCreateDirectoryP(dir);
        }
        IWriteFile* dst_file =
            file_system->createAndWriteFile(file_location.c_str());
        if(dst_file == NULL)
        {
            Log::warn("addons", "Couldn't create the file '%s'. The directory might not exist. This is ignored, but the addon might not work.", file_location.c_str());
            error = true;
            continue;
        }

        if (IFileSystem_copyFileToFile(dst_file, src_file) < 0)
        {
            Log::warn("addons", "Could not copy '%s' from archive '%s'. This is ignored, but the addon might not work.",
                      base.c_str(), from.c_str());
            error = true;
        }
        dst_file->drop();
        src_file->drop();
    }
    // Remove the zip from the filesystem to save memory and avoid
    // problem with a name conflict. Note that we have to convert
    // the path using getAbsolutePath, otherwise windows name
    // will not be detected correctly (e.g. if from=c:\...  the
    // stored filename will be c:/..., which then does not match
    // on removing it. getAbsolutePath will convert all \ to /.
    file_system->removeFileArchive(file_system->getAbsolutePath(from.c_str()));

    return !error;
}   // extract_zip
