/* This file is -*-C++-*-
------------------------------------------------------------------------------
denef - Decode NEF image files
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: metacam.h,v 1.4 2000/09/18 15:37:53 daniel Exp $

*/

#ifndef METACAM_H_INCLUDED
#define METACAM_H_INCLUDED

#include <map>
#include <vector>
#include <string>

class IFD;
class IFDEntry;
class istream;
class ostream;

typedef unsigned long tiffUNSIGNED;
typedef signed long tiffSIGNED;

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

class tiffRATIONAL
{
public:
    tiffUNSIGNED num;
    tiffUNSIGNED den;
    
    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));
    }

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

class tiffSRATIONAL
{
public:
    tiffSIGNED num;
    tiffSIGNED den;
    
    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));
    }
    
    operator double() const {
	double n(num);
	double d(den);
	return n/d;
    }

    void Normalize();
};

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

class tiffFile
{
private:
    friend class IFD;
    friend class IFDEntry;

    istream &is;
    unsigned long global_offset;
    bool bigendian;

protected:
    void Seek(unsigned long ofs);
    unsigned long Get_Offset() const {
	return is.tellg()-global_offset;
    }
    void Get_Data(unsigned char *buf, size_t bytes);
    unsigned char  Get_UByte();
    unsigned short Get_UShort();
    unsigned long  Get_ULong();
    signed char    Get_SByte();
    signed short   Get_SShort();
    signed long    Get_SLong();

    unsigned long First_IFD() {
	Seek(4);
	return Get_ULong();
    }
public:
    tiffFile(istream &i, unsigned long ofs=0);

    IFD Get_IFD();
    IFD Get_IFD(unsigned long ofs, unsigned long tagofs=0);
};

class IFDEntry;

class IFD
{
private:
    tiffFile &file;
    unsigned long ofs;
    unsigned short entries;
    unsigned long next_ifd;

    IFDEntry **table;

public:
    IFD(const IFD &);
    IFD &operator=(const IFD &);
    IFD(tiffFile &t, unsigned long o, unsigned long tagofs = 0);
    ~IFD();

    bool operator !() const {return ofs==0;}
    operator bool() const {return ofs!=0;}

    tiffFile &File() const {return file;}
    
    const IFDEntry &operator[](int n) const;
    unsigned short Entries() const {return entries;}
    IFD Next_IFD() const {
	return file.Get_IFD(next_ifd);
    }
};

class IFDEntry
{
    friend class IFD;
private:
    tiffFile &file;

    unsigned long tag;
    unsigned short type;
    unsigned long values;
    unsigned long offset;
public:
    IFDEntry(tiffFile &t) : file(t), tag(0), type(0), values(0), offset(0) {};
    
    static int Type_Length(unsigned short);

    int Length() const {return Type_Length(type) * values;}

    unsigned long  Tag() const {return tag;}
    unsigned short Type() const {return type;}
    unsigned long  Values() const {return values;}
    unsigned long  Offset() const {return offset;}

    void Output_Value(ostream &os) const;

    vector<tiffUNSIGNED> Get_UVALUES() const;
    vector<tiffSIGNED> Get_SVALUES() const;
    vector<tiffRATIONAL> Get_RATIONALS() const;
    vector<tiffSRATIONAL> Get_SRATIONALS() const;
    vector<string> Get_STRINGS() const;
    vector<unsigned char> Get_OPAQUE() const;
};

inline ostream &
operator<<(ostream &os, const IFDEntry &e)
{
    os << "[TAG " << hex << e.Tag() << dec << " TYPE " << e.Type() 
       << " VALUES " << e.Values() << " OFFSET " << e.Offset() << "]";
    return os;
}


class idpair
{
private:
    unsigned long tag;
    unsigned short type;
public:
    idpair() : tag(0), type(0) {};
    idpair(unsigned long a, unsigned short b) : tag(a), type(b) {};

    bool operator==(const idpair &i) const {
	return (tag==i.tag) && (type == i.type);
    }
    bool operator!=(const idpair &i) const {
	return (tag!=i.tag) || (type != i.type);
    }
    bool operator<=(const idpair &i) const {
	if (tag < i.tag) return true;
	if (tag > i.tag) return false;
	return (type <= i.type);
    }
    bool operator<(const idpair &i) const {
	if (tag < i.tag) return true;
	if (tag > i.tag) return false;
	return (type < i.type);
    }
    bool operator>(const idpair &i) const {
	if (tag > i.tag) return true;
	if (tag < i.tag) return false;
	return (type > i.type);
    }
    bool operator>=(const idpair &i) const {
	if (tag > i.tag) return true;
	if (tag < i.tag) return false;
	return (type >= i.type);
    }
    
    unsigned long Tag() const {return tag;}
    unsigned long Type() const {return type;}
};

typedef map<idpair, IFDEntry*> tagmap;

typedef void dpyFunction(ostream &os, const char *, const IFDEntry &e);

struct knowntag
{
    unsigned long tag;
    unsigned short type;
    int verbose;
    const char *name;
    dpyFunction *func;
};


inline ostream & operator<<(ostream &os, const idpair &p) {
    os << hex << p.Tag() << ":" << p.Type() << dec;
    return os;
}

#endif /* METACAM_H_INCLUDED */
