/* This file is -*-C++-*-
------------------------------------------------------------------------------
MetaCam - Extract EXIF information from digital camera files, with
support for Vendor specific blocks.
Copyright (C) 2000 Daniel Stephens (daniel@cheeseplant.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.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
------------------------------------------------------------------------------

$Id: metatiff.h,v 1.3 2002/09/02 19:20:01 daniel Exp $

*/

#ifndef METATIFF_H_INCLUDED
#define METATIFF_H_INCLUDED

#include <vector>
#include <string>
#include <iostream>

using namespace std;
/* Classes for access to meta-infomation from tiff files */

class _TIFFDataSource;
class _IFD;
class _IFDEntry;

class TIFFDataSource;
class IFD;
class IFDEntry;

typedef unsigned long tiffUNSIGNED;
typedef signed long tiffSIGNED;

const int tBYTE      = 1;
const int tASCII     = 2;
const int tSHORT     = 3;
const int tLONG      = 4;
const int tRATIONAL  = 5;
const int tSBYTE     = 6;
const int tUNDEFINED = 7;
const int tSSHORT    = 8;
const int tSLONG     = 9;
const int tSRATIONAL = 10;
const int tFLOAT     = 11;
const int tDOUBLE    = 12;

// Now have special types which are parsed versions of the above ones
const int tReferencedIFD  = -1;
const int tDataIFD  = -2;

class tiffRATIONAL
{
private:
    tiffUNSIGNED num;
    tiffUNSIGNED den;
    
public:
    tiffRATIONAL() : num(0), den(0) {};
    tiffRATIONAL(tiffUNSIGNED n, tiffUNSIGNED d) : num(n), den(d) {};

    bool operator == (const tiffRATIONAL &r) const {
	return ((num == r.num) && (den == r.den));
    }
    bool operator != (const tiffRATIONAL &r) const {
	return ((num != r.num) || (den != r.den));
    }

    tiffUNSIGNED getNumerator() const { return num; }
    tiffUNSIGNED getDenominator() const { return den; }

    operator double() const {
	double n(num);
	double d(den);
	return n/d;
    }
    
    tiffRATIONAL normalize() const;
};

class tiffSRATIONAL
{
private:
    tiffSIGNED num;
    tiffSIGNED den;
    
public:
    tiffSRATIONAL() : num(0), den(0) {};
    tiffSRATIONAL(tiffSIGNED n, tiffSIGNED d) : num(n), den(d) {};
    
    bool operator == (const tiffSRATIONAL &r) const {
	return ((num == r.num) && (den == r.den));
    }
    bool operator != (const tiffSRATIONAL &r) const {
	return ((num != r.num) || (den != r.den));
    }
    
    tiffSIGNED getNumerator() const { return num; }
    tiffSIGNED getDenominator() const { return den; }

    operator double() const {
	double n(num);
	double d(den);
	return n/d;
    }

    tiffSRATIONAL normalize() const;
};

inline ostream &operator << (ostream &os, const tiffRATIONAL &r)
{
    os << r.getNumerator() << "/" << r.getDenominator();
    return os;
}
    
inline ostream &operator << (ostream &os, const tiffSRATIONAL &r)
{
    os << r.getNumerator() << "/" << r.getDenominator();
    return os;
}

class _ReferenceCounted
{
private:
    bool do_not_delete; // True if this cannot be re-created automatically
    int ref_count;
public:
    _ReferenceCounted(bool dnd = false) : do_not_delete(dnd), ref_count(0) {};
    virtual ~_ReferenceCounted();

    // Reference counters
    void addRef() { 
	++ref_count; 
    }
    void delRef() { 
	--ref_count; 
	if (ref_count == 0) {
	    if (!do_not_delete) delete this;
	}
    }
    int getRefCount() const { return ref_count; }
};

    

const int taFIRST_AVAILABLE = 10000;

class _BaseAnnotation : public _ReferenceCounted
{
private:
    const int anno_type;

    static int next_avail_type;
public:
    _BaseAnnotation(int t, bool dnd=false) : _ReferenceCounted(dnd), 
	anno_type(t) {};

    int getType() { return anno_type; }
    virtual void display(ostream &os);

    static int getAnnotationType(const char *use) {
	return ++next_avail_type;
    }
};

// Front-end wrapper class for Annotation's
class Annotation
{
private:
    _BaseAnnotation *anno;
    
public:
    Annotation() : anno(0) {};
    Annotation(_BaseAnnotation *i) : anno(i) { if (anno) anno->addRef(); }
    Annotation(const Annotation &oi) : anno(oi.anno) {if (anno) anno->addRef(); }

    _BaseAnnotation *getReal() const { return anno; }

    ~Annotation() {
	if (anno) anno->delRef();
	anno = 0;
    }

    Annotation & operator = (const Annotation &oi) {
	if (oi.anno == anno) return *this;
	if (oi.anno) oi.anno->addRef();
	if (anno) anno->delRef();
	anno = oi.anno;
	return *this;
    }

    bool operator!() const { return anno == 0; }
    operator bool() const { return anno != 0; }
    bool operator==(const Annotation &i) const { return i.anno == anno; }
    bool operator!=(const Annotation &i) const { return i.anno != anno; }

    int getType() const { return anno ? anno->getType() : 0; }

    void display(ostream &os) const {
	if (anno) anno->display(os);
    }

    static int getAnnotationType(const char *use) {
	return _BaseAnnotation::getAnnotationType(use);
    }
};

inline ostream &operator << (ostream &os, const Annotation &r)
{
    r.display(os);
    return os;
}


/** Represents a view into a source of data from a TIFF file */
class _TIFFDataSource : public _ReferenceCounted
{
public:
    _TIFFDataSource(bool dnd=false) : _ReferenceCounted(dnd) {}
    virtual ~_TIFFDataSource();

    virtual bool bigEndian() const = 0;
    virtual void seek(unsigned long ofs) = 0;
    virtual unsigned long tell() const = 0;
    virtual size_t getData(unsigned char *buf, size_t bytes) = 0;
    virtual unsigned char  getUByte();
    virtual unsigned short getUShort();
    virtual unsigned long  getULong();
    virtual signed char    getSByte();
    virtual signed short   getSShort();
    virtual signed long    getSLong();

    virtual bool isGood() const = 0;

    virtual IFD getIFD();
    virtual IFD getIFD(unsigned long ofs);
};

// Front-end wrapper class for data sources
class TIFFDataSource
{
private:
    _TIFFDataSource *src;
    
public:
    TIFFDataSource() : src(0) {};
    TIFFDataSource(_TIFFDataSource *i) : src(i) { if (src) src->addRef(); }
    TIFFDataSource(const TIFFDataSource &oi) : src(oi.src) {if (src) src->addRef(); }

    ~TIFFDataSource() {
	if (src) src->delRef();
	src = 0;
    }

    TIFFDataSource & operator = (const TIFFDataSource &oi) {
	if (oi.src == src) return *this;
	if (oi.src) oi.src->addRef();
	if (src) src->delRef();
	src = oi.src;
	return *this;
    }

    bool operator!() const { return src == 0; }
    operator bool() const { return src != 0; }
    bool operator==(const TIFFDataSource &i) const { return i.src == src; }
    bool operator!=(const TIFFDataSource &i) const { return i.src != src; }

    bool bigEndian() const       { return src->bigEndian(); };
    void seek(unsigned long ofs) const { src->seek(ofs); };
    unsigned long tell() const   { return src->tell(); };
    size_t getData(unsigned char *buf, size_t bytes) const { 
	return src->getData(buf, bytes); 
    };
    unsigned char  getUByte()  const { return src->getUByte(); }
    unsigned short getUShort() const { return src->getUShort(); }
    unsigned long  getULong()  const { return src->getULong(); }
    signed char    getSByte()  const { return src->getSByte(); }
    signed short   getSShort() const { return src->getSShort(); }
    signed long    getSLong()  const { return src->getSLong(); }

    bool isGood() const { return src ? src->isGood() : false; }

    IFD getIFD() const;
    IFD getIFD(unsigned long ofs) const;
};

class _IFD : public _ReferenceCounted
{
public:
    _IFD(bool dnd=false) : _ReferenceCounted(dnd) {}
    virtual ~_IFD();

    virtual bool isDynamic() const = 0; // Return true if this can be changed

    virtual IFDEntry operator[](int n) const = 0;
    virtual IFDEntry findEntry(unsigned long tag, _IFDEntry *after=0) const = 0;

    virtual unsigned short entries() const = 0;

    virtual IFD nextIFD() const = 0;
};

class _IFDEntry: public _ReferenceCounted
{
    friend class _IFD;
private:
    unsigned long tag;
    unsigned short raw_type;
    int actual_type;
public:
    _IFDEntry(unsigned long t,
	      unsigned short raw,
	      bool dnd=false) : _ReferenceCounted(dnd),
	tag(t), raw_type(raw), actual_type(raw) {}

    _IFDEntry(unsigned long t,
	      unsigned short raw,
	      int act, bool dnd=false) : _ReferenceCounted(dnd), 
	tag(t), raw_type(raw), actual_type(act) {}

    virtual ~_IFDEntry();

    static int typeLength(unsigned short);

    int length() const {return typeLength(raw_type) * values();}

    unsigned long  getTag() const {return tag;}
    unsigned short getRawType() const {return raw_type;}
    int getType() const {return actual_type;}
    virtual unsigned long values() const = 0;

    virtual void outputValue(ostream &os) const = 0;

    virtual vector<tiffUNSIGNED>   getUVALUE() const = 0;
    virtual vector<tiffSIGNED>     getSVALUE() const = 0;
    virtual vector<tiffRATIONAL>   getRATIONAL() const = 0;
    virtual vector<tiffSRATIONAL>  getSRATIONAL() const = 0;
    virtual vector<string>         getSTRING() const = 0;
    virtual vector<unsigned char>  getOPAQUE() const = 0;

    virtual TIFFDataSource getSource() const = 0;
    virtual unsigned long getSourceOffset() const = 0;
};


// Front-end wrapper class for IFDEntries
class IFDEntry
{
    friend class IFD;
private:
    _IFDEntry *entry;
    
public:
    IFDEntry() : entry(0) {};
    IFDEntry(_IFDEntry *i) : entry(i) { if (entry) entry->addRef(); }
    IFDEntry(const IFDEntry &oi) : entry(oi.entry) {if (entry) entry->addRef(); }

    ~IFDEntry() {
	if (entry) entry->delRef();
	entry = 0;
    }

    IFDEntry & operator = (const IFDEntry &oi) {
	if (oi.entry == entry) return *this;
	if (oi.entry) oi.entry->addRef();
	if (entry) entry->delRef();
	entry = oi.entry;
	return *this;
    }

    _IFDEntry *getRealEntry() const { return entry; }

    bool operator!() const { return entry == 0; }
    operator bool() const { return entry != 0; }
    bool operator==(const IFDEntry &i) const { return i.entry == entry; }
    bool operator!=(const IFDEntry &i) const { return i.entry != entry; }

    int length() const { return entry ? entry->length() : 0; }
    unsigned long  getTag() const { return entry ? entry->getTag() : 0; }
    unsigned short getRawType() const { return entry ? entry->getRawType() : 0 ;}
    int getType() const { return entry ? entry->getType() : 0 ; }
    unsigned long values() const { return entry ? entry->values() : 0;}

    void outputValue(ostream &os) const { entry->outputValue(os);}

    vector<tiffUNSIGNED>   getUVALUE() const {
	return entry->getUVALUE();
    }
    vector<tiffSIGNED>     getSVALUE() const {
	return entry->getSVALUE();
    }
    vector<tiffRATIONAL>   getRATIONAL() const {
	return entry->getRATIONAL();
    }
    vector<tiffSRATIONAL>  getSRATIONAL() const {
	return entry->getSRATIONAL();
    }
    vector<string>         getSTRING() const {
	return entry->getSTRING();
    }
    vector<unsigned char>  getOPAQUE() const {
	return entry->getOPAQUE();
    }

    TIFFDataSource getSource() const { return entry->getSource(); }
    unsigned long getSourceOffset() const { return entry->getSourceOffset(); }
};


// Front-end wrapper class for IFD's
class IFD
{
private:
    _IFD *ifd;
    
public:
    IFD() : ifd(0) {};
    IFD(_IFD *i) : ifd(i) { if (ifd) ifd->addRef(); }
    IFD(const IFD &oi) : ifd(oi.ifd) {if (ifd) ifd->addRef(); }

    _IFD *getRealIFD() const { return ifd; }

    ~IFD() {
	if (ifd) ifd->delRef();
	ifd = 0;
    }

    IFD & operator = (const IFD &oi) {
	if (oi.ifd == ifd) return *this;
	if (oi.ifd) oi.ifd->addRef();
	if (ifd) ifd->delRef();
	ifd = oi.ifd;
	return *this;
    }

    bool operator!() const { return ifd == 0; }
    operator bool() const { return ifd != 0; }
    bool operator==(const IFD &i) const { return i.ifd == ifd; }
    bool operator!=(const IFD &i) const { return i.ifd != ifd; }

    unsigned short entries() const { return ifd ? ifd->entries() : 0 ; }
    IFD nextIFD() const { return ifd ? IFD(ifd->nextIFD()) : IFD(); }
    bool isDynamic() { return ifd ? ifd->isDynamic() : false; }
    
    IFDEntry operator[](int n) const { return IFDEntry((*ifd)[n]); }
    IFDEntry findEntry(unsigned long tag) const {
	return IFDEntry(ifd->findEntry(tag));
    }
    IFDEntry findEntry(unsigned long tag, IFDEntry &after) const {
	return IFDEntry(ifd->findEntry(tag, after.entry));
    }

    IFD getEditable();
};


#endif /* METATIFF_H_INCLUDED */
