/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#include "cpixtools.h"

#include "cpixexc.h"

#include <string>

#include "cpixfstools.h"

#include "cpixexc.h"
#include "rotlogger.h"

#include "common/cpixlog.h"

namespace Cpix
{

    RotLogger * RotLogger::instance() 
    {
        if (instance_ == NULL)
            {
                instance_ = new RotLogger();
            }

        return instance_;
    }


    void RotLogger::shutdownAll()
    {
        delete instance_;
        instance_ = NULL;
    }


    void RotLogger::createLog(const char * logFileBase,
                              size_t       logSizeLimit,
                              size_t       logSizeCheckRecurrency)
    {
        using namespace std;

        if (logFileBase == NULL)
            {
                return;
            }

        logFileBase_ = logFileBase;
        logSizeLimit_ = logSizeLimit;
        logSizeCheckRecurrency_ = logSizeCheckRecurrency;

        string
            logFile = getLogFile(true); // current

        Cpt_EINTR_RETRY(logFd_,
                        open(logFile.c_str(),
                             O_CREAT | O_APPEND | O_WRONLY,
                             0666));
        if (logFd_ == -1)
            {
                THROW_CPIXEXC("Could not open file '%s'",
                              logFile.c_str());
            }
        Cpt::FileDescSentry
            fds(&logFd_);

        duplicatedStdOut_ = dup(STDOUT_FILENO);
        if (duplicatedStdOut_ == -1)
            {
                THROW_CPIXEXC("Cannot dup() STDOUT_FILENO");
            }
        duplicatedStdErr_ = dup(STDERR_FILENO);
        if (duplicatedStdErr_ == -1)
            {
                THROW_CPIXEXC("Cannot dup() STDERR_FILENO");
            }

        Cpt_EINTR_RETRY_SP( close(STDOUT_FILENO) );
        Cpt_EINTR_RETRY_SP(  close(STDERR_FILENO) );
            
        redirectStdOutErr();

        fds.release();

        isLoggingEnabled_ = true;
    }


    void RotLogger::rotateLog() throw()
    {
        using namespace std;

        Cpt::SyncRegion
            sr(mutex_);

        if (logFd_ == -1)
            {
                return;
            }

        bool
            doNothing = true; 
        ++logCount_;
        if (logCount_ == logSizeCheckRecurrency_)
            {
                logCount_ = 0;
                doNothing = Cpt::filesize(logFd_) <= logSizeLimit_;
            }

        if (doNothing)
            {
                return;
            }

        string
            logFile = getLogFile(false); // false: next
        int
            logFd;
        Cpt_EINTR_RETRY(logFd,
                        open(logFile.c_str(),
                             O_CREAT | O_TRUNC | O_WRONLY,
                             0666));
        if (logFd == -1)
            {
                logMsg(CPIX_LL_ERROR,
                       "Could not open next log: %s. CLOSING LOGS.\n",
                       logFile.c_str());
            }

        bool
            closedAll = closeFds();

        if (logFd != -1 && closedAll)
            {
                logFd_ = logFd;
                redirectStdOutErr();
            }
    }



    bool RotLogger::IsLoggingEnabled()
    {
        return isLoggingEnabled_;
    }
            


    void RotLogger::SetLoggingEnabled(bool b)
    {
        isLoggingEnabled_ = b;
    }



    RotLogger::RotLogger()
        : logSizeLimit_(0),
          logSizeCheckRecurrency_(0),
          logFd_(-1),
          curLogIdx_(-1),
          logCount_(0),
          duplicatedStdOut_(-1),
          duplicatedStdErr_(-1)
    {
        ;
    }



    RotLogger::~RotLogger()
    {
        if (logFd_ == -1)
            {
                return;
            }

        closeFds();
    }



    std::string RotLogger::getLogFile(bool current)
    {
        if (current)
            {
                curLogIdx_ = getMostRecentlyModifiedLogIdx();
            }
        else
            {
                ++curLogIdx_;
                curLogIdx_ %= LOGFILE_NUM;
            }

        std::string
            rv(logFileBase_);
        rv += LOG_SUFFIX;
        rv[logFileBase_.length() + LOGIDX_POS] 
            = static_cast<char>('0' + curLogIdx_);

        return rv;
    }
            
        
    int RotLogger::getMostRecentlyModifiedLogIdx()
    {
        int
            curLogIdx = 0;
        time_t
            mostRecentlyModified = 0;

        Cpt::auto_array<char>
            path(new char[logFileBase_.length()
                          + strlen(LOG_SUFFIX)
                          + 1]);
        strcpy(path.get(),
               logFileBase_.c_str());
        strcpy(path.get() + logFileBase_.length(),
               LOG_SUFFIX);
        int
            pos = logFileBase_.length() + LOGIDX_POS;
        for (int i = 0; i < LOGFILE_NUM; ++i)
            {
                *(path.get() + pos) = static_cast<char>('0' + i);
                time_t
                    modified = Cpt::filemodified(path.get());

                if (modified > mostRecentlyModified)
                    {
                        mostRecentlyModified = modified;
                        curLogIdx = i;
                    }
            }
        return curLogIdx;
    }


    void RotLogger::redirectStdOutErr()
    {
        int
            newStdOut = dup2(logFd_,
                             STDOUT_FILENO);
        if (newStdOut == -1 || newStdOut != STDOUT_FILENO)
            {
                ;
            }
        else
            {
                int
                    newStdErr = dup2(logFd_,
                                     STDERR_FILENO);
                if (newStdErr == -1 || newStdErr != STDERR_FILENO)
                    {
                        Cpt::FileDescSentry
                            fdsDummy(&newStdOut);
                    }
            }
    }


    bool RotLogger::closeFds()
    {
        int
            result;
        bool
            rv = true;

        result = fflush(stdout);
        rv = result != -1;

        result = fflush(stderr);
        rv &= result != -1;

        Cpt_EINTR_RETRY(result,
                        close(logFd_));
        rv &= result != -1;
        logFd_ = -1;

        Cpt_EINTR_RETRY(result,
                        close(STDOUT_FILENO));
        rv &= result != -1;

        Cpt_EINTR_RETRY(result,
                        close(STDERR_FILENO));
        rv &= result != -1;

        return rv;
    }



    RotLogger * RotLogger::instance_         = NULL;
    bool        RotLogger::isLoggingEnabled_ = false;

}

