/* rule.hpp -- Output rules
* by [email protected] Fri Mar 15 15:11:38 CET 2002
*/

#ifdef __GNUC__
#ifndef __clang__
#pragma interface
#endif
#endif

#ifndef RULE_HPP
#define RULE_HPP 1

#include "config2.h"
#include "gensio.hpp"
#include "image.hpp"
#include "minips.hpp"

/** a namespace */
class Rule { public:
 struct Cache {
   bool WarningOK;
   BEGIN_STATIC_ENUM(unsigned char,ff_t)
     FF_default=0, /* only for one-liner */
     FF_eps=71, /* only for one-liner, temporary */
     FF_pdf=72, /* only for one-liner, temporary */
     FF_pdfb=73, /* only for one-liner, temporary */
     FF_PSL1=110,
     FF_PSLC=115,
     FF_PSL2=120,
     FF_PSL3=130,
     FF_PDFB10=90,
     FF_PDFB12=92,
     FF_PDF10=80,
     FF_PDF12=82,
     FF_GIF89a=1,
     FF_PNM=2,
     FF_PAM=3,
     FF_PIP=4, /* Portable Indexed file format by pts */
     FF_Empty=5,
     FF_Meta=6,
     FF_JPEG=7,
     FF_TIFF=8,
     FF_PNG=9,
     FF_XPM=10,
     FF_BMP=11,
     FF_XWD=12,
     FF_X11=13  /* Don't add a comma, gcc-4.8.1. can't compile it that way. */
   END_STATIC_ENUM()
   ff_t FileFormat;
   Image::sf_t SampleFormat;
   /** Used only in appliers.cpp when _check_rule() changes SampleFormat so
    * setSampleFormat() called before _work() would set that dummy
    * SampleFormat.
    */
   Image::sf_t origSampleFormat;
   BEGIN_STATIC_ENUM(unsigned char,te_t)
     TE_default=0, /* only for one-liner */
     TE_Binary=1,
     TE_ASCII=2,
     TE_Hex=3,
     TE_A85=4,
     TE_MSBfirst=5,
     TE_LSBfirst=6
   END_STATIC_ENUM()
   te_t TransferEncoding;

   BEGIN_STATIC_ENUM(unsigned char,co_t)
     CO_default=0, /* only for one-liner */
     CO_None=1,
     CO_LZW=2,
     CO_ZIP=3,
     CO_RLE=4,
     CO_Fax=5,
     CO_DCT=6,
     CO_IJG=7,
     CO_JAI=8,
     CO_max=8
   END_STATIC_ENUM()
   co_t Compression;

   BEGIN_STATIC_ENUM(unsigned char,pr_t)
     PR_None=1,
     PR_TIFF2=2,
     PR_PNGNone=10, PR_PNG=10,
     PR_PNGSub=11,
     PR_PNGUP=12,
     PR_PNGAverage=13,
     PR_PNGPaeth=14,
     PR_PNGAuto=15,
     PR_PNGAutoMaybe=25,
     PR_PNGAutoBadUnsigned=45,
     PR_PNGAutoBadSigned=55
   END_STATIC_ENUM()
   pr_t Predictor;

   Image::Sampled::rgb_t Transparent;

   bool isOneBit() const;
   bool isPDF() const;
   bool isPDFB() const;
   bool isPS() const;
   bool isPSL2() const;
   bool isPSL3() const;
   bool hasPredictor() const;
   bool isIndexed() const;
   bool isTransparentM() const;
   bool isGray() const;
   bool isRGB() const;
   bool isDCTE() const;
   bool isBinSB() const;
   /** /Compression/ZIP not OK with /PSL1, /PSLC, /PSL2, /PDF10, /PDFB10 */
   bool isZIPOK() const;
   // bool is8() const;

   /** @param s is case insensitive, leading `/' is optional.
    * @return SF_max if not found, or a valid SF_... constant
    */
   static Image::sf_t parseSampleFormat(char const*s, slen_t slen);
   static char const* dumpFileFormat(ff_t FileFormat, co_t Compression=CO_default);
   static char const* dumpTransferEncoding(te_t TransferEncoding);
   static char const* dumpSampleFormat(Image::sf_t SampleFormat);
   static char const* dumpCompression(co_t Compression);
 };

 struct CacheHints {
   BEGIN_STATIC_ENUM(unsigned char,sc_t) /* Fri Aug 16 16:33:47 CEST 2002 */
     SC_default=0,
     SC_None=1,
     SC_OK=2,
     SC_RotateOK=3
   END_STATIC_ENUM()
   sc_t Scale;

   /* Image dimensions for encoding filters (may be different from real
    * image dimensions, but _really_ disrecommended).
    * slen_t important becasuse of separate().
    */
   slen_t EncoderColumns, EncoderBPL, EncoderRows;
   /** Number of color components (channels) per pixel. */
   unsigned char EncoderColors;
   slen_t PredictorColumns;
   unsigned char PredictorBPC, PredictorColors;
   unsigned ColorTransform;
   signed Effort;
   slen_t RecordSize;
   slendiff_t K;
   /* JPEG quality for the IJG encoding filter */
   unsigned char Quality;
   unsigned TransferCPL;
   MiniPS::String *Comment, *Title, *Subject, *Author, *Creator, *Producer;
   MiniPS::String *Created, *Produced;
   MiniPS::Dict *DCT;
   /** MiniPS::Real* or integer */
   MiniPS::VALUE TopMargin, BottomMargin, LeftMargin, RightMargin;
   /** MiniPS::Real* or integer. Specifies the dimension the image should
    * be lowered below the baseline for (PostScript or PDF) output with
    * /Scale/None. Default to 0.
    */
   MiniPS::VALUE LowerMargin;
   /** 72 by default. */
   MiniPS::VALUE ImageDPI;
   static char const* dumpScale(sc_t Scale);
 };

 struct OutputRule {
   Cache cache;
   CacheHints cacheHints;
   MiniPS::Dict* dict;
   /* /Hints entry in dict */
   MiniPS::Dict* dictHints;
   /* Number of this output rule, as appeared in .job. Imp: name */
   unsigned c;
   inline bool isEOL() const { return dictHints==NULLP; }
   /** In: dict, dictHints, cache and cacheHints is uninitialized. Out: they
    * get inititalized from param. Errors might occur and error messages
    * might get printed.
    */
   void fromDict(MiniPS::VALUE);
   /** Calls info->setSampleFormat(), and sets cacheHints.PredictorColumns
    * and some other fields if they are zero (unspecified in the .job file)
    */
   void doSampleFormat(Image::SampledInfo *info, bool separate=false);
   /** Appends "" when CO_None,
    * "<</K ... /Columns ...>>/CCITTFaxDecode filter" when CO_Fax
    * "/...Decode filter" otherwise
    * Calls appendPredictorSpec() if necessary.
    * For PDF (not PS!), implies appendTransferSpec().
    */
   void appendDecoderSpec(GenBuffer::Writable &out) const;
   /** Appends "" when PR_None or
    * appends "<</BitsPerComponent.../Colors.../Columns.../Predictor 2>>" or
    * appends "<</BitsPerComponent.../Colors.../Columns.../Predictor 10>>"
    * appends "<</K ...>>"
    */
   void appendPredictorSpec(GenBuffer::Writable &out) const;
   /** Appends "" or "/ASCII85Decode filter" or "/ASCIIHexDecode filter"
    */
   void appendTransferSpec(GenBuffer::Writable &out) const;
   bool hasPredictorSpec() const;
 };

 struct Applier {
   BEGIN_STATIC_ENUM(unsigned,cons_t)
     DONT_KNOW=0, BAD=1, MAYBE=2, OK=3
   END_STATIC_ENUM()
   /** Checks the specified OutputRule for internal consistency, updates
    * the `or->cache', emits warnings, and returns consistency status
    * information.
    *
    * -- If (this) Applier doesn't know how to handle the OutputRule, returns
    *    DONT_KNOW immediately.
    * -- If (this) Applier knows that the OutputRule is internally
    *    inconsistent or proved to be unsupported (i.e printing JPEG images
    *    on PostScript Level1 printers), regardless of the image itself, it
    *    returns BAD immediately and emits a warning about the reason.
    * -- If (this) Applier knows that the OutputRule may or may not be
    *    processed, depending on the actual image, then MAYBE is returned.
    * -- If (this) Applier knows that the OutputRule can be processed on
    *    any image, then OK is returned.
    */
   typedef cons_t(*check_rule_t)(OutputRule* or_);

   /** Outputs (writes) the PostScript or PDF presentation of the image
    * specified in param `sf' to the output stream in param `out', using
    * the Output Rule specified in param `or'.
    * @return DONT_KNOW if doesn't know how to handle the OutputRule;
    *         BAD if it is sure that the image cannot be written with the
    *             specified OutputRule;
    *         OK if writing was successful
    */
   typedef cons_t(*work_t)(GenBuffer::Writable& out, OutputRule*or_, Image::SampledInfo *sf);

   /** A short, informal description of this Applier. */
   char const*format;
   check_rule_t check_rule;
   /** May be NULLP if `check_rule' never returns OK or MAYBE */
   work_t work;
   /** Null or next Applier to probe. Filled by register0(); */
   Applier *next;
 };

 /* Registers a new type of output Applier, i.e a new output file
  * format/method. The new output format will be put in front of all others,
  * and will be checked first by apply_best().
  */
 static void register0(Applier*);
 /* Loads the image contained in te file `filename'. */

 /** Builds the list of available profiles from the Profile element read
  * from a .job file. To do the task, calls check_rule() of all registered (!)
  * Appliers. Also calls OutputRule::fromDict for each profile element.
  * All returned OutputRules have an Applier which returned OK or MAYBE.
  * @return a dict==NULLP-terminated list of OutputRules
  */
 static OutputRule* buildProfile(MiniPS::VALUE Profile, bool quiet=false);

 /** Applies the Profile specified in param `rule_list' to the image
  * specified in param `sf', and writes the output to param `out'.
  */
 static void applyProfile(GenBuffer::Writable& out, OutputRule*rule_list, Image::SampledInfo *sf);

 /** Frees memory associated with a profile returned by buildProfile() */
 static void deleteProfile(OutputRule*);

 static unsigned printAppliers(GenBuffer::Writable &out);


 typedef void (*stream_writer_t)(GenBuffer::Writable&out, GenBuffer::Writable&outstream, Image::SampledInfo *sf);

 /** Writes a PS/PDF file to `out' according to `template_': substitutes
  * actual image (pixel) data from `sf->getImg()', and substitutes image
  * metadata from `sf'. `outstream' should be a prepared stream (top of chain
  * of Encode filters).
  *   See a .tte file for an example template.
  */
 static void writeTTE(
   GenBuffer::Writable& out,
   GenBuffer::Writable& outpal,
   GenBuffer::Writable& outstream,
   char const*template_,
   Rule::OutputRule*or_,
   Image::SampledInfo *sf,
   stream_writer_t stream_writer,
   char const*const*strings=(char**)NULLP);

 /** Doesn't invoke MiniPS::delete0(chunkArray); */
 static void writeTTM(
  Filter::VerbatimE &outve,
  GenBuffer::Writable&outpal,
  GenBuffer::Writable&outstream,
  MiniPS::Array *chunkArray,
  Rule::OutputRule*or_,
  Image::SampledInfo *sf,
  stream_writer_t stream_writer,
  char const*const*strings=(char**)NULLP);

 /** Used by writeTTT() to find template specification by name */
 static MiniPS::Dict* Templates;
 /** @param out must be of type Filter::VerbatimE if the template is a TTM
  * array.
  * @param template_key a key in file bts.ttt
  */
 static void writeTTT(
  GenBuffer::Writable&out,
  GenBuffer::Writable&outpal,
  GenBuffer::Writable&outstream,
  char const *template_key,
  Rule::OutputRule*or_,
  Image::SampledInfo *sf,
  stream_writer_t stream_writer,
  char const*const*strings=(char**)NULLP);

 static void writeData(GenBuffer::Writable& out, GenBuffer::Writable&outstream, Image::SampledInfo *sf);
 static void writePalData(GenBuffer::Writable& out, GenBuffer::Writable&outstream, Image::SampledInfo *sf);

};

#endif