//------------------------------------------------------------------------
//  Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>
//
//  This file is part of the ZBar Bar Code Reader.
//
//  The ZBar Bar Code Reader is free software; you can redistribute it
//  and/or modify it under the terms of the GNU Lesser Public License as
//  published by the Free Software Foundation; either version 2.1 of
//  the License, or (at your option) any later version.
//
//  The ZBar Bar Code Reader 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 Lesser Public License for more details.
//
//  You should have received a copy of the GNU Lesser Public License
//  along with the ZBar Bar Code Reader; if not, write to the Free
//  Software Foundation, Inc., 51 Franklin St, Fifth Floor,
//  Boston, MA  02110-1301  USA
//
//  http://sourceforge.net/projects/zbar
//------------------------------------------------------------------------
#ifndef _ZBAR_SYMBOL_H_
#define _ZBAR_SYMBOL_H_

/// @file
/// Symbol C++ wrapper

#ifndef _ZBAR_H_
# error "include zbar.h in your application, **not** zbar/Symbol.h"
#endif

#include <stdlib.h>
#include <string>
#include <ostream>
#include <assert.h>

namespace zbar {

class SymbolIterator;

/// container for decoded result symbols associated with an image
/// or a composite symbol.

class SymbolSet {
public:
    /// constructor.
    SymbolSet (const zbar_symbol_set_t *syms = NULL)
        : _syms(syms)
    {
        ref();
    }

    /// copy constructor.
    SymbolSet (const SymbolSet& syms)
        : _syms(syms._syms)
    {
        ref();
    }

    /// destructor.
    ~SymbolSet ()
    {
        ref(-1);
    }

    /// assignment.
    SymbolSet& operator= (const SymbolSet& syms)
    {
        syms.ref();
        ref(-1);
        _syms = syms._syms;
        return(*this);
    }

    /// truth testing.
    bool operator! () const
    {
        return(!_syms || !get_size());
    }

    /// manipulate reference count.
    void ref (int delta = 1) const
    {
        if(_syms)
            zbar_symbol_set_ref((zbar_symbol_set_t*)_syms, delta);
    }

    /// cast to C symbol set.
    operator const zbar_symbol_set_t* () const
    {
        return(_syms);
    }

    int get_size () const
    {
        return((_syms) ? zbar_symbol_set_get_size(_syms) : 0);
    }

    /// create a new SymbolIterator over decoded results.
    SymbolIterator symbol_begin() const;

    /// return a SymbolIterator suitable for ending iteration.
    const SymbolIterator symbol_end() const;

private:
    const zbar_symbol_set_t *_syms;
};

/// decoded barcode symbol result object.  stores type, data, and
/// image location of decoded symbol

class Symbol {
public:

    /// image pixel location (x, y) coordinate tuple.
    class Point {
    public:
        int x;  ///< x-coordinate.
        int y;  ///< y-coordinate.

        Point () { }

        Point(int x, int y)
            : x(x), y(y)
        { }

        /// copy constructor.
        Point (const Point& pt)
            : x(pt.x),
              y(pt.y)
        { }

        /// assignment.
        Point& operator= (const Point& pt)
        {
            x = pt.x;
            y = pt.y;
            return(*this);
        }
    };

    /// iteration over Point objects in a symbol location polygon.
    class PointIterator
        : public std::iterator<std::input_iterator_tag, Point> {

    public:
        /// constructor.
        PointIterator (const Symbol *sym = NULL,
                       int index = 0)
            : _sym(sym),
              _index(index)
        {
            sym->ref(1);
            if(!sym ||
               (unsigned)_index >= zbar_symbol_get_loc_size(*_sym))
                _index = -1;
        }

        /// copy constructor.
        PointIterator (const PointIterator& iter)
            : _sym(iter._sym),
              _index(iter._index)
        {
            _sym->ref();
        }

        /// destructor.
        ~PointIterator ()
        {
            _sym->ref(-1);
        }

        /// assignment.
        PointIterator& operator= (const PointIterator& iter)
        {
            iter._sym->ref();
            _sym->ref(-1);
            _sym = iter._sym;
            _index = iter._index;
            return(*this);
        }

        /// truth testing.
        bool operator! () const
        {
            return(!_sym || _index < 0);
        }

        /// advance iterator to next Point.
        PointIterator& operator++ ()
        {
            unsigned int i = ++_index;
            if(i >= zbar_symbol_get_loc_size(*_sym))
                _index = -1;
            return(*this);
        }

        /// retrieve currently referenced Point.
        const Point operator* () const
        {
            assert(!!*this);
            if(!*this)
                return(Point());
            return(Point(zbar_symbol_get_loc_x(*_sym, _index),
                         zbar_symbol_get_loc_y(*_sym, _index)));
        }

        /// test if two iterators refer to the same Point in the same
        /// Symbol.
        bool operator== (const PointIterator& iter) const
        {
            return(_index == iter._index &&
                   ((_index < 0) || _sym == iter._sym));
        }

        /// test if two iterators refer to the same Point in the same
        /// Symbol.
        bool operator!= (const PointIterator& iter) const
        {
            return(!(*this == iter));
        }

    private:
        const Symbol *_sym;
        int _index;
    };

    /// constructor.
    Symbol (const zbar_symbol_t *sym = NULL)
        : _xmlbuf(NULL),
          _xmllen(0)
    {
        init(sym);
        ref();
    }

    /// copy constructor.
    Symbol (const Symbol& sym)
        : _sym(sym._sym),
          _type(sym._type),
          _data(sym._data),
          _xmlbuf(NULL),
          _xmllen(0)
    {
        ref();
    }

    /// destructor.
    ~Symbol () {
        if(_xmlbuf)
            free(_xmlbuf);
        ref(-1);
    }

    /// assignment.
    Symbol& operator= (const Symbol& sym)
    {
        sym.ref(1);
        ref(-1);
        _sym = sym._sym;
        _type = sym._type;
        _data = sym._data;
        return(*this);
    }

    Symbol& operator= (const zbar_symbol_t *sym)
    {
        if(sym)
            zbar_symbol_ref(sym, 1);
        ref(-1);
        init(sym);
        return(*this);
    }

    /// truth testing.
    bool operator! () const
    {
        return(!_sym);
    }

    void ref (int delta = 1) const
    {
        if(_sym)
            zbar_symbol_ref((zbar_symbol_t*)_sym, delta);
    }

    /// cast to C symbol.
    operator const zbar_symbol_t* () const
    {
        return(_sym);
    }

    /// test if two Symbol objects refer to the same C symbol.
    bool operator== (const Symbol& sym) const
    {
        return(_sym == sym._sym);
    }

    /// test if two Symbol objects refer to the same C symbol.
    bool operator!= (const Symbol& sym) const
    {
        return(!(*this == sym));
    }

    /// retrieve type of decoded symbol.
    zbar_symbol_type_t get_type () const
    {
        return(_type);
    }

    /// retrieve the string name of the symbol type.
    const std::string get_type_name () const
    {
        return(zbar_get_symbol_name(_type));
    }

    /// retrieve the string name for any addon.
    const std::string get_addon_name () const
    {
        return(zbar_get_addon_name(_type));
    }

    /// retrieve data decoded from symbol.
    const std::string get_data () const
    {
        return(_data);
    }

    /// retrieve length of binary data
    unsigned get_data_length () const
    {
        return((_sym) ? zbar_symbol_get_data_length(_sym) : 0);
    }

    /// retrieve inter-frame coherency count.
    /// see zbar_symbol_get_count()
    /// @since 1.5
    int get_count () const
    {
        return((_sym) ? zbar_symbol_get_count(_sym) : -1);
    }

    SymbolSet get_components () const
    {
        return(SymbolSet((_sym) ? zbar_symbol_get_components(_sym) : NULL));
    }

    /// create a new PointIterator at the start of the location
    /// polygon.
    PointIterator point_begin() const
    {
        return(PointIterator(this));
    }

    /// return a PointIterator suitable for ending iteration.
    const PointIterator point_end() const
    {
        return(PointIterator());
    }

    /// see zbar_symbol_get_loc_size().
    int get_location_size () const
    {
        return((_sym) ? zbar_symbol_get_loc_size(_sym) : 0);
    }

    /// see zbar_symbol_get_loc_x().
    int get_location_x (unsigned index) const
    {
        return((_sym) ? zbar_symbol_get_loc_x(_sym, index) : -1);
    }

    /// see zbar_symbol_get_loc_y().
    int get_location_y (unsigned index) const
    {
        return((_sym) ? zbar_symbol_get_loc_y(_sym, index) : -1);
    }

    /// see zbar_symbol_get_orientation().
    /// @since 0.11
    int get_orientation () const
    {
        return(zbar_symbol_get_orientation(_sym));
    }

    /// see zbar_symbol_xml().
    const std::string xml () const
    {
        if(!_sym)
            return("");
        return(zbar_symbol_xml(_sym, (char**)&_xmlbuf, (unsigned*)&_xmllen));
    }

protected:
    /// (re)initialize Symbol from C symbol object.
    void init (const zbar_symbol_t *sym = NULL)
    {
        _sym = sym;
        if(sym) {
            _type = zbar_symbol_get_type(sym);
            _data = std::string(zbar_symbol_get_data(sym),
                                zbar_symbol_get_data_length(sym));
        }
        else {
            _type = ZBAR_NONE;
            _data = "";
        }
    }

private:
    const zbar_symbol_t *_sym;
    zbar_symbol_type_t _type;
    std::string _data;
    char *_xmlbuf;
    unsigned _xmllen;
};

/// iteration over Symbol result objects in a scanned Image or SymbolSet.
class SymbolIterator
    : public std::iterator<std::input_iterator_tag, Symbol> {

public:
    /// default constructor.
    SymbolIterator ()
    { }

    /// constructor.
    SymbolIterator (const SymbolSet &syms)
        : _syms(syms)
    {
        const zbar_symbol_set_t *zsyms = _syms;
        if(zsyms)
            _sym = zbar_symbol_set_first_symbol(zsyms);
    }

    /// copy constructor.
    SymbolIterator (const SymbolIterator& iter)
        : _syms(iter._syms)
    {
        const zbar_symbol_set_t *zsyms = _syms;
        if(zsyms)
            _sym = zbar_symbol_set_first_symbol(zsyms);
    }

    ~SymbolIterator ()
    {
    }

    /// assignment.
    SymbolIterator& operator= (const SymbolIterator& iter)
    {
        _syms = iter._syms;
        _sym = iter._sym;
        return(*this);
    }

    bool operator! () const
    {
        return(!_syms || !_sym);
    }

    /// advance iterator to next Symbol.
    SymbolIterator& operator++ ()
    {
        if(!!_sym)
            _sym = zbar_symbol_next(_sym);
        else if(!!_syms)
            _sym = zbar_symbol_set_first_symbol(_syms);
        return(*this);
    }

    /// retrieve currently referenced Symbol.
    const Symbol operator* () const
    {
        return(_sym);
    }

    /// access currently referenced Symbol.
    const Symbol* operator-> () const
    {
        return(&_sym);
    }

    /// test if two iterators refer to the same Symbol
    bool operator== (const SymbolIterator& iter) const
    {
        // it is enough to test the symbols, as they belong
        // to only one set (also simplifies invalid case)
        return(_sym == iter._sym);
    }

    /// test if two iterators refer to the same Symbol
    bool operator!= (const SymbolIterator& iter) const
    {
        return(!(*this == iter));
    }

    const SymbolIterator end () const {
        return(SymbolIterator());
    }

private:
    SymbolSet _syms;
    Symbol _sym;
};

inline SymbolIterator SymbolSet::symbol_begin () const {
    return(SymbolIterator(*this));
}

inline const SymbolIterator SymbolSet::symbol_end () const {
    return(SymbolIterator());
}

/// @relates Symbol
/// stream the string representation of a Symbol.
static inline std::ostream& operator<< (std::ostream& out,
                                        const Symbol& sym)
{
    out << sym.get_type_name()
        << sym.get_addon_name()
        << ":" << sym.get_data();
    return(out);
}

}

#endif
