/***************************************************************************
 *   Copyright (C) 2005 by David Saxton                                    *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#ifndef LANGUAGE_H
#define LANGUAGE_H

#include <QObject>
#include <QStringList>

class FlowCodeDocument;
class KTechlab;
class LogView;
class MessageInfo;
class MicroSettings;
class OutputMethodInfo;
class ProcessChain;
class ProcessOptions;
class TextDocument;
class QProcess;

typedef QList<ProcessOptions> ProcessOptionsList;

class ProcessOptionsSpecial
{
public:
    ProcessOptionsSpecial();

    bool b_addToProject;
    bool b_forceList;
    QString m_picID;
    FlowCodeDocument *p_flowCodeDocument;

    // Linking
    QString m_hexFormat;
    bool m_bOutputMapFile;
    QString m_libraryDir;
    QString m_linkerScript;
    QStringList m_linkLibraries;
    QString m_linkOther;

    // Programming
    QString m_port;
    QString m_program;
};

class ProcessOptionsHelper : public QObject
{
    Q_OBJECT
#define protected public
signals:
#undef protected
    void textOutputtedTo(TextDocument *outputtedTo);
};

class ProcessOptions : public ProcessOptionsSpecial
{
public:
    ProcessOptions();
    ProcessOptions(OutputMethodInfo info);

    class ProcessPath
    {
    public:
        enum MediaType {
            AssemblyAbsolute,
            AssemblyRelocatable,
            C,
            Disassembly,
            FlowCode,
            Library,
            Microbe,
            Object,
            Pic,
            Program,

            Unknown // Used for guessing the media type
        };

        enum Path                   // From_To				// processor that will be invoked first
        { AssemblyAbsolute_PIC,     // gpasm (indirect)
          AssemblyAbsolute_Program, // gpasm (direct)

          AssemblyRelocatable_Library, // gpasm (indirect)
          AssemblyRelocatable_Object,  // gpasm (direct)
          AssemblyRelocatable_PIC,     // gpasm (indirect)
          AssemblyRelocatable_Program, // gpasm (indirect)

          C_AssemblyRelocatable, // sdcc (direct)
          C_Library,             // sdcc (indirect)
          C_Object,              // sdcc (indirect)
          C_PIC,                 // sdcc (indirect)
          C_Program,             // sdcc (indirect)

          FlowCode_AssemblyAbsolute, // flowcode (indirect)
          FlowCode_Microbe,          // flowcode (direct)
          FlowCode_PIC,              // flowcode (indirect)
          FlowCode_Program,          // flowcode (indirect)

          Microbe_AssemblyAbsolute, // microbe (direct)
          Microbe_PIC,              // microbe (indirect)
          Microbe_Program,          // microbe (indirect)

          Object_Disassembly, // gpdasm (direct)
          Object_Library,     // gplib (direct)
          Object_PIC,         // gplink (indirect)
          Object_Program,     // gplink (direct)

          PIC_AssemblyAbsolute, // download from pic (direct)

          Program_Disassembly, // gpdasm (direct)
          Program_PIC,         // upload to pic (direct)

          Invalid, // From and to types are incompatible
          None     // From and to types are the same
        };

        static Path path(MediaType from, MediaType to);
        static MediaType from(Path path);
        static MediaType to(Path path);
    };

    class Method
    {
    public:
        enum type {
            Forget,    // Don't do anything after processing successfully
            LoadAsNew, // Load the output as a new file
            Load       // Load the output file
        };
    };

    /**
     * Tries to guess the media type from the url (and possible the contents
     * of the file as well).
     * @param url a path to a file in the local filesystem
     */
    static ProcessPath::MediaType guessMediaType(const QString &url);
    /**
     * The *final* target file (not any intermediatary ones)
     */
    QString targetFile() const
    {
        return m_targetFile;
    }
    /**
     * This sets the final target file, as well as the initial intermediatary one
     */
    void setTargetFile(const QString &file);

    void setIntermediaryOutput(const QString &file)
    {
        m_intermediaryFile = file;
    }
    QString intermediaryOutput() const
    {
        return m_intermediaryFile;
    }

    /**
     * As paths in local filesystem
     */
    void setInputFiles(const QStringList &files)
    {
        m_inputFiles = files;
    }
    QStringList inputFiles() const
    {
        return m_inputFiles;
    }

    void setMethod(Method::type method)
    {
        m_method = method;
    }
    Method::type method() const
    {
        return m_method;
    }

    void setProcessPath(ProcessPath::Path path)
    {
        m_processPath = path;
    }
    ProcessPath::Path processPath() const
    {
        return m_processPath;
    }

    /**
     * If the output is text; If the user has selected (in config options)
     * ReuseSameViewForOutput, then the given TextDocument will have its
     * text set to the output if the TextDocument is not modified and has
     * an empty url. Otherwise a new TextDocument will be created. Either
     * way, once the processing has finished, a signal will be emitted
     * to the given receiver passing a TextDocument * as an argument. This
     * is not to be confused with setTextOutputtedTo, which is called once
     * the processing has finished, and will call-back to the slot given.
     */
    void setTextOutputTarget(TextDocument *target, QObject *receiver, const char *slot);
    /**
     * @see setTextOutputTarget
     */
    TextDocument *textOutputTarget() const
    {
        return m_pTextOutputTarget;
    }
    /**
     * @see setTextOuputTarget
     */
    void setTextOutputtedTo(TextDocument *outputtedTo);

protected:
    TextDocument *m_pTextOutputTarget;
    ProcessOptionsHelper *m_pHelper;
    bool b_targetFileSet;
    QStringList m_inputFiles;
    QString m_targetFile;
    QString m_intermediaryFile;
    Method::type m_method;
    ProcessPath::Path m_processPath;
};

/**
@author Daniel Clarke
@author David Saxton
*/
class Language : public QObject
{
    Q_OBJECT
public:
    Language(ProcessChain *processChain, const QString &name);
    ~Language() override;

    /**
     * Compile / assemble / dissassembly / whatever the given input.
     */
    virtual void processInput(ProcessOptions options) = 0;
    /**
     * Return the ProcessOptions object current state
     */
    ProcessOptions processOptions() const
    {
        return m_processOptions;
    }
    /**
     * Return the output path from the given input path. Will return None
     * if we've done processing.
     */
    virtual ProcessOptions::ProcessPath::Path outputPath(ProcessOptions::ProcessPath::Path inputPath) const = 0;

signals:
    /**
     * Emitted when the processing was successful.
     * @param language Pointer to this class
     */
    void processSucceeded(Language *language);
    /**
     * Emitted when the processing failed.
     * @param language Pointer to this class
     */
    void processFailed(Language *language);

protected:
    /**
     * Examines the string for the line number if applicable, and creates a new
     * MessageInfo for it.
     */
    virtual MessageInfo extractMessageInfo(const QString &text);

    /**
     * Reset the error count
     */
    void reset();
    void outputMessage(const QString &message);
    void outputWarning(const QString &message);
    void outputError(const QString &error);
    void finish(bool successful);

    int m_errorCount;
    ProcessOptions m_processOptions;
    ProcessChain *p_processChain;

    /**
     * A message appropriate to the language's success after compilation or similar.
     */
    QString m_successfulMessage;
    /**
     * A message appropriate to the language's failure after compilation or similar.
     */
    QString m_failedMessage;
};

#endif
