#if !defined(IMAGE_H)
#define IMAGE_H

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

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h> 
#include <bfd.h>
}

extern bool g_quitOnError;

class Image;

class Location
{
    friend              class Image;

    bfd_vma             m_address;
    bool                m_isKernel;
    bool                m_isMapped;
    unsigned            m_count;
    pid_t               m_pid;
    const char*         m_modulename;
    const char*         m_filename;
    const char*         m_functionname;
    unsigned            m_linenumber;

public:
    Location( bool isKernel, unsigned pid, uintptr_t address, unsigned count ) :
    m_address( address ),
    m_isKernel( isKernel ),
    m_isMapped( false ),
    m_count( count ),
    m_pid( pid ),
    m_modulename( 0 ),
    m_filename( 0 ),
    m_functionname( 0 ),
    m_linenumber( 0 )
    {
    }

    bfd_vma getAddress() const
    {
        return m_address;
    }

    bool isKernel() const
    {
        return m_isKernel;
    }

    bool isMapped() const
    {
        return m_isMapped;
    }

    void isMapped( bool mapped )
    {
        m_isMapped = mapped;
    }

    unsigned getCount() const
    {
        return m_count;
    }

    pid_t getPid() const
    {
        return m_pid;
    }

    const char* getModuleName() const
    {
        return m_modulename == 0 ? "" : m_modulename;
    }

    const char* getFileName() const
    {
        return m_filename == 0 ? "" : m_filename;
    }

    const char* getFunctionName() const
    {
        return m_functionname == 0 ? "" : m_functionname;
    }

    unsigned getLineNumber() const
    {
        return m_linenumber;
    }

    bool operator<( const Location& other ) const
    {
        return m_count > other.m_count;
    }
};

typedef std::vector< Location > LocationList;

class FunctionLocation : public Location
{
    friend              class Image;
public:
    typedef std::vector< unsigned > LineLocationList;

private:
    unsigned m_totalCount;
    LineLocationList m_lineLocationList;

public:
    FunctionLocation( const Location& location ) :
    Location( location ),
    m_totalCount( location.getCount() )
    {
        m_lineLocationList.push_back( location.getLineNumber() );
    }

    FunctionLocation& operator+=( const Location& location )
    {
        m_totalCount += location.getCount();

        unsigned lineNumber = location.getLineNumber();

        LineLocationList::iterator findPos = find( m_lineLocationList.begin(),
            m_lineLocationList.end(), lineNumber );

        if ( findPos == m_lineLocationList.end() )
        {
            m_lineLocationList.push_back( location.getLineNumber() );
        }

        return *this;
    }

    bool operator<( const FunctionLocation& other ) const
    {
        return m_totalCount > other.m_totalCount;
    }

    unsigned getCount() const
    {
        return m_totalCount;
    }

    LineLocationList& getLineLocationList()
    {
        return m_lineLocationList;
    }
};

typedef std::map< std::string, FunctionLocation > FunctionLocationMap;
typedef std::vector< FunctionLocation > FunctionList;

class Image
{
    typedef std::map< std::string, Image* > ImageMap;
    typedef std::map< bfd_vma, std::string > LoadableImageMap;

    static bool firstTimeInit;
    static const std::string KERNEL_NAME;
    static const std::string BOOT_DIRECTORY;
    static const std::string TEXT_SECTION_NAME;
    static Image* kernel;
    static ImageMap imageMap;
    static LoadableImageMap kernelLoadableImageMap;

    int m_fd;

    long m_symCount;
    long m_dynCount;

    bfd* m_bfdHandle;

    bfd_vma m_startAddress;
    bfd_size_type m_textSize;
    bfd_vma m_endAddress;

    asection* m_textSection;

    asymbol **m_symTable;
    asymbol **m_dynTable;

    static Image& getKernel();

    Image( const std::string& imageName );
    ~Image();

    void loadSymtab();
    void loadDyntab();
    void dumpSymtab();
    void dumpDyntab();

    bfd_vma getStartAddress()
    {
        return m_startAddress;
    }

    bfd_size_type getSize()
    {
        return m_textSize;
    }

    bfd_vma getEndAddress()
    {
        return m_endAddress;
    }

    bool isContained( Location& location, uintptr_t loadOffset = 0 );
    void mapLocation( Location& location, uintptr_t loadOffset = 0 );
    void functionStart( Location& location );

public:
    static char* demangle( const char* name );
    static std::string getLoadableImageName( const Location& location, uintptr_t& loadOffset );
    static void mapAllLocations( LocationList& locationList );

    bool isOk() const;
    static void mapFunctionStart( FunctionLocation& functionLocation );
};

#endif // #if !defined(IMAGE_H)
