#include <Profiler.h>
#include <EventFactory.h>
#include <Process.h>
#include <Image.h>
#include <ProcessState.h>
#include <Sample.h>

extern "C" {
#include <paths.h>
#include <libgen.h>
}

void
Profiler::createProfile()
{
    Process::fillProcessMap();
    EventFactory::createEvents( *this );
    printf( "Events processed: %u\n\n", m_sampleCount );
    LocationList locationList;
    Process::collectAllLocations( locationList );
    Image::mapAllLocations( locationList );

    unsigned cumulative = 0;
    for ( LocationList::iterator it = locationList.begin(); it != locationList.end(); ++it )
    {
        const char* execPath = "";
        const char* base = "";
        Location& location( *it );
        char* functionName( Image::demangle( location.getFunctionName() ) );

        if ( location.isKernel() )
        {
            execPath = getbootfile();
            base = basename( execPath );
        }
        else
        {
            Process* process = Process::getProcess( location.getPid() );
            execPath = process == 0 ? "" : ( process -> getName() ).c_str();
            base = basename( execPath );
        }

        cumulative += location.getCount();
        printf( "%6.2f%% %6.2f%% %s, %6u, %10s, %6u, 0x%08lx, %s, %s, %s:%u %s\n",
            (location.getCount() * 100.0) / m_sampleCount,
            (cumulative * 100.0) / m_sampleCount,
            location.isKernel() ? "kern" : "user",
            location.getPid(),
            ( ( base == 0 ) || ( *base == '\0' ) ) ? execPath : basename( execPath ),
            location.getCount(),
            location.getAddress(),
            location.isMapped() ? "mapped  " : "unmapped",
            execPath,
            *( location.getFileName() ) == '\0' ? location.getModuleName() : location.getFileName(),
            location.getLineNumber(),
            functionName);

        free( functionName );
    }

    Process::mapAllFunctions( locationList );
    Process::ActiveProcessList activeProcessList;
    Process::collectActiveProcesses( activeProcessList );

    for ( Process::ActiveProcessList::iterator it = activeProcessList.begin();
        it != activeProcessList.end(); ++it )
    {
        Process& process( **it );
        printf( "\nProcess: %6u, %s, total: %u (%6.2f%%)\n", process.getPid(), process.getName().c_str(),
            process.getSampleCount(), (process.getSampleCount() * 100.0) / m_sampleCount);
        FunctionList functionList;
        process.getFunctionList( functionList );
        unsigned cumulative = 0;
        printf("       time   time-t   samples   env  file / library, line number, function\n");
        for ( FunctionList::iterator it = functionList.begin(); it != functionList.end(); ++it )
        {
            FunctionLocation& functionLocation( *it );
            char* functionName( Image::demangle( functionLocation.getFunctionName() ) );
            cumulative += functionLocation.getCount();
            printf( "    %6.2f%%, %6.2f%%, %8u, %s, %s:%u, %s",
                    (functionLocation.getCount() * 100.0) / process.getSampleCount(),
                    (cumulative * 100.0) / process.getSampleCount(),
                    functionLocation.getCount(),
                    functionLocation.isKernel() ? "kern" : "user",
                    *( functionLocation.getFileName() ) == '\0' ? functionLocation.getModuleName() : functionLocation.getFileName(),
                    functionLocation.getLineNumber(),
                    functionName);
            free( functionName );
            if (m_showlines)
            {
                printf(" lines:");
                FunctionLocation::LineLocationList& lineLocationList( functionLocation.getLineLocationList() );
                for ( FunctionLocation::LineLocationList::iterator it = lineLocationList.begin();
                    it != lineLocationList.end(); ++it )
                {
                    printf( " %u", *it );
                }
            }
            printf( "\n" );
        }
    }    
}

void
Profiler::processEvent( const ProcessExec& processExec )
{
    Process::getProcess( processExec );
}

void
Profiler::processEvent( const Sample& sample )
{
    Process::getProcess( sample ).addSample( sample );
    m_sampleCount++;
}
