// ppmzip.cpp

#include "stdafx.h"

#define COMP   1
#define DECOMP 2

#define TRY_ROT(a) (rot?a^0xff:a)

/* PPMDi getc/putc bridge */
PPMDI::PPMDI(FILE *f) 
{
   file = f;
   count = 0;
}

void PPMDI::_putc(char c) 
{
   putc (c,file);
   count++;
}

int PPMDI::_getc() 
{
   count++;
#ifdef _DEBUG
   int a = count;
   a = getc(file);
   return a;
#else
   return getc(file);
#endif
}

int PPMDI::getCount()
{
   return count;
}

/* copy-paste from xmilldata.cpp etc */
typedef struct {
   int size;
   int order;
} level_settings;

#define XMILL_PPMDI_IDXS            19

level_settings ppmsettings[XMILL_PPMDI_IDXS] = { 
  {1,6},
  {1,7},
  {1,8},
  {1,9},
  {4,9},
  {1,10},
  {2,10},
  {5,10},
  {6,10},
  {8,10},
  {10,10},
  {20,10},
  {1,12},
  {2,12},
  {1,16},
  {2,16},
  {3,16},
  {4,16},
  {8,16}
};

/* main program, contains all PPMDi (de)compress API calls */
#ifdef WIN32
int _cdecl main(int argc,char **argv)
#else
int main(int argc,char **argv)
#endif
{
   int action = COMP;
   FILE* outfile = stdout;
   FILE* infile = stdin;
   int c, stat = 0, size = 10, order = 6, i = 0, c1;
   PPM_ENCODER* enc = NULL;
   PPM_DECODER* dec = NULL;
   PPMDIData *data = NULL;
   PPMDI *f = NULL;
   bool rot = false, skip4 = false;

   /* check arguments */
   if (argc > 1) {
      if (toupper(argv[1][0]) == 'D') {
         action = DECOMP;
      } else if (toupper(argv[1][0]) == 'C') {
         action = COMP;
      }
   }

   if (argc > 2) {
      infile = fopen(argv[2], "r");
   }

   if (argc > 3) {
      outfile = fopen(argv[3], "wb");
   }

   if (argc > 5) {
      size = atoi(argv[4]);
      order = atoi(argv[5]);
   }

   if (argc > 6) {
      skip4 = (toupper(argv[6][0]) == 'T');
      rot = argv[6][1];
   }

#ifdef WIN32
   _setmode(_fileno(infile), _O_BINARY);
   _setmode(_fileno(outfile), _O_BINARY);
#else
   /* how do I implement this on Unix?? */
#endif

   switch (action) {
      case COMP:
         /* compress the file */
         f = new PPMDI(outfile);
         data = new PPMDIData(f);
         data->getAlloc(size);
         enc = new PPM_ENCODER (size,order,data);
         data->ariInitEncoder();
         while ((c = getc(infile)) != EOF) {
            enc->EncodeChar(TRY_ROT(c));
            i++;
         }
         enc->EncodeChar(EOF);
         data->ariFlushEncoder();
         break;

      case DECOMP:
         /* decompress the file */
         if (skip4) {
            /* skip first four Bytes & read size/order from the last one */
            for (int j=0; j<4; j++) {
               c1 = getc(infile);
            }
            size = ppmsettings[c1].size;
            order = ppmsettings[c1].order;
         }
         f = new PPMDI(infile);
         data = new PPMDIData(f);
         data->getAlloc(size);
         dec = new PPM_DECODER (size,order,data);
         data->ariInitDecoder();
         while (TRUE) {
            if ((c1 = dec->DecodeChar()) == EOF) {
               goto cleanup;
            }
            i++;
            putc(TRY_ROT(c1), outfile);
         }

         break;

      default:
         stat = 1;
         goto cleanup;
   }

cleanup:
   if (stat == 0 && outfile != stdout && f) {
      printf ("(de)compressed %ld Bytes to %ld Bytes\n", f->getCount(), i);
   }

   if (f) delete f;
   if (data) delete data;
   if (enc) delete enc;
   if (dec) delete dec;
   if (infile) fclose(infile);
   if (outfile) fclose(outfile);

   return stat;
}
