

class Image {
 public:
  enum Error;
  typedef void (*ErrorHandler)(Error err, char *func);
  
  enum Tag { Bad = 0, Gray, Grey = Gray, Binary, Bit, Double, Float,
             Long, Short, RGB, RGBF, Color8, HSV, Other };
  
  enum Error { NoError = 0, Bounds, Allocation, FileSave, FileLoad, LoadTag,
               NoData, NoHeader, BadHeader };

 private:
  static void DefaultErrorHandler(Error err, char *func);
  static void handleError(Error err, char *func);
  
 public:
  static ErrorHandler setEH(ErrorHandler eh);
  static ErrorHandler getEH();

  static const char *errorName(Error err);

  friend class ImageHeader;
  friend class ImageOf<T>;
  friend class BitImage;
}



class ImageHeader {
  int       w, h, xb, yb;
  Image::Tag  type;
  int       esize;
  String    text;
  uint      rcount;
  bool      wrap;
  void     *idata;
  RGB      *rgbcolors;             // for PPM8 images (uchar + color table)
                                   // this should be in class Color8Image ...
    
  Image::ErrorHandler ehandler;

  void handleError(Image::Error err, char *func);

 public:
  Image::ErrorHandler setEH(Image::ErrorHandler eh);
  Image::ErrorHandler getEH();
  
 private:
  void checktag(char *func) { if (type == Bad) handleError(BadHeader, func); }
  
 public:
  ImageHeader();
  ImageHeader(FILE *fp);
 ~ImageHeader() { delete [] rgbcolors; }

  bool load(FILE *fp);
  FILE *load(char *fn);
  
  bool goodtag()  { return type != Bad; }
  bool havedata() { return idata != 0; }

  int width()        { checktag("ImageHeader::width()");   return w; }
  int height()       { checktag("ImageHeader::height()");  return h; }
  int xbase()        { checktag("ImageHeader::xbase()");   return xb; }
  int ybase()        { checktag("ImageHeader::ybase()");   return yb; }
  int xmax()         { checktag("ImageHeader::xmax()");    return xb + w - 1; }
  int ymax()         { checktag("ImageHeader::ymax()");    return yb + h - 1; }
  ImageTag tag()     { checktag("ImageHeader::tag()");     return type; }
  bool wrapped()     { checktag("ImageHeader::wrapped()"); return wrap; }
  String &comment()  { checktag("ImageHeader::comment()"); return text; }
  const RGB *colors(){ checktag("ImageHeader::colors()");  return rgbcolors; }
  int elsize()       { checktag("ImageHeader::elsize()");  return esize; }

  friend class ImageOf<T>;
  friend class BitImage;
}



template<class T> class ImageOf {
  T           **data;
  ImageHeader  *header;

  const static ImageTag  tag;
  const static char     *Magic;
  const static bool      HasMxVal;
  const static int       ElemSize;

  static Image::ErrorHandler ehandler;
  void handleError(Error err, char *func);

 public:
  ErrorHandler setEH(ErrorHandler eh) { ErrorHandler r = ehandler; ehandler = eh; return r; }
  ErrorHandler getEH() { return ehandler; }

 private:
  Image::Error reasonNotOK();
  
 public:
  ImageOf();
  ImageOf(int w, int h, int xb=0, int yb=0);
  ImageOf(T *raw, int w, int h, int xb=0, int yb=0);
  ImageOf(FILE *fp, ImageHeader *ih=0);
  ImageOf(const char *fn);
  ImageOf(const ImageOf<T> &im);
  ImageOf(const FastImageOf<T> &im);

  ~ImageOf();
  free();

  bool alloc(int w, int h, int xb=0, int yb=0);
  
  ImageOf copy();                      // make a new copy
  ImageOf copy(ImageOf<T> from);       // copy into me
  ImageOf dup() { return this->copy(); };

  ImageOf downsample(int dx, int dy=0);
  ImageOf downsample(float dx, float dy=0.0);

  ImageOf upsample(int dx, int dy=0);
  ImageOf upsample(float dx, float dy=0.0);

  friend void imConv(ImageOf<T1>, ImageOf<T2>);  // convert types

  ImageOf crop(int x0, int y0, int w, int h);
  bool break();              // force copy if ref-count > 1

  void print(ostream os=cout);
  
  bool save(FILE *fp);
  bool save(const char *fn);
  
  bool load(FILE *fp, ImageHeader *ih=0);
  bool load(const char *fn);

  void init(T *raw, int w, int h, int xb=0, int yb=0);
  void init(T &val);
  void init(T (*fn)());
  void init(T (*fn)(int i, int j));

  void apply(T (*fn)(T val));
  void apply(T (*fn)(T val, int i, int j));
  void apply(void (*fn)(T &val));
  void apply(void (*fn)(T &val, int i, int j));

  void setOffset(int xb, int yb);

  bool resize();

  T &ref(int i, int j) { return data[j][i]; }  // also check ok() and valid(i, j)
  T &operator()(int i, int j) { return ref(i, j); }
  T  operator()(double i, double j);

  T *rawPtr() { return &ref(xbase(), ybase()); }
  int size()  { return width() * height(); }
  
  void operator=(ImageOf<T> &ival);  // shallow, incr. ref count
  
  bool ok() { return (data && header && header->goodtag()); }
  
  bool validx(int x);
  bool validy(int y);
  bool valid(int x, int y);
  
  int width();
  int height();
  int xbase();
  int ybase();
  int xmin();     // same as xbase()
  int ymin();
  int xmax();
  int ymax();
  ImageTag tag();
  bool wrapped();
  String &comment();
  const RGB *colors();

  operator ImageHeader() { return header; };
  operator int()  { return ok(); }
  operator bool() { return ok(); }
};

template<class T> class FastImageOf {   // no bounds check...conversions back 
};                                      // and forth btwn checked and unchecked.


template <class T> ImageOf<T>::Magic    = "UserImage";
template <class T> ImageOf<T>::Tag      = Image::Other;
template <class T> ImageOf<T>::HasMxVal = false;
template <class T> ImageOf<T>::ElemSize = sizeof(T);
template <class T> ImageOf<T>::ehandler = 0;
  

typedef ImageOf<bool>     BinaryImage;
typedef ImageOf<uchar>    GrayImage, GreyImage;
typedef ImageOf<short>    ShortImage;
typedef ImageOf<long>     LongImage;
typedef ImageOf<float>    FloatImage;
typedef ImageOf<double>   DoubleImage;
typedef ImageOf<RGB>      RGBImage;
typedef ImageOf<RGBFloat> RGBFImage;
typedef ImageOf<HSV>      HSVImage;
typedef ImageOf<void *>   PtrImage;

// setup IO params for those types... g++ doesn't handle...

ImageOf<uchar>::Magic    = "P5";         // PGM tag
ImageOf<uchar>::Tag      = Image::Gray;
ImageOf<uchar>::HasMxVal = true;


// specialize BinaryImage reading/writing ... have to pack PBM!
  


// BitImage is like a BinaryImage but packed into 32 bit ulongs

class Bit {
  ulong &word;
  int bitno;

 public:

};

class BitImage {


};


class Color8Image : GrayImage {   // gotta think abt this more...

  RGB ColorTable[256];
};
