/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2021 A. Stebich (librecad@mail.lordofbikes.de)
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file gpl-2.0.txt included in the
** packaging of this file.
**
** 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
** This copyright notice MUST APPEAR in all copies of the script!
**
**********************************************************************/

#include <cstddef>
#include <QFileInfo>
#include <QTextStream>
#ifdef DWGSUPPORT
#include <QMessageBox>
#include <QApplication>
#endif
#include "rs_fileio.h"
#include "rs_filtercxf.h"
#include "rs_filterdxf1.h"
#include "rs_filterjww.h"
#include "rs_filterlff.h"
#include "rs_filterdxfrw.h"
#include "rs_debug.h"

/**
 * Calls the import method of the filter responsible for the format
 * of the given file.
 *
 * @param graphic The container to which we will add
 *        entities. Usually that's an RS_Graphic entity but
 *        it can also be a polyline, text, ...
 * @param file Path and name of the file to import.
 */
bool RS_FileIO::fileImport(RS_Graphic& graphic, const QString& file,
        RS2::FormatType type) {

    RS_DEBUG->print("Trying to import file '%s'...", file.toLatin1().data());

    RS2::FormatType t;
    if (type == RS2::FormatUnknown) {
        t = detectFormat(file);
    }
    else {
        t = type;
    }

    if (RS2::FormatUnknown != t) {
		std::unique_ptr<RS_FilterInterface>&& filter(getImportFilter(file, t));
		if (filter){
#ifdef DWGSUPPORT
            bool isDwg {file.endsWith( ".dwg", Qt::CaseInsensitive)};
            if (isDwg) {
                QApplication::restoreOverrideCursor();  // disable WaitCursor for massagebox

                // use QStringList to avoid "\n" in translation strings
                QStringList info { QObject::tr("DWG support is not complete!"),
                                   "",
                                   QObject::tr("If this file fails to open try an older DWG format"),
                                   QObject::tr("or try to find a converter to make it a DXF file.") };

                QMessageBox::information( qApp->activeWindow(),
                                          QObject::tr("Information"),
                                          info.join( "\n"),
                                          QMessageBox::Ok,
                                          QMessageBox::NoButton);
                QApplication::setOverrideCursor( QCursor(Qt::WaitCursor));
            }
#endif
            bool bImported {filter->fileImport(graphic, file, t)};
            if (!bImported) {
                QApplication::restoreOverrideCursor();  // disable WaitCursor for massagebox

                QString strTitle {QObject::tr("Error", "fileImport")};
                QString strError {QObject::tr("Import error:", "fileImport")};
                QString strLastError( "    %1");
#ifdef DWGSUPPORT
                if (isDwg) {
                    if (graphic.isEmpty()) {
                        QMessageBox::critical( qApp->activeWindow(),
                                               strTitle,
                                               QStringList( {strError, strLastError}).join( "\n").arg( filter->lastError()),
                                               QMessageBox::Ok,
                                               QMessageBox::NoButton);
                    }
                    else {
                        QStringList message { strError,
                                    strLastError,
                                    "",
                                    QObject::tr("Anyhow, there are some entities identified.", "dwgImport"),
                                    QObject::tr("If you open the file now, the drawing may be not complete or unusable.", "dwgImport"),
                                    "",
                                    QObject::tr("Ignore error and open the file?", "dwgImport"),
                                         };
                        QMessageBox::StandardButton answer = QMessageBox::warning( qApp->activeWindow(),
                                                                                   QObject::tr("Warning"),
                                                                                   message.join( "\n").arg( filter->lastError()),
                                                                                   QMessageBox::Yes | QMessageBox::No,
                                                                                   QMessageBox::NoButton);
                        if (QMessageBox::Yes == answer) {
                            return true;   // open the file anyhow
                        }
                    }
                }
                else
#endif
                {
                    QMessageBox::critical( qApp->activeWindow(),
                                           strTitle,
                                           QStringList( {strError, strLastError}).join( "\n").arg( filter->lastError()),
                                           QMessageBox::Ok,
                                           QMessageBox::NoButton);
                }
                QApplication::setOverrideCursor( QCursor(Qt::WaitCursor));
            }

            return bImported;
        }
        RS_DEBUG->print(RS_Debug::D_WARNING,
                        "RS_FileIO::fileImport: failed to import file: %s",
                        file.toLatin1().data());
    }
    else {
        RS_DEBUG->print(RS_Debug::D_WARNING,
                        "RS_FileIO::fileImport: failed to detect file format: %s",
                        file.toLatin1().data());
    }

    return false;
}


/** \brief extension2Type convert extension to file format type
 * \param file type
 * \param verifyRead read the file to verify dxf/dxfrw type, default to false
 * \return file format type
 */
RS2::FormatType RS_FileIO::detectFormat(QString const& file, bool forRead)
{
	// look up table
	std::map<QString, RS2::FormatType> list{
		{"dxf", RS2::FormatDXFRW},
		{"cxf", RS2::FormatCXF},
		{"lff", RS2::FormatLFF}
	};
	// only read support for dwg
	if(forRead) list["dwg"]=RS2::FormatDWG;

	QString const extension = QFileInfo(file).suffix().toLower();
	RS2::FormatType type=(list.find(extension)!=
			list.end()) ? list[extension]:RS2::FormatUnknown;

	//only read dxf to verify
	if (forRead && type==RS2::FormatDXFRW) {
		type = RS2::FormatDXFRW;
		QFile f(file);

		if (!f.open(QIODevice::ReadOnly)) {
			// Error opening file:
			RS_DEBUG->print(RS_Debug::D_WARNING,
							"%s:"
							"Cannot open file: %s",
							__func__,
							file.toLatin1().data());
			type = RS2::FormatUnknown;
		} else {
			RS_DEBUG->print("%s:"
							"Successfully opened DXF file: %s",
							__func__,
							file.toLatin1().data());

			QTextStream ts(&f);
			QString line;
			int c=0;
			while (!ts.atEnd() && ++c<100) {
				line = ts.readLine();
				if (line=="$ACADVER" || line=="ENTITIES") {
					type = RS2::FormatDXFRW;
					break;
				}
				// very simple reduced DXF:
				//                if (line=="ENTITIES" && c<10) {
				//                    type = RS2::FormatDXFRW;
				//                }
			}
			f.close();
		}
	}
	return type;
}

/**
 * Calls the export method of the object responsible for the format
 * of the given file.
 *
 * @param file Path and name of the file to import.
 */
bool RS_FileIO::fileExport(RS_Graphic& graphic, const QString& file,
        RS2::FormatType type) {

    RS_DEBUG->print("RS_FileIO::fileExport");
    //RS_DEBUG->print("Trying to export file '%s'...", file.latin1());

    if (type==RS2::FormatUnknown) {
		type=detectFormat(file, false);
    }

	std::unique_ptr<RS_FilterInterface>&& filter(getExportFilter(file, type));
	if (filter){
        return filter->fileExport(graphic, file, type);
    }
    RS_DEBUG->print("RS_FileIO::fileExport: no filter found");

    return false;
}


RS_FileIO* RS_FileIO::instance() {
	static RS_FileIO* uniqueInstance=nullptr;
	if (!uniqueInstance) {
		uniqueInstance = new RS_FileIO();
	}
	return uniqueInstance;
}

/**
 * @return Filter which can import the given file type.
 */
std::unique_ptr<RS_FilterInterface> RS_FileIO::getImportFilter(const QString &fileName,
									RS2::FormatType t) const{
	for(auto f: getFilters()){
		std::unique_ptr<RS_FilterInterface> filter(f());
		if(filter &&
				filter->canImport(fileName, t)){
			return filter;
		}
	}
	return nullptr;
}

/**
 * @return Filter which can export the given file type.
 */
std::unique_ptr<RS_FilterInterface> RS_FileIO::getExportFilter(const QString &fileName,
									RS2::FormatType t) const{
	for(auto f: getFilters()){
		std::unique_ptr<RS_FilterInterface> filter(f());
		if(filter &&
				filter->canExport(fileName, t)){
			return filter;
		}
	}
	return nullptr;
}

std::vector<std::function<RS_FilterInterface*()>> RS_FileIO::getFilters()
{
												  return {
												  RS_FilterLFF::createFilter
												  ,RS_FilterDXFRW::createFilter
												  ,RS_FilterCXF::createFilter
												  ,RS_FilterJWW::createFilter
												  ,RS_FilterDXF1::createFilter
												  };
}

// EOF
