/*
 * CiderPress
 * Copyright (C) 2007, 2008 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Reformatter base classes.  The main app does not need this header file.
 *
 * Every converter turns the source into text or graphics.  Currently it's
 * not possible to convert something into a mix of both.  We could change
 * that, but we'd have to figure out what that means when extracting a file
 * (i.e. figure out the RTF embedded bitmap format).
 */
#ifndef REFORMAT_REFORMATBASE_H
#define REFORMAT_REFORMATBASE_H

#include "Reformat.h"
#include "Charset.h"

#define BufPrintf fExpBuf.Printf


/*
 * Abstract base class for reformatting a file into readable text.
 *
 * The transmuted version is written on top of the original, or is allocated
 * in new[]ed storage and replaces the original (which is delete[]d).
 */
class Reformat {
public:
    Reformat(void) {}
    virtual ~Reformat(void) {}

    enum {
        kTypePCD    = 0x02,
        kTypePTX    = 0x03,
        kTypeTXT    = 0x04,
        kTypeBIN    = 0x06,
        kTypeFOT    = 0x08,
        kTypeBA3    = 0x09,
        kTypeDIR    = 0x0f,
        kTypeADB    = 0x19,
        kTypeAWP    = 0x1a,
        kTypeASP    = 0x1b,
        kType8OB    = 0x2b,
        kTypeP8C    = 0x2e,
        kTypeGWP    = 0x50,
        kTypeOBJ    = 0xb1,
        kTypeLIB    = 0xb2,
        kTypeFST    = 0xbd,
        kTypePNT    = 0xc0,
        kTypePIC    = 0xc1,
        kTypeCMD    = 0xf0,
        kTypeDOS_B  = 0xf4,     // alternate 'B'
        kTypeOS     = 0xf9,
        kTypeINT    = 0xfa,
        kTypeBAS    = 0xfc,
        kTypeSYS    = 0xff,
    };

    /* test applicability of all file parts */
    virtual void Examine(ReformatHolder* pHolder) = 0;

    /* reformat appropriately; returns 0 on success, -1 on error */
    virtual int Process(const ReformatHolder* pHolder,
        ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
        ReformatOutput* pOutput) = 0;

    // grab the next 8 bits
    static inline uint8_t Read8(const uint8_t** pBuf, long* pLength) {
        if (*pLength > 0) {
            (*pLength)--;
            return *(*pBuf)++;
        } else {
            // ought to throw an exception here
            ASSERT(false);
            return (uint8_t) -1;
        }
    }
    // grab a 16-bit little-endian value
    static inline uint16_t Read16(const uint8_t** pBuf, long* pLength) {
        uint16_t val;
        if (*pLength >= 2) {
            val = *(*pBuf)++;
            val |= *(*pBuf)++ << 8;
            *pLength -= 2;
        } else {
            // ought to throw an exception here
            ASSERT(false);
            val = (uint16_t) -1;
        }
        return val;
    }
    // grab a 32-bit little-endian value
    static inline uint32_t Read32(const uint8_t** pBuf, long* pLength) {
        uint32_t val;
        if (*pLength >= 4) {
            val = *(*pBuf)++;
            val |= *(*pBuf)++ << 8;
            val |= *(*pBuf)++ << 16;
            val |= *(*pBuf)++ << 24;
            *pLength -= 4;
        } else {
            // ought to throw an exception here
            ASSERT(false);
            val = (uint32_t) -1;
        }
        return val;
    }

    static inline uint16_t Get16LE(const uint8_t* buf) {
        return *buf | *(buf+1) << 8;
    }
    static inline uint32_t Get32LE(const uint8_t* buf) {
        return *buf | *(buf+1) << 8 | *(buf+2) << 16 | *(buf+3) << 24;
    }
    static inline uint16_t Get16BE(const uint8_t* buf) {
        return *buf << 8 | *(buf+1);
    }
    static inline uint32_t Get32BE(const uint8_t* buf) {
        return *buf << 24 | *(buf+1) << 16 | *(buf+2) << 8 | *(buf+3);
    }
    static inline uint16_t Get16(const uint8_t* buf, bool littleEndian) {
        if (littleEndian)
            return Get16LE(buf);
        else
            return Get16BE(buf);
    }
    static inline uint32_t Get32(const uint8_t* buf, bool littleEndian) {
        if (littleEndian)
            return Get32LE(buf);
        else
            return Get32BE(buf);
    }

private:
    DECLARE_COPY_AND_OPEQ(Reformat)
};

/*
 * Abstract base class for reformatting a graphics file into a
 * device-independent bitmap.
 */
class ReformatGraphics: public Reformat {
public:
    ReformatGraphics() {
        InitPalette();
    }

protected:
    void SetResultBuffer(ReformatOutput* pOutput, MyDIBitmap* pDib);

    /*
     * Color palette to use for color conversions.  We store it here
     * so it can be configured to suit the user's tastes.
     */
    enum { kPaletteBlack, kPaletteRed, kPaletteDarkBlue,
           kPalettePurple, kPaletteDarkGreen, kPaletteDarkGrey,
           kPaletteMediumBlue, kPaletteLightBlue, kPaletteBrown,
           kPaletteOrange, kPaletteLightGrey, kPalettePink,
           kPaletteGreen, kPaletteYellow, kPaletteAqua,
           kPaletteWhite, kPaletteSize };
    RGBQUAD fPalette[kPaletteSize];

    int UnpackBytes(uint8_t* dst, const uint8_t* src,
        long dstRem, long srcLen);
    void UnPackBits(const uint8_t** pSrcBuf, long* pSrcLen,
        uint8_t** pOutPtr, long dstLen, uint8_t xorVal);

private:
    DECLARE_COPY_AND_OPEQ(ReformatGraphics)

    void InitPalette();
};

/*
 * Abstract base class for reformatting a file into readable text.
 *
 * Includes an expanding buffer that can be appended to, and a set of RTF
 * primitives for adding structure.
 */
class ReformatText : public Reformat {
public:
    enum ParagraphJustify {
        kJustifyLeft,
        kJustifyRight,
        kJustifyCenter,
        kJustifyFull,
    };

    ReformatText()
      : fUseRTF(true),
        fLeftMargin(0),
        fRightMargin(0),
        fPointSize(8),
        fPreMultPointSize(8),
        fGSFontSizeMult(1.0),
        fBoldEnabled(false),
        fItalicEnabled(false),
        fUnderlineEnabled(false),
        fInverseEnabled(false),
        fOutlineEnabled(false),
        fShadowEnabled(false),
        fSuperscriptEnabled(false),
        fSubscriptEnabled(false),
        fJustified(kJustifyLeft),
        fTextColor(kColorNone)
    {}
    virtual ~ReformatText(void) {}

    /*
     * The numeric values are determined by the RTF header that we output.
     * If the header in RTFBegin() changes, update these values.
     */
    typedef enum RTFFont {
        // basic fonts (one monospace, one proportional, Courier New default)
        kFontCourierNew     = 0,
        kFontTimesRoman     = 1,
        kFontArial          = 2,
        kFontSymbol         = 3,
    } RTFFont;
    typedef enum {
        kColorNone          = 0,
        // full colors (RGB 0 or 255)
        kColorBlack         = 1,
        kColorBlue          = 2,
        kColorCyan          = 3,
        kColorGreen         = 4,        // a little bright for white bkgnd
        kColorPink          = 5,
        kColorRed           = 6,
        kColorYellow        = 7,
        kColorWhite         = 8,
        // mixed colors
        kColorMediumBlue    = 9,
        kColorMediumAqua    = 10,
        kColorMediumGreen   = 11,
        kColorMagena        = 12,
        kColorMediumRed     = 13,
        kColorOlive         = 14,
        kColorMediumGrey    = 15,
        kColorLightGrey     = 16,
        kColorDarkGrey      = 17,
        kColorOrange        = 18,
    } TextColor;

    /* Apple IIgs families */
    typedef enum GSFontFamily {
        // standard fonts, defined in toolbox ref
        kGSFontNewYork      = 0x0002,
        kGSFontGeneva       = 0x0003,   // sans-sarif, like Arial
        kGSFontMonaco       = 0x0004,   // monospace
        kGSFontVenice       = 0x0005,   // script font
        kGSFontLondon       = 0x0006,
        kGSFontAthens       = 0x0007,
        kGSFontSanFran      = 0x0008,
        kGSFontToronto      = 0x0009,
        kGSFontCairo        = 0x000b,
        kGSFontLosAngeles   = 0x000c,
        kGSFontTimes        = 0x0014,   // sarif, equal to Times New Roman
        kGSFontHelvetica    = 0x0015,   // sans-sarif, equal to Arial
        kGSFontCourier      = 0x0016,   // monospace, sarif, Courier
        kGSFontSymbol       = 0x0017,
        kGSFontTaliesin     = 0x0018,
        // I had these installed, by apps or by Pointless
        kGSFontStarfleet    = 0x078d,
        kGSFontWestern      = 0x088e,
        kGSFontGenoa        = 0x0bcb,
        kGSFontClassical    = 0x2baa,
        kGSFontChicago      = 0x3fff,
        kGSFontGenesys      = 0x7530,
        kGSFontPCMonospace  = 0x7fdc,   // monospace, sans-sarif
        kGSFontAppleM       = 0x7f58,   // looks like classic Apple II font
        kGSFontUnknown1     = 0x9c50,   // found in French AWGS doc "CONVSEC"
        kGSFontUnknown2     = 0x9c54,   // found in French AWGS doc "CONVSEC"
        // ROM font
        kGSFontShaston      = 0xfffe,   // rounded sans-sarif
    } GSFontFamily;

    /* QuickDraw II font styles; this is a bit mask */
    typedef enum QDFontStyle {
        kQDStyleBold            = 0x01,
        kQDStyleItalic          = 0x02,
        kQDStyleUnderline       = 0x04,
        kQDStyleOutline         = 0x08,
        kQDStyleShadow          = 0x10,
        kQDStyleReserved        = 0x20,
        kQDStyleSuperscript     = 0x40,     // not in QDII -- AWGS only
        kQDStyleSubscript       = 0x80,     // not in QDII -- AWGS only
    } QDFontStyle;

    /* flags for RTFBegin */
    enum {
        kRTFFlagColorTable  = 1,        // include color table
    };

protected:
    void RTFBegin(int flags = 0);
    void RTFEnd(void);
    void RTFSetPara(void);
    void RTFNewPara(void);
    void RTFPageBreak(void);
    void RTFTab(void);
    void RTFBoldOn(void);
    void RTFBoldOff(void);
    void RTFItalicOn(void);
    void RTFItalicOff(void);
    void RTFUnderlineOn(void);
    void RTFUnderlineOff(void);
    void RTFInverseOn(void);
    void RTFInverseOff(void);
    void RTFParaLeft(void);
    void RTFParaRight(void);
    void RTFParaCenter(void);
    void RTFParaJustify(void);
    void RTFSubscriptOn(void);
    void RTFSubscriptOff(void);
    void RTFSuperscriptOn(void);
    void RTFSuperscriptOff(void);
    void RTFOutlineOn(void);
    void RTFOutlineOff(void);
    void RTFShadowOn(void);
    void RTFShadowOff(void);
    TextColor RTFSetColor(TextColor color);
    void RTFSetFont(RTFFont font);
    void RTFSetFontSize(int points);
    void RTFLeftMargin(int margin);
    void RTFRightMargin(int margin);
    void RTFSetGSFont(uint16_t family);
    void RTFSetGSFontSize(int points);
    void RTFSetGSFontStyle(uint8_t qdStyle);
//  void RTFProportionalOn(void);
//  void RTFProportionalOff(void);

    void ConvertEOL(const uint8_t* srcBuf, long srcLen,
        bool stripHiBits);
    void ConvertEOL(const uint8_t* srcBuf, long srcLen,
        bool stripHiBits, bool stripNulls);
    void BufHexDump(const uint8_t* srcBuf, long srcLen);
    void SetResultBuffer(ReformatOutput* pOutput, bool multiFont = false);

    ExpandBuffer    fExpBuf;
    bool            fUseRTF;

    // return a low-ASCII character so we can read high-ASCII files
    inline char PrintableChar(uint8_t ch) {
        if (ch < 0x20)
            return '.';
        else if (ch < 0x7f)
            return ch;
        else if (ch < 0xa0 || ch == 0xff)   // 0xff becomes 0x7f
            return '.';
        else
            return ch & 0x7f;
    }
    // output an RTF-escaped char (do we want to trap Ctrl-Z?)
    // (only use this if we're in RTF mode)
    inline void RTFPrintChar(uint8_t ch) {
        char pch = PrintableChar(ch);
        RTFPrintUTF16Char(pch);
    }
    // output an RTF-escaped char, allowing UTF-16 Unicode values
    // (only use this if we're in RTF mode)
    inline void RTFPrintUTF16Char(uint16_t ch) {
        if (ch == '\\') {
            fExpBuf.Printf("\\\\");
        } else if (ch == '{') {
            fExpBuf.Printf("\\{");
        } else if (ch == '}') {
            fExpBuf.Printf("\\}");
        } else if (ch >= 0x20 && ch < 0x80) {
            // don't use Unicode escapes for these, or the output will be
            // unreadable by mere humans
            fExpBuf.Printf("%c", ch);
        } else {
            // must print as a *signed* 16-bit decimal value, though it
            // looks like most parsers work either way
            fExpBuf.Printf("\\u%d?", (int16_t)ch);
        }
    }
    // output a char, doubling up double quotes (for .CSV)
    inline void BufPrintQChar(uint8_t ch) {
        if (ch == '"')
            fExpBuf.Printf("\"\"");
        else
            fExpBuf.Printf("%c", ch);
    }

    // Converts a MouseText value (0-31) to a 16-bit UTF-16 value.  If
    // the character is in the BMP then only low is used.  If it requires
    // encoding as a surrogate pair, high will be nonzero.
    void MouseTextToUTF16(uint8_t mtVal, uint16_t* pLow, uint16_t* pHigh);

    // Converts a MouseText value (0-31) to an ASCII equivalent.  This is not
    // a 1:1 conversion, so it's not reversible.
    int8_t MouseTextToASCII(uint8_t mtVal);

private:
    DECLARE_COPY_AND_OPEQ(ReformatText)
    int CreateWorkBuf(void);

    enum { kRTFUnitsPerInch = 1440 };   // TWIPS

    int     fLeftMargin, fRightMargin;  // for documents, in 1/10th inch
    int     fPointSize;
    int     fPreMultPointSize;
    float   fGSFontSizeMult;
    bool    fBoldEnabled;
    bool    fItalicEnabled;
    bool    fUnderlineEnabled;
    bool    fInverseEnabled;
    bool    fOutlineEnabled;
    bool    fShadowEnabled;
    bool    fSuperscriptEnabled;
    bool    fSubscriptEnabled;
    ParagraphJustify    fJustified;
    TextColor   fTextColor;
};

#endif /*REFORMAT_REFORMATBASE_H*/
